diff options
| author | Eric Andersen <andersen@codepoet.org> | 2005-04-28 20:29:58 +0000 | 
|---|---|---|
| committer | Eric Andersen <andersen@codepoet.org> | 2005-04-28 20:29:58 +0000 | 
| commit | ead7e247a8f03b11551e872b8098aafbce9fa602 (patch) | |
| tree | cd5458c882e884f0a672bbd5786d8b19a76bcbe0 /libc | |
| parent | 5aa1733024895911ef4e94ed47f81c8df15fef0a (diff) | |
Fix it so uClibc returns multiple ips via h_addr_list,
This fix, based on this patch
	http://bugs.uclibc.org/view.php?id=104
makes it so uClibc fills out round robin dns lists for
applications such as nslookup:
Before:
	$ nslookup google.com
	Server:     mace.codepoet.org
	Address:    10.10.10.1
	Name:       google.com
	Address:    216.239.39.99
After:
	$ nslookup google.com
	Server:     mace.codepoet.org
	Address:    10.10.10.1
	Name:       google.com
	Addresses:  216.239.57.99, 216.239.37.99, 216.239.39.99
Diffstat (limited to 'libc')
| -rw-r--r-- | libc/inet/resolv.c | 183 | 
1 files changed, 117 insertions, 66 deletions
| diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c index 6ceb07796..368e52893 100644 --- a/libc/inet/resolv.c +++ b/libc/inet/resolv.c @@ -216,6 +216,9 @@ struct resolv_answer {  	int rdlength;  	unsigned char * rdata;  	int rdoffset; +	char* buf; +	size_t buflen; +	size_t add_count;  };  enum etc_hosts_action { @@ -669,6 +672,8 @@ int __dns_lookup(const char *name, int type, int nscount, char **nsip,  	fd_set fds;  	struct resolv_header h;  	struct resolv_question q; +	struct resolv_answer ma; +	int first_answer = 1;  	int retries = 0;  	unsigned char * packet = malloc(PACKETSZ);  	char *dns, *lookup = malloc(MAXDNAME); @@ -831,21 +836,56 @@ int __dns_lookup(const char *name, int type, int nscount, char **nsip,  		}  		DPRINTF("Decoding answer at pos %d\n", pos); -		for (j=0;j<h.ancount;j++) +		first_answer = 1; +		for (j=0;j<h.ancount;j++,pos += i)  		{ -		    i = __decode_answer(packet, pos, a); +		    i = __decode_answer(packet, pos, &ma);  		    if (i<0) {  			DPRINTF("failed decode %d\n", i);  			goto again;  		    } -		    /* For all but T_SIG, accept first answer */ -		    if (a->atype != T_SIG) -			break; -		    DPRINTF("skipping T_SIG %d\n", i); -		    free(a->dotted); -		    pos += i; +		    if ( first_answer ) +		    { +			ma.buf = a->buf; +			ma.buflen = a->buflen; +			ma.add_count = a->add_count; +			memcpy(a, &ma, sizeof(ma)); +			if (a->atype != T_SIG && (0 == a->buf || (type != T_A && type != T_AAAA))) +			{ +			    break; +			} +			if (a->atype != type) +			{ +			    free(a->dotted); +			    continue; +			} +			a->add_count = h.ancount - j - 1; +			if ((a->rdlength + sizeof(struct in_addr*)) * a->add_count > a->buflen) +			{ +			    break; +			} +			a->add_count = 0; +			first_answer = 0; +		    } +		    else +		    { +			free(ma.dotted); +			if (ma.atype != type) +			{ +			    continue; +			} +			if (a->rdlength != ma.rdlength) +			{ +			    free(a->dotted); +			    DPRINTF("Answer address len(%u) differs from original(%u)\n", +				    ma.rdlength, a->rdlength); +			    goto again; +			} +			memcpy(a->buf + (a->add_count * ma.rdlength), ma.rdata, ma.rdlength); +			++a->add_count; +		    }  		}  		DPRINTF("Answer name = |%s|\n", a->dotted); @@ -1397,10 +1437,8 @@ int __read_etc_hosts_r(FILE * fp, const char * name, int type,  	struct in6_addr	*in6=NULL;  	struct in6_addr	**addr_list6=NULL;  #endif /* __UCLIBC_HAS_IPV6__ */ -	char					*cp; -	char					**alias; -	int						aliases, i; -	int		ret=HOST_NOT_FOUND; +	char *cp, **alias; +	int aliases, i, ret=HOST_NOT_FOUND;  	if (buflen < sizeof(char *)*(ALIAS_DIM))  		return ERANGE; @@ -1619,7 +1657,8 @@ int __get_hosts_byname_r(const char * name, int type,  			    struct hostent ** result,  			    int * h_errnop)  { -	return(__read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME, result_buf, buf, buflen, result, h_errnop)); +	return(__read_etc_hosts_r(NULL, name, type, GET_HOSTS_BYNAME, +		    result_buf, buf, buflen, result, h_errnop));  }  #endif @@ -1706,7 +1745,8 @@ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,  			if (!(flags & NI_NUMERICHOST)) {  #ifdef __UCLIBC_HAS_IPV6__  				if (sa->sa_family == AF_INET6) -					h = gethostbyaddr ((const void *) &(((const struct sockaddr_in6 *) sa)->sin6_addr), +					h = gethostbyaddr ((const void *) +						&(((const struct sockaddr_in6 *) sa)->sin6_addr),  						sizeof(struct in6_addr), AF_INET6);  				else  #endif /* __UCLIBC_HAS_IPV6__ */ @@ -1745,7 +1785,7 @@ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,  						c = inet_ntop (AF_INET6,  							(const void *) &sin6p->sin6_addr, host, hostlen);  #if 0 -/* Does scope id need to be supported? */ +						/* Does scope id need to be supported? */  						uint32_t scopeid;  						scopeid = sin6p->sin6_scope_id;  						if (scopeid != 0) { @@ -1768,7 +1808,7 @@ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,  									scopelen = strlen (scopebuf);  							} else {  								++ni_numericscope; -                            } +							}  							if (ni_numericscope)  								scopelen = 1 + snprintf (scopeptr, @@ -1784,8 +1824,8 @@ int getnameinfo (const struct sockaddr *sa, socklen_t addrlen, char *host,  #endif  					} else  #endif /* __UCLIBC_HAS_IPV6__ */ -						c = inet_ntop (AF_INET, -							(const void *) &(((const struct sockaddr_in *) sa)->sin_addr), +						c = inet_ntop (AF_INET, (const void *) +							&(((const struct sockaddr_in *) sa)->sin_addr),  							host, hostlen);  					if (c == NULL) { @@ -1867,7 +1907,6 @@ int gethostbyname_r(const char * name,  	unsigned char *packet;  	struct resolv_answer a;  	int i; -	int nest = 0;  	int __nameserversXX;  	char ** __nameserverXX; @@ -1882,7 +1921,7 @@ int gethostbyname_r(const char * name,  		__set_errno(0);			/* to check for missing /etc/hosts. */  		if ((i=__get_hosts_byname_r(name, AF_INET, result_buf, -									buf, buflen, result, h_errnop))==0) +				buf, buflen, result, h_errnop))==0)  			return i;  		switch (*h_errnop) {  			case HOST_NOT_FOUND: @@ -1944,54 +1983,61 @@ int gethostbyname_r(const char * name,  	for (;;) { -	BIGLOCK; -	__nameserversXX=__nameservers; -	__nameserverXX=__nameserver; -	BIGUNLOCK; -		i = __dns_lookup(buf, T_A, __nameserversXX, __nameserverXX, &packet, &a); - -		if (i < 0) { -			*h_errnop = HOST_NOT_FOUND; -			DPRINTF("__dns_lookup\n"); -			return TRY_AGAIN; -		} - -		strncpy(buf, a.dotted, buflen); +	    BIGLOCK; +	    __nameserversXX=__nameservers; +	    __nameserverXX=__nameserver; +	    BIGUNLOCK; +	    a.buf = buf; +	    a.buflen = buflen; +	    a.add_count = 0; +	    i = __dns_lookup(name, T_A, __nameserversXX, __nameserverXX, &packet, &a); + +	    if (i < 0) { +		*h_errnop = HOST_NOT_FOUND; +		DPRINTF("__dns_lookup\n"); +		return TRY_AGAIN; +	    } + +	    if ((a.rdlength + sizeof(struct in_addr*)) * a.add_count + 256 > buflen) +	    {  		free(a.dotted); - -		if (a.atype == T_CNAME) {		/* CNAME */ -			DPRINTF("Got a CNAME in gethostbyname()\n"); -			i = __decode_dotted(packet, a.rdoffset, buf, buflen); -			free(packet); - -			if (i < 0) { -				*h_errnop = NO_RECOVERY; -				DPRINTF("__decode_dotted\n"); -				return -1; -			} -			if (++nest > MAX_RECURSE) { -				*h_errnop = NO_RECOVERY; -				DPRINTF("recursion\n"); -				return -1; -			} -			continue; -		} else if (a.atype == T_A) {	/* ADDRESS */ -			memcpy(in, a.rdata, sizeof(*in)); -			result_buf->h_name = buf; -			result_buf->h_addrtype = AF_INET; -			result_buf->h_length = sizeof(*in); -			result_buf->h_addr_list = (char **) addr_list; +		free(packet); +		*h_errnop = NETDB_INTERNAL; +		DPRINTF("buffer to small(multiple addresses)\n"); +		return ERANGE; +	    } +	    else if(a.add_count > 0) +	    { +		memmove(buf - sizeof(struct in_addr*)*2, buf, a.add_count * a.rdlength); +		addr_list = (struct in_addr**)(buf + a.add_count * a.rdlength); +		addr_list[0] = in; +		for (i = a.add_count-1; i>=0; --i) +		    addr_list[i+1] = (struct in_addr*)(buf - sizeof(struct in_addr*)*2 + a.rdlength * i); +		addr_list[a.add_count + 1] = 0; +		buflen -= (((char*)&(addr_list[a.add_count + 2])) - buf); +		buf = (char*)&addr_list[a.add_count + 2]; +	    } + +	    strncpy(buf, a.dotted, buflen); +	    free(a.dotted); + +	    if (a.atype == T_A) { /* ADDRESS */ +		memcpy(in, a.rdata, sizeof(*in)); +		result_buf->h_name = buf; +		result_buf->h_addrtype = AF_INET; +		result_buf->h_length = sizeof(*in); +		result_buf->h_addr_list = (char **) addr_list;  #ifdef __UCLIBC_MJN3_ONLY__  #warning TODO -- generate the full list  #endif -			result_buf->h_aliases = alias; /* TODO: generate the full list */ -			free(packet); -			break; -		} else { -			free(packet); -			*h_errnop=HOST_NOT_FOUND; -			return TRY_AGAIN; -		} +		result_buf->h_aliases = alias; /* TODO: generate the full list */ +		free(packet); +		break; +	    } else { +		free(packet); +		*h_errnop=HOST_NOT_FOUND; +		return TRY_AGAIN; +	    }  	}  	*result=result_buf; @@ -2009,7 +2055,8 @@ int gethostbyname2_r(const char *name, int family,  			    int * h_errnop)  {  #ifndef __UCLIBC_HAS_IPV6__ -	return family == AF_INET ? gethostbyname_r(name, result_buf, buf, buflen, result, h_errnop) : HOST_NOT_FOUND; +	return family == (AF_INET)? gethostbyname_r(name, result_buf, +		buf, buflen, result, h_errnop) : HOST_NOT_FOUND;  #else /* __UCLIBC_HAS_IPV6__ */  	struct in6_addr *in;  	struct in6_addr **addr_list; @@ -2037,7 +2084,7 @@ int gethostbyname2_r(const char *name, int family,  		__set_errno(0);			/* to check for missing /etc/hosts. */  		if ((i=__get_hosts_byname_r(name, AF_INET, result_buf, -									buf, buflen, result, h_errnop))==0) +				buf, buflen, result, h_errnop))==0)  			return i;  		switch (*h_errnop) {  			case HOST_NOT_FOUND: @@ -2087,6 +2134,8 @@ int gethostbyname2_r(const char *name, int family,  	    return NETDB_SUCCESS;  	} +	memset((char *) &a, '\0', sizeof(a)); +  	for (;;) {  	BIGLOCK;  	__nameserversXX=__nameservers; @@ -2166,6 +2215,8 @@ int gethostbyaddr_r (const void *addr, socklen_t len, int type,  	if (!addr)  		return EINVAL; +	memset((char *) &a, '\0', sizeof(a)); +  	switch (type) {  		case AF_INET:  			if (len != sizeof(struct in_addr)) | 
