ObjFW  Check-in [452616df69]

Overview
Comment:OFDNSResolver: Let queries time out
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 452616df69b37e5fe498c032bcebb51afce6f032eff754bfb3b3464505abce80
User & Date: js on 2018-08-05 15:24:43
Other Links: manifest | tags
Context
2018-08-05
19:27
OFRunLoop: More robust handling of cancels check-in: 1bf3b2ac7c user: js tags: trunk
15:24
OFDNSResolver: Let queries time out check-in: 452616df69 user: js tags: trunk
2018-08-04
23:54
Fix compilation with GCC check-in: 7b0f48419d user: js tags: trunk
Changes

Modified src/OFDNSResolver.m from [ef5cd89abc] to [7ee76905cc].

25
26
27
28
29
30
31

32
33
34
35
36
37
38
#import "OFCharacterSet.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFFile.h"
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFString.h"

#import "OFUDPSocket.h"
#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif

#import "OFInvalidArgumentException.h"
#import "OFInvalidServerReplyException.h"







>







25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#import "OFCharacterSet.h"
#import "OFData.h"
#import "OFDictionary.h"
#import "OFFile.h"
#import "OFLocale.h"
#import "OFNumber.h"
#import "OFString.h"
#import "OFTimer.h"
#import "OFUDPSocket.h"
#ifdef OF_WINDOWS
# import "OFWindowsRegistryKey.h"
#endif

#import "OFInvalidArgumentException.h"
#import "OFInvalidServerReplyException.h"
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
 * RFC 1035 doesn't specify if pointers to pointers are allowed, and if so how
 * many. Since it's unspecified, we have to assume that it might happen, but we
 * also want to limit it to avoid DoS. Limiting it to 16 levels of pointers and
 * immediately rejecting pointers to itself seems like a fair balance.
 */
#define MAX_ALLOWED_POINTERS 16



/*
 * TODO:
 *
 *  - Timeouts
 *  - Resolve with each search domain
 *  - Iterate through name servers
 *  - Fallback to TCP
 */

@interface OFDNSResolver_context: OFObject
{
	OFString *_host;
	of_dns_resource_record_class_t _recordClass;
	of_dns_resource_record_type_t _recordType;
	OFNumber *_ID;
	OFArray OF_GENERIC(OFString *) *_nameServers, *_searchDomains;
	size_t _nameServersIndex, _searchDomainsIndex;
	OFMutableData *_queryData;
	id _target;
	SEL _selector;
	id _userContext;

}

@property (readonly, nonatomic) OFString *host;
@property (readonly, nonatomic) of_dns_resource_record_class_t recordClass;
@property (readonly, nonatomic) of_dns_resource_record_type_t recordType;
@property (readonly, nonatomic) OFNumber *ID;
@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *nameServers;
@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains;
@property (nonatomic) size_t nameServersIndex;
@property (nonatomic) size_t searchDomainsIndex;
@property (readonly, nonatomic) OFMutableData *queryData;
@property (readonly, nonatomic) id target;
@property (readonly, nonatomic) SEL selector;
@property (readonly, nonatomic) id userContext;


- (instancetype)initWithHost: (OFString *)host
		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			  ID: (OFNumber *)ID
		 nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers
	       searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains
		   queryData: (OFMutableData *)queryData
		      target: (id)target
		    selector: (SEL)selector
		 userContext: (id)userContext;

@end

@interface OFDNSResolver ()
#ifdef OF_HAVE_FILES
- (void)of_parseHosts: (OFString *)path;
# ifndef OF_WINDOWS
- (void)of_parseResolvConf: (OFString *)path;







>
>



<

















>














>










|
>







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
111
112
113
114
115
 * RFC 1035 doesn't specify if pointers to pointers are allowed, and if so how
 * many. Since it's unspecified, we have to assume that it might happen, but we
 * also want to limit it to avoid DoS. Limiting it to 16 levels of pointers and
 * immediately rejecting pointers to itself seems like a fair balance.
 */
#define MAX_ALLOWED_POINTERS 16

#define TIMEOUT 2

/*
 * TODO:
 *

 *  - Resolve with each search domain
 *  - Iterate through name servers
 *  - Fallback to TCP
 */

@interface OFDNSResolver_context: OFObject
{
	OFString *_host;
	of_dns_resource_record_class_t _recordClass;
	of_dns_resource_record_type_t _recordType;
	OFNumber *_ID;
	OFArray OF_GENERIC(OFString *) *_nameServers, *_searchDomains;
	size_t _nameServersIndex, _searchDomainsIndex;
	OFMutableData *_queryData;
	id _target;
	SEL _selector;
	id _userContext;
	OFTimer *_timer;
}

@property (readonly, nonatomic) OFString *host;
@property (readonly, nonatomic) of_dns_resource_record_class_t recordClass;
@property (readonly, nonatomic) of_dns_resource_record_type_t recordType;
@property (readonly, nonatomic) OFNumber *ID;
@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *nameServers;
@property (readonly, nonatomic) OFArray OF_GENERIC(OFString *) *searchDomains;
@property (nonatomic) size_t nameServersIndex;
@property (nonatomic) size_t searchDomainsIndex;
@property (readonly, nonatomic) OFMutableData *queryData;
@property (readonly, nonatomic) id target;
@property (readonly, nonatomic) SEL selector;
@property (readonly, nonatomic) id userContext;
@property (readonly, nonatomic) OFTimer *timer;

- (instancetype)initWithHost: (OFString *)host
		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			  ID: (OFNumber *)ID
		 nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers
	       searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains
		   queryData: (OFMutableData *)queryData
		      target: (id)target
		    selector: (SEL)selector
		 userContext: (id)userContext
		       timer: (OFTimer *)timer;
@end

@interface OFDNSResolver ()
#ifdef OF_HAVE_FILES
- (void)of_parseHosts: (OFString *)path;
# ifndef OF_WINDOWS
- (void)of_parseResolvConf: (OFString *)path;
499
500
501
502
503
504
505

506
507
508
509
510
511
512
513
514
515
516

517
518
519
520
521
522
523
524
525
526
527
528
529
530

531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547

548
549
550
551
552
553
554
@implementation OFDNSResolver_context
@synthesize host = _host, recordClass = _recordClass, recordType = _recordType;
@synthesize ID = _ID, nameServers = _nameServers;
@synthesize searchDomains = _searchDomains;
@synthesize nameServersIndex = _nameServersIndex;
@synthesize searchDomainsIndex = _searchDomainsIndex, queryData = _queryData;
@synthesize target = _target, selector = _selector, userContext = _userContext;


- (instancetype)initWithHost: (OFString *)host
		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			  ID: (OFNumber *)ID
		 nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers
	       searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains
		   queryData: (OFMutableData *)queryData
		      target: (id)target
		    selector: (SEL)selector
		 userContext: (id)userContext

{
	self = [super init];

	@try {
		_host = [host copy];
		_recordClass = recordClass;
		_recordType = recordType;
		_ID = [ID retain];
		_nameServers = [nameServers copy];
		_searchDomains = [searchDomains copy];
		_queryData = [queryData retain];
		_target = [target retain];
		_selector = selector;
		_userContext = [userContext retain];

	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_host release];
	[_ID release];
	[_nameServers release];
	[_searchDomains release];
	[_queryData release];
	[_target release];
	[_userContext release];


	[super dealloc];
}
@end

@implementation OFDNSResolver
@synthesize staticHosts = _staticHosts, nameServers = _nameServers;







>











>














>

















>







504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
@implementation OFDNSResolver_context
@synthesize host = _host, recordClass = _recordClass, recordType = _recordType;
@synthesize ID = _ID, nameServers = _nameServers;
@synthesize searchDomains = _searchDomains;
@synthesize nameServersIndex = _nameServersIndex;
@synthesize searchDomainsIndex = _searchDomainsIndex, queryData = _queryData;
@synthesize target = _target, selector = _selector, userContext = _userContext;
@synthesize timer = _timer;

- (instancetype)initWithHost: (OFString *)host
		 recordClass: (of_dns_resource_record_class_t)recordClass
		  recordType: (of_dns_resource_record_type_t)recordType
			  ID: (OFNumber *)ID
		 nameServers: (OFArray OF_GENERIC(OFString *) *)nameServers
	       searchDomains: (OFArray OF_GENERIC(OFString *) *)searchDomains
		   queryData: (OFMutableData *)queryData
		      target: (id)target
		    selector: (SEL)selector
		 userContext: (id)userContext
		       timer: (OFTimer *)timer
{
	self = [super init];

	@try {
		_host = [host copy];
		_recordClass = recordClass;
		_recordType = recordType;
		_ID = [ID retain];
		_nameServers = [nameServers copy];
		_searchDomains = [searchDomains copy];
		_queryData = [queryData retain];
		_target = [target retain];
		_selector = selector;
		_userContext = [userContext retain];
		_timer = [timer retain];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_host release];
	[_ID release];
	[_nameServers release];
	[_searchDomains release];
	[_queryData release];
	[_target release];
	[_userContext release];
	[_timer release];

	[super dealloc];
}
@end

@implementation OFDNSResolver
@synthesize staticHosts = _staticHosts, nameServers = _nameServers;
902
903
904
905
906
907
908

909
910
911
912
913
914
915

	ID = [OFNumber numberWithUInt16: (buffer[0] << 8) | buffer[1]];
	DNSResolverContext = [[[_queries objectForKey: ID] retain] autorelease];

	if (DNSResolverContext == nil)
		return false;


	[_queries removeObjectForKey: ID];

	target = [DNSResolverContext target];
	selector = [DNSResolverContext selector];
	callback = (void (*)(id, SEL, OFArray *, id, id))
	    [target methodForSelector: selector];
	queryData = [DNSResolverContext queryData];







>







911
912
913
914
915
916
917
918
919
920
921
922
923
924
925

	ID = [OFNumber numberWithUInt16: (buffer[0] << 8) | buffer[1]];
	DNSResolverContext = [[[_queries objectForKey: ID] retain] autorelease];

	if (DNSResolverContext == nil)
		return false;

	[[DNSResolverContext timer] invalidate];
	[_queries removeObjectForKey: ID];

	target = [DNSResolverContext target];
	selector = [DNSResolverContext selector];
	callback = (void (*)(id, SEL, OFArray *, id, id))
	    [target methodForSelector: selector];
	queryData = [DNSResolverContext queryData];
1025
1026
1027
1028
1029
1030
1031




























1032
1033
1034
1035
1036
1037
1038
	}

	callback(target, selector, answers, [DNSResolverContext userContext],
	    nil);

	return false;
}





























- (size_t)of_socket: (OFUDPSocket *)sock
      didSendBuffer: (void **)buffer
	  bytesSent: (size_t)bytesSent
	   receiver: (of_socket_address_t *)receiver
	    context: (id)DNSResolverContext
	  exception: (id)exception







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







1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
	}

	callback(target, selector, answers, [DNSResolverContext userContext],
	    nil);

	return false;
}

- (void)of_queryWithIDTimedOut: (OFNumber *)ID
{
	OFDNSResolver_context *DNSResolverContext = [_queries objectForKey: ID];
	id target;
	SEL selector;
	void (*callback)(id, SEL, OFArray *, id, id);
	OFResolveHostFailedException *exception;

	if (DNSResolverContext == nil)
		return;

	target = [[[DNSResolverContext target] retain] autorelease];
	selector = [DNSResolverContext selector];
	callback = (void (*)(id, SEL, OFArray *, id, id))
	    [target methodForSelector: selector];

	exception = [OFResolveHostFailedException
	    exceptionWithHost: [DNSResolverContext host]
		  recordClass: [DNSResolverContext recordClass]
		   recordType: [DNSResolverContext recordType]
			error: OF_DNS_RESOLVER_ERROR_TIMEOUT];

	[_queries removeObjectForKey: [DNSResolverContext ID]];

	callback(target, selector, nil,
	    [DNSResolverContext userContext], exception);
}

- (size_t)of_socket: (OFUDPSocket *)sock
      didSendBuffer: (void **)buffer
	  bytesSent: (size_t)bytesSent
	   receiver: (of_socket_address_t *)receiver
	    context: (id)DNSResolverContext
	  exception: (id)exception
1080
1081
1082
1083
1084
1085
1086

1087
1088
1089
1090
1091
1092
1093
	      recordType: (of_dns_resource_record_type_t)recordType
		  target: (id)target
		selector: (SEL)selector
		 context: (id)context
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [OFMutableData dataWithCapacity: 512];

	OFDNSResolver_context *DNSResolverContext;
	OFNumber *ID;
	uint16_t tmp;
	OFUDPSocket *sock;
	of_socket_address_t address;

	/* TODO: Properly try all search domains */







>







1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
	      recordType: (of_dns_resource_record_type_t)recordType
		  target: (id)target
		selector: (SEL)selector
		 context: (id)context
{
	void *pool = objc_autoreleasePoolPush();
	OFMutableData *data = [OFMutableData dataWithCapacity: 512];
	OFTimer *timer;
	OFDNSResolver_context *DNSResolverContext;
	OFNumber *ID;
	uint16_t tmp;
	OFUDPSocket *sock;
	of_socket_address_t address;

	/* TODO: Properly try all search domains */
1143
1144
1145
1146
1147
1148
1149







1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160

1161
1162
1163
1164
1165
1166
1167
		 count: 2];

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








	DNSResolverContext = [[[OFDNSResolver_context alloc]
	    initWithHost: host
	     recordClass: recordClass
	      recordType: recordType
		      ID: ID
	     nameServers: _nameServers
	   searchDomains: _searchDomains
	       queryData: data
		  target: target
		selector: selector
	     userContext: context] autorelease];

	[_queries setObject: DNSResolverContext
		     forKey: ID];

	address = of_socket_address_parse_ip(
	    [[DNSResolverContext nameServers] firstObject], 53);

#ifdef OF_HAVE_IPV6







>
>
>
>
>
>
>










|
>







1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
		 count: 2];

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

	timer = [OFTimer
	    scheduledTimerWithTimeInterval: TIMEOUT
				    target: self
				  selector: @selector(of_queryWithIDTimedOut:)
				    object: ID
				   repeats: false];

	DNSResolverContext = [[[OFDNSResolver_context alloc]
	    initWithHost: host
	     recordClass: recordClass
	      recordType: recordType
		      ID: ID
	     nameServers: _nameServers
	   searchDomains: _searchDomains
	       queryData: data
		  target: target
		selector: selector
	     userContext: context
		   timer: timer] autorelease];
	[_queries setObject: DNSResolverContext
		     forKey: ID];

	address = of_socket_address_parse_ip(
	    [[DNSResolverContext nameServers] firstObject], 53);

#ifdef OF_HAVE_IPV6