ObjFW  Check-in [25b05b7bce]

Overview
Comment:OFDNSResolver: Resolve CNAMEs for socket addresses

This only resolves them when they have been included in the answer.
Sending another query when they are not is the next step.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 25b05b7bcea56a2956a26215c5e07c06d391e5c8981a6f9b09580322c7926c76
User & Date: js on 2018-09-15 17:39:03
Other Links: manifest | tags
Context
2018-09-15
20:04
OFDNSResolver: Fix compilation with GCC check-in: c4836192b2 user: js tags: trunk
17:39
OFDNSResolver: Resolve CNAMEs for socket addresses check-in: 25b05b7bce user: js tags: trunk
13:45
OFDNSResolver: Group records by domain name check-in: 7ba597c52d user: js tags: trunk
Changes

Modified src/OFDNSResolver.m from [721e3310ec] to [9407cece68].

69
70
71
72
73
74
75


76
77
78
79
80
81
82
 * 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



#if defined(OF_HAIKU)
# define HOSTS_PATH @"/system/settings/network/hosts"
# define RESOLV_CONF_PATH @"/system/settings/network/resolv.conf"
#elif defined(OF_MORPHOS)
# define HOSTS_PATH @"ENV:sys/net/hosts"
# define RESOLV_CONF_PATH @"ENV:sys/net/resolv.conf"
#elif defined(OF_AMIGAOS4)







>
>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
 * 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 CNAME_RECURSION 3

#if defined(OF_HAIKU)
# define HOSTS_PATH @"/system/settings/network/hosts"
# define RESOLV_CONF_PATH @"/system/settings/network/resolv.conf"
#elif defined(OF_MORPHOS)
# define HOSTS_PATH @"ENV:sys/net/hosts"
# define RESOLV_CONF_PATH @"ENV:sys/net/resolv.conf"
#elif defined(OF_AMIGAOS4)
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
909
910
911
912
913
914
915
916
917
918
919
920
921
	[_host release];
	[_target release];
	[_context release];
	[_records release];

	[super dealloc];
}







































































































-	(void)resolver: (OFDNSResolver *)resolver
  didResolveDomainName: (OFString *)domainName
	 answerRecords: (OFDictionary *)answerRecords
      authorityRecords: (OFDictionary *)authorityRecords
     additionalRecords: (OFDictionary *)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 objectForKey: domainName]) {
			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;







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














|



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

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

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







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
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953





954







955




956


957
958






959
960

961





962
963



964

965

966

967




968





969
970



971
972
973
974
975
976
977
	[_host release];
	[_target release];
	[_context release];
	[_records release];

	[super dealloc];
}

- (bool)parseRecords: (OFArray *)records
	    resolver: (OFDNSResolver *)resolver
       answerRecords: (OFDictionary *)answerRecords
   additionalRecords: (OFDictionary *)additionalRecords
	  recordType: (of_dns_resource_record_type_t)recordType
	   recursion: (unsigned int)recursion
{
	bool found = false;

	for (OF_KINDOF(OFDNSResourceRecord *) record in records) {
		if ([record recordClass] != OF_DNS_RESOURCE_RECORD_CLASS_IN)
			continue;

		if ([record recordType] == recordType) {
			[_records addObject: record];
			found = true;
		} else if ([record recordType] ==
		    OF_DNS_RESOURCE_RECORD_TYPE_CNAME) {
			[self	resolveCNAME: record
				    resolver: resolver
			       answerRecords: answerRecords
			   additionalRecords: additionalRecords
				  recordType: recordType
				   recursion: recursion];
			found = true;
		}
	}

	return found;
}

- (void)resolveCNAME: (OFCNAMEDNSResourceRecord *)CNAME
	    resolver: (OFDNSResolver *)resolver
       answerRecords: (OFDictionary *)answerRecords
   additionalRecords: (OFDictionary *)additionalRecords
	  recordType: (of_dns_resource_record_type_t)recordType
	   recursion: (unsigned int)recursion
{
	OFString *domainName = [CNAME alias];
	bool found = false;

	if (recursion == 0)
		return;

	if ([self parseRecords: [answerRecords objectForKey: domainName]
		      resolver: resolver
		 answerRecords: answerRecords
	     additionalRecords: additionalRecords
		    recordType: recordType
		     recursion: recursion - 1])
		found = true;

	if ([self parseRecords: [additionalRecords objectForKey: domainName]
		      resolver: resolver
		 answerRecords: answerRecords
	     additionalRecords: additionalRecords
		    recordType: recordType
		     recursion: recursion - 1])
		found = true;

	if (!found) {
		/*
		 * TODO: Send request, add CNAME to _records and replace once
		 *	 resolved.
		 */
	}
}

- (void)doneWithDomainName: (OFString *)domainName
		  resolver: (OFDNSResolver *)resolver
{
	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);
	}
}

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

	of_dns_resource_record_type_t recordType = [context intValue];

	_expectedResponses--;

	if (exception != nil) {





		if (_expectedResponses == 0)







			[self doneWithDomainName: domainName




					resolver: resolver];



		return;






	}


	[self parseRecords: [answerRecords objectForKey: domainName]





		  resolver: resolver
	     answerRecords: answerRecords



	 additionalRecords: additionalRecords

		recordType: recordType

		 recursion: CNAME_RECURSION];






	if (_expectedResponses == 0)





		[self doneWithDomainName: domainName
				resolver: resolver];



}
@end

@implementation OFDNSResolver
@synthesize staticHosts = _staticHosts, nameServers = _nameServers;
@synthesize localDomain = _localDomain, searchDomains = _searchDomains;
@synthesize timeout = _timeout, maxAttempts = _maxAttempts;
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
1842
1843
1844

1845
1846
1847
1848
1849
1850
1851
						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();







<



















<
<










|
>











|
>







1849
1850
1851
1852
1853
1854
1855

1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874


1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
						addressFamily
				    target: (id)target
				  selector: (SEL)selector
				   context: (id)userContext
{
	void *pool = objc_autoreleasePoolPush();
	OFDNSResolver_ResolveSocketAddressContext *context;


	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];
	}



	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: [OFNumber numberWithInt:
					    OF_DNS_RESOURCE_RECORD_TYPE_AAAA]];

	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: [OFNumber numberWithInt:
					    OF_DNS_RESOURCE_RECORD_TYPE_A]];

	objc_autoreleasePoolPop(pool);
}

- (void)close
{
	void *pool = objc_autoreleasePoolPush();