ObjFW  Check-in [3826822733]

Overview
Comment:OFDNSResolver: Add support for parsing CNAMEs
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 3826822733823abd5a8f785baa4cb59d7202db59567f670fb601a04580461e2e
User & Date: js on 2018-07-29 18:38:58
Other Links: manifest | tags
Context
2018-07-29
19:13
OFDNSResolver: Allow specifying query class & type check-in: af30016cfb user: js tags: trunk
18:38
OFDNSResolver: Add support for parsing CNAMEs check-in: 3826822733 user: js tags: trunk
16:16
macros.h: Fix a typo check-in: 9383a4cf4a user: js tags: trunk
Changes

Modified src/OFDNSResolver.m from [f68c605682] to [f87770019a].

180
181
182
183
184
185
186












































187
188
189
190
191
192
193
		[components addObject: component];
	} while (componentLength > 0);

	*idx = i;

	return [components componentsJoinedByString: @"."];
}













































@implementation OFDNSResolver_context
@synthesize host = _host, nameServers = _nameServers;
@synthesize searchDomains = _searchDomains;
@synthesize nameServersIndex = _nameServersIndex;
@synthesize searchDomainsIndex = _searchDomainsIndex, queryData = _queryData;
@synthesize target = _target, selector = _selector, userContext = _userContext;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
		[components addObject: component];
	} while (componentLength > 0);

	*idx = i;

	return [components componentsJoinedByString: @"."];
}

static id
parseData(const unsigned char *buffer, size_t length, size_t i,
    size_t dataLength, of_dns_resource_record_class_t recordClass,
    of_dns_resource_record_type_t recordType)
{
	id data;

	if (recordClass == OF_DNS_RESOURCE_RECORD_CLASS_IN) {
		size_t j;

		switch (recordType) {
		case OF_DNS_RESOURCE_RECORD_TYPE_A:
			if (dataLength != 4)
				@throw [OFInvalidServerReplyException
				    exception];

			data = [OFString stringWithFormat:
			    @"%u.%u.%u.%u",
			    buffer[i], buffer[i + 1],
			    buffer[i + 2], buffer[i + 3]];
			break;
		case OF_DNS_RESOURCE_RECORD_TYPE_CNAME:
			j = i;

			data = parseName(buffer, length, &j,
			    ALLOWED_POINTER_LEVELS);

			if (j != i + dataLength)
				@throw [OFInvalidServerReplyException
				    exception];

			break;
		default:
			data = [OFData dataWithItems: &buffer[i]
					       count: dataLength];
			break;
		}
	} else
		data = [OFData dataWithItems: &buffer[i]
				       count: dataLength];

	return data;
}

@implementation OFDNSResolver_context
@synthesize host = _host, nameServers = _nameServers;
@synthesize searchDomains = _searchDomains;
@synthesize nameServersIndex = _nameServersIndex;
@synthesize searchDomainsIndex = _searchDomainsIndex, queryData = _queryData;
@synthesize target = _target, selector = _selector, userContext = _userContext;
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639

640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660

661
662
663
664
665
666

667
668
669
670
671
672
673
		}

		numQuestions = (buffer[4] << 8) | buffer[5];

		numAnswers = (buffer[6] << 8) | buffer[7];
		answers = [OFMutableArray arrayWithCapacity: numAnswers];

		/* "Consume" headers */
		i = 12;

		/*
		 * Skip over the questions - we use the ID to identify the
		 * query.
		 *
		 * TODO: Compare to our query, just in case?
		 */
		for (uint_fast16_t j = 0; j < numQuestions; j++) {
			parseName(buffer, length, &i, ALLOWED_POINTER_LEVELS);
			i += 4;
		}

		for (uint_fast16_t j = 0; j < numAnswers; j++) {
			OFString *name = parseName(buffer, length, &i,
			    ALLOWED_POINTER_LEVELS);
			uint16_t type, dataClass;

			uint32_t TTL;
			uint16_t dataLength;
			OFData *data;
			OFDNSResourceRecord *record;

			if (i + 10 > length)
				@throw [OFTruncatedDataException exception];

			type = (buffer[i] << 16) | buffer[i + 1];
			dataClass = (buffer[i + 2] << 16) | buffer[i + 3];
			TTL = (buffer[i + 4] << 24) | (buffer[i + 5] << 16) |
			    (buffer[i + 6] << 8) | buffer[i + 7];
			dataLength = (buffer[i + 8] << 16) | buffer[i + 9];

			i += 10;

			if (i + dataLength > length)
				@throw [OFTruncatedDataException exception];

			data = [OFData dataWithItems: &buffer[i]
					       count: dataLength];

			i += dataLength;

			record = [[[OFDNSResourceRecord alloc]
			    initWithName: name
				    type: type
			       dataClass: dataClass

				    data: data
				     TTL: TTL] autorelease];

			[answers addObject: record];
		}
	} @catch (id e) {
		callback(target, selector, nil,







<
















|
>


|





|
|









<
|
>




<
|
>







659
660
661
662
663
664
665

666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702

703
704
705
706
707
708

709
710
711
712
713
714
715
716
717
		}

		numQuestions = (buffer[4] << 8) | buffer[5];

		numAnswers = (buffer[6] << 8) | buffer[7];
		answers = [OFMutableArray arrayWithCapacity: numAnswers];


		i = 12;

		/*
		 * Skip over the questions - we use the ID to identify the
		 * query.
		 *
		 * TODO: Compare to our query, just in case?
		 */
		for (uint_fast16_t j = 0; j < numQuestions; j++) {
			parseName(buffer, length, &i, ALLOWED_POINTER_LEVELS);
			i += 4;
		}

		for (uint_fast16_t j = 0; j < numAnswers; j++) {
			OFString *name = parseName(buffer, length, &i,
			    ALLOWED_POINTER_LEVELS);
			of_dns_resource_record_class_t recordClass;
			of_dns_resource_record_type_t recordType;
			uint32_t TTL;
			uint16_t dataLength;
			id data;
			OFDNSResourceRecord *record;

			if (i + 10 > length)
				@throw [OFTruncatedDataException exception];

			recordType = (buffer[i] << 16) | buffer[i + 1];
			recordClass = (buffer[i + 2] << 16) | buffer[i + 3];
			TTL = (buffer[i + 4] << 24) | (buffer[i + 5] << 16) |
			    (buffer[i + 6] << 8) | buffer[i + 7];
			dataLength = (buffer[i + 8] << 16) | buffer[i + 9];

			i += 10;

			if (i + dataLength > length)
				@throw [OFTruncatedDataException exception];


			data = parseData(buffer, length, i, dataLength,
			    recordClass, recordType);
			i += dataLength;

			record = [[[OFDNSResourceRecord alloc]
			    initWithName: name

			     recordClass: recordClass
			      recordType: recordType
				    data: data
				     TTL: TTL] autorelease];

			[answers addObject: record];
		}
	} @catch (id e) {
		callback(target, selector, nil,
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
		length8 = (uint8_t)length;
		[data addItem: &length8];
		[data addItems: [component UTF8String]
			 count: length];
	}

	/* QTYPE */
	tmp = OF_BSWAP16_IF_LE(1); /* A */
	[data addItems: &tmp
		 count: 2];

	/* QCLASS */
	tmp = OF_BSWAP16_IF_LE(1); /* IN */
	[data addItems: &tmp
		 count: 2];

	DNSResolverContext = [[[OFDNSResolver_context alloc]
	    initWithHost: host
	     nameServers: _nameServers
	   searchDomains: _searchDomains







|




|







802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
		length8 = (uint8_t)length;
		[data addItem: &length8];
		[data addItems: [component UTF8String]
			 count: length];
	}

	/* QTYPE */
	tmp = OF_BSWAP16_IF_LE(OF_DNS_RESOURCE_RECORD_TYPE_A);
	[data addItems: &tmp
		 count: 2];

	/* QCLASS */
	tmp = OF_BSWAP16_IF_LE(OF_DNS_RESOURCE_RECORD_CLASS_IN);
	[data addItems: &tmp
		 count: 2];

	DNSResolverContext = [[[OFDNSResolver_context alloc]
	    initWithHost: host
	     nameServers: _nameServers
	   searchDomains: _searchDomains

Modified src/OFDNSResourceRecord.h from [f3a43148a0] to [1e61fb86ab].

15
16
17
18
19
20
21


22
23
24





















25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55




56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

74
75
76
77











78
 * file.
 */

#import "OFObject.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN



@class OFData;






















/*!
 * @class OFDNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class represenging a DNS resource record.
 */
@interface OFDNSResourceRecord: OFObject
{
	OFString *_name;
	uint16_t _type;
	uint16_t _dataClass;
	OFData *_data;
	uint32_t _TTL;
}

/**
 * @brief The domain name to which the resource record belongs.
 */
@property (readonly, nonatomic) OFString *name;

/*!
 * @brief The resource record type code.
 */
@property (readonly, nonatomic) uint16_t type;

/*!
 * @brief The class of the data.
 */
@property (readonly, nonatomic) uint16_t dataClass;

/*!
 * The data of the resource.




 */
@property (readonly, nonatomic) OFData *data;

/*!
 * @brief The number of seconds after which the resource record should be
 *	  discarded from the cache.
 */
@property (readonly, nonatomic) uint32_t TTL;

/*!
 * @brief If the resource record is an A or AAAA record, this contains the data
 *	  interpreted as an IP address.
 */
@property (readonly, nonatomic) OFString *IPAddress;

- (instancetype)initWithName: (OFString *)name
			type: (uint16_t)type
		   dataClass: (uint16_t)dataClass

			data: (OFData *)data
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end












OF_ASSUME_NONNULL_END







>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








|
|
|









|

|


|

|


|
>
>
>
>

|







<
<
<
<
<
<

<
|
>
|



>
>
>
>
>
>
>
>
>
>
>

15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91






92

93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 * file.
 */

#import "OFObject.h"
#import "OFString.h"

OF_ASSUME_NONNULL_BEGIN

/*! @file */

@class OFData;

/*!
 * @brief The class of a DNS resource record.
 */
typedef enum {
	OF_DNS_RESOURCE_RECORD_CLASS_IN = 1
} of_dns_resource_record_class_t;

/*!
 * @brief The type of a DNS resource record.
 */
typedef enum {
	OF_DNS_RESOURCE_RECORD_TYPE_A	  = 1,
	OF_DNS_RESOURCE_RECORD_TYPE_NS	  = 2,
	OF_DNS_RESOURCE_RECORD_TYPE_CNAME = 5,
	OF_DNS_RESOURCE_RECORD_TYPE_SOA	  = 6,
	OF_DNS_RESOURCE_RECORD_TYPE_PTR	  = 12,
	OF_DNS_RESOURCE_RECORD_TYPE_MX	  = 15,
	OF_DNS_RESOURCE_RECORD_TYPE_TXT	  = 16,
	OF_DNS_RESOURCE_RECORD_TYPE_AAAA  = 28
} of_dns_resource_record_type_t;

/*!
 * @class OFDNSResourceRecord OFDNSResourceRecord.h ObjFW/OFDNSResourceRecord.h
 *
 * @brief A class represenging a DNS resource record.
 */
@interface OFDNSResourceRecord: OFObject
{
	OFString *_name;
	of_dns_resource_record_class_t _recordClass;
	of_dns_resource_record_type_t _recordType;
	id _data;
	uint32_t _TTL;
}

/**
 * @brief The domain name to which the resource record belongs.
 */
@property (readonly, nonatomic) OFString *name;

/*!
 * @brief The class of the data.
 */
@property (readonly, nonatomic) of_dns_resource_record_class_t recordClass;

/*!
 * @brief The resource record type code.
 */
@property (readonly, nonatomic) of_dns_resource_record_type_t recordType;

/*!
 * The class and type-dependent data of the resource.
 *
 * For A and AAAA records, this is a string with the IP address.
 * For CNAME records, this is a string with the alias.
 * For anything else, this is OFData.
 */
@property (readonly, nonatomic) id data;

/*!
 * @brief The number of seconds after which the resource record should be
 *	  discarded from the cache.
 */
@property (readonly, nonatomic) uint32_t TTL;







- (instancetype)initWithName: (OFString *)name

		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			data: (id)data
			 TTL: (uint32_t)TTL OF_DESIGNATED_INITIALIZER;
@end

#ifdef __cplusplus
extern "C" {
#endif
extern OFString *_Nonnull of_dns_resource_record_class_to_string(
    of_dns_resource_record_class_t recordClass);
extern OFString *_Nonnull of_dns_resource_record_type_to_string(
    of_dns_resource_record_type_t recordType);
#ifdef __cplusplus
}
#endif

OF_ASSUME_NONNULL_END

Modified src/OFDNSResourceRecord.m from [ceee6d54ef] to [1fb8ff8a93].

17
18
19
20
21
22
23
24





































25
26
27
28
29
30
31

32
33
34
35
36
37
38
39
40

41
42
43
44
45
46
47

#include "config.h"

#import "OFDNSResourceRecord.h"
#import "OFData.h"

#import "OFInvalidFormatException.h"






































@implementation OFDNSResourceRecord
@synthesize name = _name, type = _type, dataClass = _dataClass, data = _data;
@synthesize TTL = _TTL;

- (instancetype)initWithName: (OFString *)name
			type: (uint16_t)type
		   dataClass: (uint16_t)dataClass

			data: (OFData *)data
			 TTL: (uint32_t)TTL
{
	self = [super init];

	@try {
		_name = [name copy];
		_type = type;
		_dataClass = dataClass;

		_data = [data copy];
		_TTL = TTL;
	} @catch (id e) {
		[self release];
		@throw e;
	}









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
|


<
|
>
|






<
|
>







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84

#include "config.h"

#import "OFDNSResourceRecord.h"
#import "OFData.h"

#import "OFInvalidFormatException.h"

OFString *
of_dns_resource_record_class_to_string(
    of_dns_resource_record_class_t recordClass)
{
	switch (recordClass) {
	case OF_DNS_RESOURCE_RECORD_CLASS_IN:
		return @"IN";
	default:
		return [OFString stringWithFormat: @"%u", recordClass];
	}
}

OFString *
of_dns_resource_record_type_to_string(of_dns_resource_record_type_t recordType)
{
	switch (recordType) {
	case OF_DNS_RESOURCE_RECORD_TYPE_A:
		return @"A";
	case OF_DNS_RESOURCE_RECORD_TYPE_NS:
		return @"NS";
	case OF_DNS_RESOURCE_RECORD_TYPE_CNAME:
		return @"CNAME";
	case OF_DNS_RESOURCE_RECORD_TYPE_SOA:
		return @"SOA";
	case OF_DNS_RESOURCE_RECORD_TYPE_PTR:
		return @"PTR";
	case OF_DNS_RESOURCE_RECORD_TYPE_MX:
		return @"MX";
	case OF_DNS_RESOURCE_RECORD_TYPE_TXT:
		return @"TXT";
	case OF_DNS_RESOURCE_RECORD_TYPE_AAAA:
		return @"AAAA";
	default:
		return [OFString stringWithFormat: @"%u", recordType];
	}
}

@implementation OFDNSResourceRecord
@synthesize name = _name, recordClass = _recordClass, recordType = _recordType;
@synthesize data = _data, TTL = _TTL;

- (instancetype)initWithName: (OFString *)name

		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			data: (id)data
			 TTL: (uint32_t)TTL
{
	self = [super init];

	@try {
		_name = [name copy];

		_recordClass = recordClass;
		_recordType = recordType;
		_data = [data copy];
		_TTL = TTL;
	} @catch (id e) {
		[self release];
		@throw e;
	}

64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
		return false;

	otherRecord = otherObject;

	if (otherRecord->_name != _name && ![otherRecord->_name isEqual: _name])
		return false;

	if (otherRecord->_type != _type)
		return false;

	if (otherRecord->_dataClass != _dataClass)
		return false;

	if (otherRecord->_data != _data && ![otherRecord->_data isEqual: _data])
		return false;

	if (otherRecord->_TTL != _TTL)
		return false;

	return true;
}

- (uint32_t)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, [_name hash]);
	OF_HASH_ADD(hash, _type >> 8);
	OF_HASH_ADD(hash, _type);
	OF_HASH_ADD(hash, _dataClass >> 8);
	OF_HASH_ADD(hash, _dataClass);
	OF_HASH_ADD_HASH(hash, [_data hash]);
	OF_HASH_ADD(hash, _TTL >> 24);
	OF_HASH_ADD(hash, _TTL >> 16);
	OF_HASH_ADD(hash, _TTL >> 8);
	OF_HASH_ADD(hash, _TTL);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{
	id data = _data;

	if (_dataClass == 1 && _type == 1)
		data = [self IPAddress];

	return [OFString stringWithFormat:
	    @"<OFDNSResourceRecord:\n"
	    @"\tName = %@,\n"

	    @"\tType = %" PRIu16 "\n"
	    @"\tData Class = %" PRIu16 "\n"
	    @"\tData = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",
	    _name, _type, _dataClass, data, _TTL];
}

- (OFString *)IPAddress
{
	const unsigned char *dataItems;

	if (_dataClass != 1)
		@throw [OFInvalidFormatException exception];

	if ([_data itemSize] != 1)
		@throw [OFInvalidFormatException exception];

	dataItems = [_data items];

	switch (_type) {
	case 1:
		if ([_data count] != 4)
			@throw [OFInvalidFormatException exception];

		return [OFString stringWithFormat: @"%u.%u.%u.%u",
		    dataItems[0], dataItems[1], dataItems[2], dataItems[3]];
	case 28:
		/* TODO: Implement */
	default:
		@throw [OFInvalidFormatException exception];
	}
}
@end







|


|


















|
|
|
|













<
<
<
<
<



>
|
<



<
<
|
<
<
<
|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146





147
148
149
150
151

152
153
154


155



156


157


















158
		return false;

	otherRecord = otherObject;

	if (otherRecord->_name != _name && ![otherRecord->_name isEqual: _name])
		return false;

	if (otherRecord->_recordClass != _recordClass)
		return false;

	if (otherRecord->_recordType != _recordType)
		return false;

	if (otherRecord->_data != _data && ![otherRecord->_data isEqual: _data])
		return false;

	if (otherRecord->_TTL != _TTL)
		return false;

	return true;
}

- (uint32_t)hash
{
	uint32_t hash;

	OF_HASH_INIT(hash);

	OF_HASH_ADD_HASH(hash, [_name hash]);
	OF_HASH_ADD(hash, _recordClass >> 8);
	OF_HASH_ADD(hash, _recordClass);
	OF_HASH_ADD(hash, _recordType >> 8);
	OF_HASH_ADD(hash, _recordType);
	OF_HASH_ADD_HASH(hash, [_data hash]);
	OF_HASH_ADD(hash, _TTL >> 24);
	OF_HASH_ADD(hash, _TTL >> 16);
	OF_HASH_ADD(hash, _TTL >> 8);
	OF_HASH_ADD(hash, _TTL);

	OF_HASH_FINALIZE(hash);

	return hash;
}

- (OFString *)description
{





	return [OFString stringWithFormat:
	    @"<OFDNSResourceRecord:\n"
	    @"\tName = %@,\n"
	    @"\tClass = %@\n"
	    @"\tType = %@\n"

	    @"\tData = %@\n"
	    @"\tTTL = %" PRIu32 "\n"
	    @">",


	    _name, of_dns_resource_record_class_to_string(_recordClass),



	    of_dns_resource_record_type_to_string(_recordType), _data, _TTL];


}


















@end