diff options
| -rw-r--r-- | include/resolv.h | 33 | ||||
| -rw-r--r-- | libc/inet/resolv.c | 162 | 
2 files changed, 140 insertions, 55 deletions
diff --git a/include/resolv.h b/include/resolv.h index 6651dded9..d5168770d 100644 --- a/include/resolv.h +++ b/include/resolv.h @@ -118,10 +118,9 @@ struct __res_state {  	int	retry;			/* number of times to retransmit */  #endif  	u_int32_t options;		/* (was: ulong) option flags - see below. */ -	struct sockaddr_in -		nsaddr_list[MAXNS];	/* address of name server */ +	struct sockaddr_in nsaddr_list[MAXNS]; /* address of name server */  #define nsaddr nsaddr_list[0]		/* for backward compatibility */ -	char	*dnsrch[MAXDNSRCH+1];	/* components of domain to search */ +	char	*dnsrch[MAXDNSRCH + 1];	/* components of domain to search */  #ifdef __UCLIBC_HAS_COMPAT_RES_STATE__  	/* googling for "_res.defdname" says it's still sometimes used.  	 * Pity. It's huge, I want to move it to EXTRA_COMPAT... */ @@ -138,18 +137,38 @@ struct __res_state {  		struct in_addr	addr;  		u_int32_t	mask;  	} sort_list[MAXRESOLVSORT]; +#endif + +#ifdef __UCLIBC_HAS_IPV6__ +	/* I assume that the intention is to store all +	 * DNS servers' addresses here, and duplicate in nsaddr_list[] +	 * those which have IPv4 address. In the case of IPv4 address +	 * _u._ext.nsaddrs[x] will point to some nsaddr_list[y], +	 * otherwise it will point into malloc'ed sockaddr_in6. +	 * nscount is the number of IPv4 addresses and _u._ext.nscount +	 * is the number of addresses of all kinds. +	 * +	 * If this differs from established usage and you need +	 * to change this, please describe how is it supposed to work. +	 */  	union {  		struct { -			u_int16_t		nscount; -			u_int16_t		nstimes[MAXNS]; /* ms. */ +			struct sockaddr_in6	*nsaddrs[MAXNS]; +			u_int8_t		nscount; /* (was: u_int16_t) */ +#ifdef __UCLIBC_HAS_COMPAT_RES_STATE__ +			/* rather obscure, and differs in BSD and glibc */ +			u_int16_t		nstimes[MAXNS];  			int			nssocks[MAXNS]; -			/* below: not in xBSD. glibc only? */  			u_int16_t		nscount6;  			u_int16_t		nsinit; -			struct sockaddr_in6	*nsaddrs[MAXNS]; +			/* glibc also has: */ +			/*u_int16_t		nsmap[MAXNS];*/ +			/*unsigned long long	initstamp;*/ +#endif  		} _ext;  	} _u;  #endif +  #ifdef __UCLIBC_HAS_EXTRA_COMPAT_RES_STATE__  	/* Truly obscure stuff.  	 * Googling for "_res.XXX" for these members diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index defd4d77c..4a1f97541 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -241,6 +241,9 @@ libc_hidden_proto(__libc_getdomainname)  #define DPRINTF(X,args...)  #endif /* DEBUG */ +#undef ARRAY_SIZE +#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0])) +  /* Make sure the incoming char * buffer is aligned enough to handle our random   * structures.  This define is the same as we use for malloc alignment (which   * has same requirements).  The offset is the number of bytes we need to adjust @@ -305,6 +308,11 @@ extern unsigned __nameservers attribute_hidden;  extern unsigned __searchdomains attribute_hidden;  extern sockaddr46_t *__nameserver attribute_hidden;  extern char **__searchdomain attribute_hidden; +#ifdef __UCLIBC_HAS_IPV4__ +extern const struct sockaddr_in __local_nameserver attribute_hidden; +#else +extern const struct sockaddr_in6 __local_nameserver attribute_hidden; +#endif  /* Arbitrary */  #define MAXLEN_searchdomain 128 @@ -858,6 +866,12 @@ int __form_query(int id, const char *name, int type, unsigned char *packet,  #ifdef L_opennameservers +# if __BYTE_ORDER == __LITTLE_ENDIAN +#define NAMESERVER_PORT_N (__bswap_constant_16(NAMESERVER_PORT)) +#else +#define NAMESERVER_PORT_N NAMESERVER_PORT +#endif +  __UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_MUTEX_INITIALIZER);  /* Protected by __resolv_lock */ @@ -867,6 +881,17 @@ unsigned __nameservers;  unsigned __searchdomains;  sockaddr46_t *__nameserver;  char **__searchdomain; +#ifdef __UCLIBC_HAS_IPV4__ +const struct sockaddr_in __local_nameserver = { +	.sin_family = AF_INET, +	.sin_port = NAMESERVER_PORT_N, +}; +#else +const struct sockaddr_in6 __local_nameserver = { +	.sin6_family = AF_INET6, +	.sin6_port = NAMESERVER_PORT_N, +}; +#endif  /* Helpers. Both stop on EOL, if it's '\n', it is converted to NUL first */  static char *skip_nospace(char *p) @@ -954,30 +979,28 @@ void attribute_hidden __open_nameservers(void)  			}  			if (strcmp(keyword, "domain") == 0 || strcmp(keyword, "search") == 0) {  				char *p1; -//TODO: delete old domains... + +				/* free old domains ("last 'domain' or 'search' wins" rule) */ +				while (__searchdomains) +					free(__searchdomain[--__searchdomains]); +				/*free(__searchdomain);*/ +				/*__searchdomain = NULL; - not necessary */   next_word:  				/* terminate current word */  				p1 = skip_nospace(p);  				/* find next word (maybe) */  				p1 = skip_and_NUL_space(p1); -				/* paranoia - done by having szBuffer[MAXLEN_searchdomain] */ -				/*if (strlen(p) > MAXLEN_searchdomain)*/ -				/*	goto skip;*/ -				/* do we have this domain already? */ -				for (i = 0; i < __searchdomains; i++) -					if (strcmp(p, __searchdomain[i]) == 0) -						goto skip;  				/* add it */  				ptr = realloc(__searchdomain, (__searchdomains + 1) * sizeof(__searchdomain[0]));  				if (!ptr)  					continue;  				__searchdomain = ptr; +				/* NB: strlen(p) <= MAXLEN_searchdomain) because szBuffer[] is smaller */  				ptr = strdup(p);  				if (!ptr)  					continue;  				DPRINTF("adding search %s\n", (char*)ptr);  				__searchdomain[__searchdomains++] = (char*)ptr; -	 skip:  				p = p1;  				if (*p)  					goto next_word; @@ -989,17 +1012,7 @@ void attribute_hidden __open_nameservers(void)  		fclose(fp);  	}  	if (__nameservers == 0) { -		__nameserver = malloc(sizeof(__nameserver[0])); -//TODO: error check? -		memset(&__nameserver[0], 0, sizeof(__nameserver[0])); -#ifdef __UCLIBC_HAS_IPV4__ -		__nameserver[0].sa4.sin_family = AF_INET; -		/*__nameserver[0].sa4.sin_addr = INADDR_ANY; - done by memset */ -		__nameserver[0].sa4.sin_port = htons(NAMESERVER_PORT); -#else -		__nameserver[0].sa6.sin6_family = AF_INET6; -		__nameserver[0].sa6.sin6_port = htons(NAMESERVER_PORT); -#endif +		__nameserver = (void*) &__local_nameserver;  		__nameservers++;  	}  	if (__searchdomains == 0) { @@ -1009,10 +1022,16 @@ void attribute_hidden __open_nameservers(void)  		buf[sizeof(buf) - 1] = '\0';  		if (i == 0 && (p = strchr(buf, '.')) != NULL && p[1]) {  			p = strdup(p + 1); +			if (!p) +				goto err;  			__searchdomain = malloc(sizeof(__searchdomain[0])); -//TODO: error check? +			if (!__searchdomain) { +				free(p); +				goto err; +			}  			__searchdomain[0] = p;  			__searchdomains++; + err: ;  		}  	}  	DPRINTF("nameservers = %d\n", __nameservers); @@ -1029,7 +1048,8 @@ void attribute_hidden __open_nameservers(void)  /* Must be called under __resolv_lock. */  void attribute_hidden __close_nameservers(void)  { -	free(__nameserver); +	if (__nameserver != (void*) &__local_nameserver) +		free(__nameserver);  	__nameserver = NULL;  	__nameservers = 0;  	while (__searchdomains) @@ -1383,7 +1403,7 @@ libc_hidden_def(gethostbyname)  struct hostent *gethostbyname2(const char *name, int family)  {  #ifndef __UCLIBC_HAS_IPV6__ -	return family == AF_INET ? gethostbyname(name) : (struct hostent*)0; +	return family == AF_INET ? gethostbyname(name) : (struct hostent*)NULL;  #else /* __UCLIBC_HAS_IPV6__ */  	static struct hostent h;  	static char buf[sizeof(struct in6_addr) + @@ -1404,30 +1424,27 @@ struct hostent *gethostbyname2(const char *name, int family)  /* Protected by __resolv_lock */  struct __res_state _res; -#undef ARRAY_SIZE -#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0])) -  /* Will be called under __resolv_lock. */  static void res_sync_func(void)  {  	struct __res_state *rp = &(_res); +	int n; -	/* Track .nscount and .nsaddr_list -	 * (busybox's nslookup uses it). -	 */ -	if (__nameservers > rp->nscount) -		__nameservers = rp->nscount;  	/* TODO:  	 * if (__nameservers < rp->nscount) - try to grow __nameserver[]?  	 */ -#ifdef __UCLIBC_HAS_IPV4__ -	{ -		int n = __nameservers; -		while (--n >= 0) { -			__nameserver[n].sa.sa_family = AF_INET; -			__nameserver[n].sa4 = rp->nsaddr_list[n]; /* struct copy */ -		} -	} +#ifdef __UCLIBC_HAS_IPV6__ +	if (__nameservers > rp->_u._ext.nscount) +		__nameservers = rp->_u._ext.nscount; +	n = __nameservers; +	while (--n >= 0) +		__nameserver[n].sa6 = *rp->_u._ext.nsaddrs[n]; /* struct copy */ +#else /* __UCLIBC_HAS_IPV4__ */ +	if (__nameservers > rp->nscount) +		__nameservers = rp->nscount; +	n = __nameservers; +	while (--n >= 0) +		__nameserver[n].sa4 = rp->nsaddr_list[n]; /* struct copy */  #endif  	/* Extend and comment what program is known  	 * to use which _res.XXX member(s). @@ -1439,8 +1456,12 @@ static void res_sync_func(void)  /* Our res_init never fails (always returns 0) */  int res_init(void)  { -	int i, n;  	struct __res_state *rp = &(_res); +	int i; +	int n; +#ifdef __UCLIBC_HAS_IPV6__ +	int m = 0; +#endif  	__UCLIBC_MUTEX_LOCK(__resolv_lock);  	__close_nameservers(); @@ -1465,23 +1486,57 @@ int res_init(void)  		n = ARRAY_SIZE(rp->dnsrch);  	for (i = 0; i < n; i++)  		rp->dnsrch[i] = __searchdomain[i]; -#ifdef __UCLIBC_HAS_IPV4__ + +	/* copy nameservers' addresses */  	i = 0; +#ifdef __UCLIBC_HAS_IPV4__  	n = 0;  	while (n < ARRAY_SIZE(rp->nsaddr_list) && i < __nameservers) { -		if (__nameserver[i].sa.sa_family != AF_INET) -			goto next_i; -		rp->nsaddr_list[n] = __nameserver[i].sa4; /* struct copy */ -		n++; - next_i: +		if (__nameserver[i].sa.sa_family == AF_INET) { +			rp->nsaddr_list[n] = __nameserver[i].sa4; /* struct copy */ +#ifdef __UCLIBC_HAS_IPV6__ +			if (m < ARRAY_SIZE(rp->_u._ext.nsaddrs)) { +				rp->_u._ext.nsaddrs[m] = (void*) &rp->nsaddr_list[n]; +				m++; +			} +#endif +			n++; +		} +#ifdef __UCLIBC_HAS_IPV6__ +		if (__nameserver[i].sa.sa_family == AF_INET6 +		 && m < ARRAY_SIZE(rp->_u._ext.nsaddrs) +		) { +			struct sockaddr_in6 *sa6 = malloc(sizeof(sa6)); +			if (sa6) { +				*sa6 = __nameserver[i].sa6; /* struct copy */ +				rp->_u._ext.nsaddrs[m] = sa6; +				m++; +			} +		} +#endif  		i++;  	}  	rp->nscount = n; +#ifdef __UCLIBC_HAS_IPV6__ +	rp->_u._ext.nscount = m;  #endif + +#else /* if !__UCLIBC_HAS_IPV4__ (only IPV6) */ +	while (m < ARRAY_SIZE(rp->_u._ext.nsaddrs) && i < __nameservers) { +		struct sockaddr_in6 *sa6 = malloc(sizeof(sa6)); +		if (sa6) { +			*sa6 = __nameserver[i].sa6; /* struct copy */ +			rp->_u._ext.nsaddrs[m] = sa6; +			m++; +		} +		i++; +	} +	rp->_u._ext.nscount = m; +#endif /* !__UCLIBC_HAS_IPV4__ (only IPV6) */ +  	__UCLIBC_MUTEX_UNLOCK(__resolv_lock);  	return 0;  } -#undef ARRAY_SIZE  libc_hidden_def(res_init)  #ifdef __UCLIBC_HAS_BSD_RES_CLOSE__ @@ -1490,6 +1545,18 @@ void res_close(void)  	__UCLIBC_MUTEX_LOCK(__resolv_lock);  	__close_nameservers();  	__res_sync = NULL; +#ifdef __UCLIBC_HAS_IPV6__ +	{ +		char *p1 = (char*) &(_res.nsaddr_list[0]); +		int m = 0; +		/* free nsaddrs[m] if they do not point to nsaddr_list[x] */ +		while (m < ARRAY_SIZE(_res._u._ext.nsaddrs)) { +			char *p2 = (char*)(_res._u._ext.nsaddrs[m]); +			if (p2 < p1 || (p2 - p1) > sizeof(_res.nsaddr_list)) +				free(p2); +		} +	} +#endif  	memset(&_res, 0, sizeof(_res));  	__UCLIBC_MUTEX_UNLOCK(__resolv_lock);  } @@ -1497,7 +1564,6 @@ void res_close(void)  #endif /* L_res_init */ -  #ifdef L_res_query  int res_query(const char *dname, int class, int type,  | 
