summaryrefslogtreecommitdiff
path: root/libc
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2005-04-28 20:29:58 +0000
committerEric Andersen <andersen@codepoet.org>2005-04-28 20:29:58 +0000
commitead7e247a8f03b11551e872b8098aafbce9fa602 (patch)
treecd5458c882e884f0a672bbd5786d8b19a76bcbe0 /libc
parent5aa1733024895911ef4e94ed47f81c8df15fef0a (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.c183
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))