ObjFW  Check-in [8555d64ee6]

Overview
Comment:OFDNSResolver: Method to resolve host to addresses

This provides more convenience when the goal is to connect a socket and
provides functionality similar to getaddrinfo().

No support for resolving CNAMEs yet - this will be the next step.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8555d64ee6418e42a753ba9286f14f7ac158ce3587306351874c122746e35eb8
User & Date: js on 2018-09-10 20:44:40
Other Links: manifest | tags
Context
2018-09-15
13:45
OFDNSResolver: Group records by domain name check-in: 7ba597c52d user: js tags: trunk
2018-09-10
20:44
OFDNSResolver: Method to resolve host to addresses check-in: 8555d64ee6 user: js tags: trunk
2018-09-09
14:32
PLATFORMS.md: Update macOS, add Darling check-in: 02e92f88aa user: js tags: trunk
Changes

Modified src/OFDNSResolver.h from [818844ff98] to [6f6cb9c138].

231
232
233
234
235
236
237
238





























































239
240
241
242
243
244
- (void)asyncResolveHost: (OFString *)host
	     recordClass: (of_dns_resource_record_class_t)recordClass
	      recordType: (of_dns_resource_record_type_t)recordType
		  target: (id)target
		selector: (SEL)selector
		 context: (nullable id)context;

/*!





























































 * @brief Closes all sockets and cancels all ongoing requests.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END








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






231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
- (void)asyncResolveHost: (OFString *)host
	     recordClass: (of_dns_resource_record_class_t)recordClass
	      recordType: (of_dns_resource_record_type_t)recordType
		  target: (id)target
		selector: (SEL)selector
		 context: (nullable id)context;

/*!
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param target The target to call with the result once resolving is done
 * @param selector The selector to call on the target. The signature must be
 *		   the following:
 *		   @parblock
 *
 *		       void (OFDNSResolver *resolver, OFString *domainName,
 *		           OFData *_Nullable, socketAddresses,
 *		           id _Nullable context, id _Nullable exception)
 *
 *		   `resolver` is the acting resolver.@n
 *		   `domainName` is the fully qualified domain name used to
 *		   resolve the host.@n
 *		   `socketAddresses` is OFData containing several
 *		   of_socket_address_t.@n
 *		   `context` is the context object originally passed.@n
 *		   `exception` is an exception that happened during resolving,
 *		   otherwise nil.
 *		   @endparblock
 * @param context A context object to pass along to the target
 */
- (void)asyncResolveSocketAddressesForHost: (OFString *)host
				    target: (id)target
				  selector: (SEL)selector
				   context: (nullable id)context;

/*!
 * @brief Asynchronously resolves the specified host to socket addresses.
 *
 * @param host The host to resolve
 * @param addressFamily The desired socket address family
 * @param target The target to call with the result once resolving is done
 * @param selector The selector to call on the target. The signature must be
 *		   the following:
 *		   @parblock
 *
 *		       void (OFDNSResolver *resolver, OFString *domainName,
 *		           OFData *_Nullable socketAddresses,
 *		           id _Nullable context, id _Nullable exception)
 *
 *		   `resolver` is the acting resolver.@n
 *		   `domainName` is the fully qualified domain name used to
 *		   resolve the host.@n
 *		   `socketAddresses` is OFData containing several
 *		   of_socket_address_t.@n
 *		   `context` is the context object originally passed.@n
 *		   `exception` is an exception that happened during resolving,
 *		   otherwise nil.
 *		   @endparblock
 * @param context A context object to pass along to the target
 */
- (void)asyncResolveSocketAddressesForHost: (OFString *)host
			     addressFamily: (of_socket_address_family_t)
						addressFamily
				    target: (id)target
				  selector: (SEL)selector
				   context: (nullable id)context;

/*!
 * @brief Closes all sockets and cancels all ongoing requests.
 */
- (void)close;
@end

OF_ASSUME_NONNULL_END

Modified src/OFDNSResolver.m from [f53bdeefa1] to [8914dd7f51].

152
153
154
155
156
157
158
























159
160
161
162
163
164
165
		    settings: (OFDNSResolverSettings *)settings
	    nameServersIndex: (size_t)nameServersIndex
	  searchDomainsIndex: (size_t)searchDomainsIndex
		      target: (id)target
		    selector: (SEL)selector
		     context: (id)context;
@end

























@interface OFDNSResolver ()
- (void)of_setDefaults;
- (void)of_obtainSystemConfig;
#if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_3DS)
- (void)of_parseHosts: (OFString *)path;
# if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS4)







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







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
		    settings: (OFDNSResolverSettings *)settings
	    nameServersIndex: (size_t)nameServersIndex
	  searchDomainsIndex: (size_t)searchDomainsIndex
		      target: (id)target
		    selector: (SEL)selector
		     context: (id)context;
@end

@interface OFDNSResolver_ResolveSocketAddressContext: OFObject
{
	OFString *_host;
	id _target;
	SEL _selector;
	id _context;
	OFMutableArray OF_GENERIC(OF_KINDOF(OFDNSResourceRecord *)) *_records;
@public
	unsigned int _expectedResponses;
}

- (instancetype)initWithHost: (OFString *)host
		      target: (id)target
		    selector: (SEL)selector
		     context: (id)context;
-	(void)resolver: (OFDNSResolver *)resolver
  didResolveDomainName: (OFString *)domainName
	 answerRecords: (OFArray *)answerRecords
      authorityRecords: (OFArray *)authorityRecords
     additionalRecords: (OFArray *)additionalRecords
	       context: (id)context
	     exception: (id)exception;
@end

@interface OFDNSResolver ()
- (void)of_setDefaults;
- (void)of_obtainSystemConfig;
#if defined(OF_HAVE_FILES) && !defined(OF_NINTENDO_3DS)
- (void)of_parseHosts: (OFString *)path;
# if !defined(OF_WINDOWS) && !defined(OF_AMIGAOS4)
750
751
752
753
754
755
756

























































































































757
758
759
760
761
762
763
	[_settings release];
	[_target release];
	[_context release];
	[_queryData release];
	[_cancelTimer release];

	[super dealloc];

























































































































}
@end

@implementation OFDNSResolver
@synthesize staticHosts = _staticHosts, nameServers = _nameServers;
@synthesize localDomain = _localDomain, searchDomains = _searchDomains;
@synthesize timeout = _timeout, maxAttempts = _maxAttempts;







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







774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
	[_settings release];
	[_target release];
	[_context release];
	[_queryData release];
	[_cancelTimer release];

	[super dealloc];
}
@end

@implementation OFDNSResolver_ResolveSocketAddressContext
- (instancetype)initWithHost: (OFString *)host
		      target: (id)target
		    selector: (SEL)selector
		     context: (id)context
{
	self = [super init];

	@try {
		_host = [host copy];
		_target = [target retain];
		_selector = selector;
		_context = [context retain];

		_records = [[OFMutableArray alloc] init];
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	[_host release];
	[_target release];
	[_context release];
	[_records release];

	[super dealloc];
}

-	(void)resolver: (OFDNSResolver *)resolver
  didResolveDomainName: (OFString *)domainName
	 answerRecords: (OFArray *)answerRecords
      authorityRecords: (OFArray *)authorityRecords
     additionalRecords: (OFArray *)additionalRecords
	       context: (id)context
	     exception: (id)exception
{
	/*
	 * TODO: Error handling could be improved. Ignore error if there are
	 * responses, otherwise propagate error.
	 */

	of_socket_address_family_t addressFamily = [context intValue];

	_expectedResponses--;

	if (exception == nil) {
		for (OFDNSResourceRecord *record in answerRecords) {
			if (![[record name] isEqual: domainName])
				continue;

			if ([record recordClass] !=
			    OF_DNS_RESOURCE_RECORD_CLASS_IN)
				continue;

			switch ([record recordType]) {
			case OF_DNS_RESOURCE_RECORD_TYPE_A:
				if (addressFamily ==
				    OF_SOCKET_ADDRESS_FAMILY_IPV4 ||
				    addressFamily ==
				    OF_SOCKET_ADDRESS_FAMILY_ANY)
					[_records addObject: record];

				break;
			case OF_DNS_RESOURCE_RECORD_TYPE_AAAA:
				if (addressFamily ==
				    OF_SOCKET_ADDRESS_FAMILY_IPV6 ||
				    addressFamily ==
				    OF_SOCKET_ADDRESS_FAMILY_ANY)
					[_records addObject: record];

				break;
			/*
			 * TODO: Add CNAMEs and replace them with addresses in
			 *	 a later stage.
			 */
			default:
				break;
			}
		}
	}

	if (_expectedResponses == 0) {
		void (*method)(id, SEL, OFDNSResolver *, OFString *, OFData *,
		    id, id) = (void (*)(id, SEL, OFDNSResolver *, OFString *,
		    OFData *, id, id))[_target methodForSelector: _selector];
		OFMutableData *addresses = nil;

		if ([_records count] > 0) {
			addresses = [OFMutableData
			    dataWithItemSize: sizeof(of_socket_address_t)
				    capacity: [_records count]];

			for (OF_KINDOF(OFDNSResourceRecord *) record in
			    _records)
				[addresses addItem: [record address]];

			[addresses makeImmutable];

			method(_target, _selector, resolver, domainName,
			    addresses, _context, nil);
		} else {
			OFResolveHostFailedException *e;

			e = [OFResolveHostFailedException
			    exceptionWithHost: _host
				  recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN
				   recordType: 0
					error: OF_DNS_RESOLVER_ERROR_UNKNOWN];

			method(_target, _selector, resolver, domainName, nil,
			    _context, e);
		}
	}
}
@end

@implementation OFDNSResolver
@synthesize staticHosts = _staticHosts, nameServers = _nameServers;
@synthesize localDomain = _localDomain, searchDomains = _searchDomains;
@synthesize timeout = _timeout, maxAttempts = _maxAttempts;
1605
1606
1607
1608
1609
1610
1611














































































1612
1613
1614
1615
1616
1617
1618

	callback(query->_target, query->_selector, self, query->_domainName,
	    answerRecords, authorityRecords, additionalRecords,
	    query->_context, nil);

	return false;
}















































































- (void)close
{
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator OF_GENERIC(OFDNSResolverQuery *) *enumerator;
	OFDNSResolverQuery *query;








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







1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841

	callback(query->_target, query->_selector, self, query->_domainName,
	    answerRecords, authorityRecords, additionalRecords,
	    query->_context, nil);

	return false;
}

- (void)asyncResolveSocketAddressesForHost: (OFString *)host
				    target: (id)target
				  selector: (SEL)selector
				   context: (nullable id)context
{
#ifdef OF_HAVE_IPV6
	[self asyncResolveSocketAddressesForHost: host
				   addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY
					  target: target
					selector: selector
					 context: context];
#else
	[self asyncResolveSocketAddressesForHost: host
				   addressFamily: OF_SOCKET_ADDRESS_FAMILY_IPV4
					  target: target
					selector: selector
					 context: context];
#endif
}

- (void)asyncResolveSocketAddressesForHost: (OFString *)host
			     addressFamily: (of_socket_address_family_t)
						addressFamily
				    target: (id)target
				  selector: (SEL)selector
				   context: (id)userContext
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSResolver_ResolveSocketAddressContext *context;
	OFNumber *addressFamilyNumber;

	context = [[[OFDNSResolver_ResolveSocketAddressContext alloc]
	    initWithHost: host
		  target: target
		selector: selector
		 context: userContext] autorelease];

	switch (addressFamily) {
	case OF_SOCKET_ADDRESS_FAMILY_IPV4:
	case OF_SOCKET_ADDRESS_FAMILY_IPV6:
		context->_expectedResponses = 1;
		break;
	case OF_SOCKET_ADDRESS_FAMILY_ANY:
		context->_expectedResponses = 2;
		break;
	default:
		@throw [OFInvalidArgumentException exception];
	}

	addressFamilyNumber = [OFNumber numberWithInt: addressFamily];

	if (addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV6 ||
	    addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY)
		[self asyncResolveHost: host
			   recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN
			    recordType: OF_DNS_RESOURCE_RECORD_TYPE_AAAA
				target: context
			      selector: @selector(resolver:didResolveDomainName:
					    answerRecords:authorityRecords:
					    additionalRecords:context:
					    exception:)
			       context: addressFamilyNumber];

	if (addressFamily == OF_SOCKET_ADDRESS_FAMILY_IPV4 ||
	    addressFamily == OF_SOCKET_ADDRESS_FAMILY_ANY)
		[self asyncResolveHost: host
			   recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN
			    recordType: OF_DNS_RESOURCE_RECORD_TYPE_A
				target: context
			      selector: @selector(resolver:didResolveDomainName:
					    answerRecords:authorityRecords:
					    additionalRecords:context:
					    exception:)
			       context: addressFamilyNumber];

	objc_autoreleasePoolPop(pool);
}

- (void)close
{
	void *pool = objc_autoreleasePoolPush();
	OFEnumerator OF_GENERIC(OFDNSResolverQuery *) *enumerator;
	OFDNSResolverQuery *query;

Modified src/socket.h from [0a5ee032af] to [2431bf7b16].

79
80
81
82
83
84
85

86

87

88


89
90
91
92
93
94
95
typedef u8 sa_family_t;
#endif

/*!
 * @brief A socket address family.
 */
typedef enum {

	OF_SOCKET_ADDRESS_FAMILY_UNKNOWN,

	OF_SOCKET_ADDRESS_FAMILY_IPV4,

	OF_SOCKET_ADDRESS_FAMILY_IPV6,


} of_socket_address_family_t;

#ifndef OF_HAVE_IPV6
struct sockaddr_in6 {
	sa_family_t sin6_family;
	in_port_t sin6_port;
	uint32_t sin6_flowinfo;







>

>

>

>
>







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
typedef u8 sa_family_t;
#endif

/*!
 * @brief A socket address family.
 */
typedef enum {
	/** An unknown address family. */
	OF_SOCKET_ADDRESS_FAMILY_UNKNOWN,
	/** IPv4 */
	OF_SOCKET_ADDRESS_FAMILY_IPV4,
	/** IPv6 */
	OF_SOCKET_ADDRESS_FAMILY_IPV6,
	/** Any address family */
	OF_SOCKET_ADDRESS_FAMILY_ANY = 255
} of_socket_address_family_t;

#ifndef OF_HAVE_IPV6
struct sockaddr_in6 {
	sa_family_t sin6_family;
	in_port_t sin6_port;
	uint32_t sin6_flowinfo;