summaryrefslogtreecommitdiff
path: root/libc/inet
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-11-14 00:37:48 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-11-14 00:37:48 +0000
commit6bd2adf45125649541fdcdad99971d55d78486c9 (patch)
treeb859aee413b1f88ea8d10253c2631655fb2658b2 /libc/inet
parent97a8f9f4dc1f94060b74943d9887cffed268b21b (diff)
fixing resolver part 3: fix completely bogus locking
in __dns_lookup.
Diffstat (limited to 'libc/inet')
-rw-r--r--libc/inet/resolv.c615
1 files changed, 311 insertions, 304 deletions
diff --git a/libc/inet/resolv.c b/libc/inet/resolv.c
index b7ab27690..d388db13b 100644
--- a/libc/inet/resolv.c
+++ b/libc/inet/resolv.c
@@ -222,16 +222,16 @@ int __libc_getdomainname(char *name, size_t len);
libc_hidden_proto(__libc_getdomainname)
-#define MAX_RECURSE 5
+#define MAX_RECURSE 5
#define REPLY_TIMEOUT 10
-#define MAX_RETRIES 3
-#define MAX_SERVERS 3
-#define MAX_SEARCH 4
+#define MAX_RETRIES 3
+#define MAX_SERVERS 3
+#define MAX_SEARCH 4
-#define MAX_ALIASES 5
+#define MAX_ALIASES 5
/* 1:ip + 1:full + MAX_ALIASES:aliases + 1:NULL */
-#define ALIAS_DIM (2 + MAX_ALIASES + 1)
+#define ALIAS_DIM (2 + MAX_ALIASES + 1)
#undef DEBUG
/* #define DEBUG */
@@ -251,14 +251,6 @@ libc_hidden_proto(__libc_getdomainname)
#define ALIGN_BUFFER_OFFSET(buf) ((ALIGN_ATTR - ((size_t)buf % ALIGN_ATTR)) % ALIGN_ATTR)
-/* Global stuff (stuff needing to be locked to be thread safe)... */
-extern int __nameservers attribute_hidden;
-extern char * __nameserver[MAX_SERVERS] attribute_hidden;
-extern int __searchdomains attribute_hidden;
-extern char * __searchdomain[MAX_SEARCH] attribute_hidden;
-
-
-
/* Structs */
struct resolv_header {
int id;
@@ -294,6 +286,26 @@ enum etc_hosts_action {
GET_HOSTS_BYADDR,
};
+typedef union sockaddr46_t {
+ struct sockaddr sa;
+#ifdef __UCLIBC_HAS_IPV4__
+ struct sockaddr_in sa4;
+#endif
+#ifdef __UCLIBC_HAS_IPV6__
+ struct sockaddr_in6 sa6;
+#endif
+} sockaddr46_t;
+
+
+/* Protected by __resolv_lock */
+extern int __nameservers attribute_hidden;
+extern int __searchdomains attribute_hidden;
+extern sockaddr46_t *__nameserver attribute_hidden;
+extern char **__searchdomain attribute_hidden;
+/* Arbitrary */
+#define MAXLEN_searchdomain 128
+
+
/* function prototypes */
extern int __get_hosts_byname_r(const char * name, int type,
struct hostent * result_buf,
@@ -312,8 +324,8 @@ extern int __read_etc_hosts_r(FILE *fp, const char * name, int type,
char * buf, size_t buflen,
struct hostent ** result,
int * h_errnop) attribute_hidden;
-extern int __dns_lookup(const char * name, int type, int nscount,
- char ** nsip, unsigned char ** outpacket, struct resolv_answer * a) attribute_hidden;
+extern int __dns_lookup(const char * name, int type,
+ unsigned char ** outpacket, struct resolv_answer * a) attribute_hidden;
extern int __encode_dotted(const char * dotted, unsigned char * dest, int maxlen) attribute_hidden;
extern int __decode_dotted(const unsigned char * const message, int offset,
@@ -731,16 +743,10 @@ int __form_query(int id, const char *name, int type, unsigned char *packet,
#endif
#ifdef L_dnslookup
-__UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
-/* Just for the record, having to lock __dns_lookup() just for these two globals
- * is pretty lame. I think these two variables can probably be de-global-ized,
- * which should eliminate the need for doing locking here... Needs a closer
- * look anyways. */
-static int static_ns = 0;
-/* uint16: minimizing rw data size, even if code grows a tiny bit.
- * rw data costs more. */
-static uint16_t static_id = 1;
+/* Protected by __resolv_lock */
+static int last_ns_num = 0;
+static uint16_t last_id = 1;
/* On entry:
* a.buf(len) = auxiliary buffer for IP addresses after first one
@@ -759,15 +765,11 @@ static uint16_t static_id = 1;
* This is a malloced string. May be NULL because strdup failed.
*/
int attribute_hidden __dns_lookup(const char *name, int type,
- int nscount, char **nsip,
unsigned char **outpacket,
struct resolv_answer *a)
-//FIXME: nscount/nsip are *always* taken from __nameservers and __nameserver[]
-//globals. Locking is busted: __nameserver[] can be freed under us.
-//Fix it by eliminating these params and accessing __nameserver(s)
-//only under lock inside this routine.
{
int i, j, len, fd, pos, rc;
+ int name_len;
#ifdef USE_SELECT
struct timeval tv;
fd_set fds;
@@ -780,119 +782,127 @@ int attribute_hidden __dns_lookup(const char *name, int type,
bool first_answer = 1;
unsigned retries = 0;
unsigned char *packet = malloc(PACKETSZ);
- char *dns, *lookup = malloc(MAXDNAME);
- int variant = -1; /* search domain to append, -1 - none */
- int local_ns = -1, local_id = -1;
+ char *lookup;
+ int variant = -1; /* search domain to append, -1: none */
+ int local_ns_num = -1; /* Nth server to use */
+ int local_id = local_id; /* for compiler */
+ int sdomains;
bool ends_with_dot;
- union {
- struct sockaddr sa;
-#ifdef __UCLIBC_HAS_IPV4__
- struct sockaddr_in sa4;
-#endif
-#ifdef __UCLIBC_HAS_IPV6__
- struct sockaddr_in6 sa6;
-#endif
- } sa;
+ sockaddr46_t sa;
fd = -1;
-
- if (!packet || !lookup || !nscount || !name[0])
+ lookup = NULL;
+ name_len = strlen(name);
+ if ((unsigned)name_len >= MAXDNAME - MAXLEN_searchdomain - 2)
+ goto fail; /* paranoia */
+ lookup = malloc(name_len + 1/*for '.'*/ + MAXLEN_searchdomain + 1);
+ if (!packet || !lookup || !name[0])
goto fail;
-
+ ends_with_dot = (name[name_len - 1] == '.');
DPRINTF("Looking up type %d answer for '%s'\n", type, name);
- ends_with_dot = (name[strlen(name) - 1] == '.');
-
- /* Mess with globals while under lock */
- __UCLIBC_MUTEX_LOCK(mylock);
- local_ns = static_ns % nscount;
- local_id = static_id;
- __UCLIBC_MUTEX_UNLOCK(mylock);
-
while (retries < MAX_RETRIES) {
- if (fd != -1)
+ if (fd != -1) {
close(fd);
+ fd = -1;
+ }
- memset(packet, 0, PACKETSZ);
+ /* no strcpy! paranoia, user might change name[] under us */
+ memcpy(lookup, name, name_len);
+ lookup[name_len] = '\0';
+ /* Mess with globals while under lock */
+ /* NB: even data *pointed to* by globals may vanish
+ * outside the locks. We should assume any and all
+ * globals can completely change between locked
+ * code regions. OTOH, this is rare, so we don't need
+ * to handle it "nicely" (do not skip servers,
+ * search domains, etc), we only need to ensure
+ * we do not SEGV, use freed+overwritten data
+ * or do other Really Bad Things. */
+ __UCLIBC_MUTEX_LOCK(__resolv_lock);
+ __open_nameservers();
+ sdomains = __searchdomains;
+ if ((unsigned)variant < sdomains) {
+ /* lookup is name_len + 1 + MAXLEN_searchdomain + 1 long */
+ /* __searchdomain[] is not bigger than MAXLEN_searchdomain */
+ lookup[name_len] = '.';
+ strcpy(&lookup[name_len + 1], __searchdomain[variant]);
+ }
+ if (local_ns_num < 0) { /* first time */
+ local_id = last_id;
+//TODO: implement /etc/resolv.conf's "options rotate"
+// (a.k.a. RES_ROTATE bit in _res.options)
+// local_ns_num = 0;
+// if (_res.options & RES_ROTATE)
+ local_ns_num = last_ns_num;
+ if (local_ns_num >= __nameservers)
+ local_ns_num = 0;
+ }
+ /* __nameservers == 0 case: act as if we
+ * have one DNS server configured - on 127.0.0.1 */
+ {
+ int my_nameservers = __nameservers;
+ if (my_nameservers == 0)
+ my_nameservers++;
+ if (local_ns_num >= my_nameservers) {
+ local_ns_num = 0;
+ retries++;
+ /* break if retries >= MAX_RETRIES - *after unlock*! */
+ }
+ }
+ local_id++;
+ local_id &= 0xffff;
+ /* write new values back while still under lock */
+ last_id = local_id;
+ last_ns_num = local_ns_num;
+ if (__nameservers != 0) {
+ /* struct copy */
+ /* can't just take a pointer, __nameserver[]
+ * is not safe to use outside of locks */
+ sa = __nameserver[local_ns_num];
+ } else {
+ memset(&sa, 0, sizeof(sa));
+#ifdef __UCLIBC_HAS_IPV4__
+ sa.sa4.sin_family = AF_INET;
+ /*sa.sa4.sin_addr = INADDR_ANY; - done by memset */
+ sa.sa4.sin_port = htons(NAMESERVER_PORT);
+#else
+ sa.sa6.sin_family = AF_INET6;
+ sa.sa6.sin6_port = htons(NAMESERVER_PORT);
+#endif
+ }
+ __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
+ if (retries >= MAX_RETRIES)
+ break;
+
+ memset(packet, 0, PACKETSZ);
memset(&h, 0, sizeof(h));
- ++local_id;
- local_id &= 0xffff;
+ /* encode header */
h.id = local_id;
h.qdcount = 1;
h.rd = 1;
-
DPRINTF("encoding header\n", h.rd);
-
i = __encode_header(&h, packet, PACKETSZ);
if (i < 0)
goto fail;
- strncpy(lookup, name, MAXDNAME);
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- /* nsip is really __nameserver[] which is a global that
- needs to hold __resolv_lock before access!! */
- dns = nsip[local_ns];
-/* TODO: all future accesses to 'dns' were guarded by __resolv_lock too.
- * Why? We already fetched nsip[local_ns] here,
- * future changes to nsip[] by other threads cannot affect us.
- * We can use 'dns' without locking. If I'm wrong,
- * please explain in comments why locking is needed.
- * One thing that worries me is - what if __close_nameservers() free()s
- * dns under us? __resolv_lock'ing around accesses to dns won't help either,
- * as free() might occur between accesses!
- */
- if (variant >= 0) {
- if (variant < __searchdomains) {
- strncat(lookup, ".", MAXDNAME);
- strncat(lookup, __searchdomain[variant], MAXDNAME);
- }
- }
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
-
+ /* encode question */
DPRINTF("lookup name: %s\n", lookup);
q.dotted = lookup;
q.qtype = type;
q.qclass = C_IN; /* CLASS_IN */
-
j = __encode_question(&q, packet+i, PACKETSZ-i);
if (j < 0)
goto fail;
-
len = i + j;
- DPRINTF("On try %d, sending query to port %d of machine %s\n",
- retries+1, NAMESERVER_PORT, dns);
-
- sa.sa.sa_family = AF_INET;
-#ifdef __UCLIBC_HAS_IPV6__
- //__UCLIBC_MUTEX_LOCK(__resolv_lock);
- ///* 'dns' is really __nameserver[] which is a global that
- // needs to hold __resolv_lock before access!! */
- if (inet_pton(AF_INET6, dns, &sa.sa6.sin6_addr) > 0)
- sa.sa.sa_family = AF_INET6;
- //__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
-#endif
- /* Connect to the UDP socket so that asyncronous errors are returned */
-#ifdef __UCLIBC_HAS_IPV6__
- if (sa.sa.sa_family == AF_INET6) {
- sa.sa6.sin6_port = htons(NAMESERVER_PORT);
- /* sa6.sin6_addr is already here */
- } else
-#endif
-#ifdef __UCLIBC_HAS_IPV4__
- {
- sa.sa4.sin_port = htons(NAMESERVER_PORT);
- //__UCLIBC_MUTEX_LOCK(__resolv_lock);
- ///* 'dns' is really __nameserver[] which is a global that
- // needs to hold __resolv_lock before access!! */
- sa.sa4.sin_addr.s_addr = inet_addr(dns);
- //__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
- }
-#endif
+ /* send packet */
+ DPRINTF("On try %d, sending query to port %d\n",
+ retries+1, NAMESERVER_PORT);
fd = socket(sa.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP);
- if (fd < 0) {
+ if (fd < 0) { /* paranoia */
retries++;
continue;
}
@@ -900,16 +910,13 @@ int attribute_hidden __dns_lookup(const char *name, int type,
if (rc < 0) {
if (errno == ENETUNREACH) {
/* routing error, presume not transient */
- goto tryall;
+ goto try_next_server;
}
/* retry */
retries++;
continue;
}
-
- DPRINTF("Transmitting packet of length %d, id=%d, qr=%d\n",
- len, h.id, h.qr);
-
+ DPRINTF("Xmit packet len:%d id:%d qr:%d\n", len, h.id, h.qr);
send(fd, packet, len, 0);
#ifdef USE_SELECT
@@ -919,34 +926,35 @@ int attribute_hidden __dns_lookup(const char *name, int type,
tv.tv_usec = 0;
if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0) {
DPRINTF("Timeout\n");
- /* timed out, so retry send and receive,
- * to next nameserver on queue */
- goto tryall;
+ /* timed out, so retry send and receive
+ * to next nameserver */
+ goto try_next_server;
}
#else
fds.fd = fd;
fds.events = POLLIN;
if (poll(&fds, 1, REPLY_TIMEOUT * 1000) <= 0) {
DPRINTF("Timeout\n");
- /* timed out, so retry send and receive,
- * to next nameserver on queue */
- goto tryall;
+ /* timed out, so retry send and receive
+ * to next nameserver */
+ goto try_next_server;
}
#endif
-
+//TODO: MSG_DONTWAIT?
len = recv(fd, packet, PACKETSZ, 0);
if (len < HFIXEDSZ) {
/* too short! */
- goto again;
+//TODO: why next sdomain? it's just a bogus packet from somewhere,
+// we can as well wait more...
+ goto try_next_sdomain;
}
__decode_header(packet, &h);
-
DPRINTF("id = %d, qr = %d\n", h.id, h.qr);
-
if ((h.id != local_id) || (!h.qr)) {
/* unsolicited */
- goto again;
+//TODO: why next sdomain?...
+ goto try_next_sdomain;
}
DPRINTF("Got response (i think)!\n");
@@ -960,12 +968,9 @@ int attribute_hidden __dns_lookup(const char *name, int type,
// which is, eh, an error. :) We were incurring long delays because of this.
/* if possible, try next search domain */
if (!ends_with_dot) {
- int sdomains;
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- sdomains = __searchdomains;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
+ DPRINTF("variant:%d sdomains:%d\n", variant, sdomains);
if (variant < sdomains - 1) {
- /* next search */
+ /* next search domain */
variant++;
continue;
}
@@ -975,21 +980,20 @@ int attribute_hidden __dns_lookup(const char *name, int type,
goto fail1;
}
if (h.rcode != 0) /* error */
- goto again;
+ goto try_next_sdomain;
/* code below won't work correctly with h.ancount == 0, so... */
- if (h.ancount < 1) {
+ if (h.ancount <= 0) {
h_errno = NO_DATA; /* is this correct code? */
goto fail1;
}
pos = HFIXEDSZ;
-
for (j = 0; j < h.qdcount; j++) {
DPRINTF("Skipping question %d at %d\n", j, pos);
/* returns -1 only if packet == NULL (can't happen) */
i = __length_question(packet, pos);
//if (i < 0)
- // goto again;
+ // goto try_next_sdomain;
DPRINTF("Length of question %d is %d\n", j, i);
pos += i;
}
@@ -1001,10 +1005,10 @@ int attribute_hidden __dns_lookup(const char *name, int type,
if (i < 0) {
DPRINTF("failed decode %d\n", i);
/* if the message was truncated and we have
- decoded some answers, pretend it's OK */
+ * decoded some answers, pretend it's OK */
if (j && h.tc)
break;
- goto again;
+ goto try_next_sdomain;
}
pos += i;
@@ -1032,7 +1036,7 @@ int attribute_hidden __dns_lookup(const char *name, int type,
free(a->dotted);
DPRINTF("Answer address len(%u) differs from original(%u)\n",
ma.rdlength, a->rdlength);
- goto again;
+ goto try_next_sdomain;
}
memcpy(a->buf + (a->add_count * ma.rdlength), ma.rdata, ma.rdlength);
++a->add_count;
@@ -1041,52 +1045,29 @@ int attribute_hidden __dns_lookup(const char *name, int type,
DPRINTF("Answer name = |%s|\n", a->dotted);
DPRINTF("Answer type = |%d|\n", a->atype);
-
- close(fd);
-
+ if (fd != -1)
+ close(fd);
if (outpacket)
*outpacket = packet;
else
free(packet);
free(lookup);
+ return len; /* success! */
- /* Mess with globals while under lock */
- __UCLIBC_MUTEX_LOCK(mylock);
- static_ns = local_ns;
- static_id = local_id;
- __UCLIBC_MUTEX_UNLOCK(mylock);
-
- return len; /* success! */
-
- tryall:
- /* if there are other nameservers, give them a go,
- otherwise return with error */
- variant = -1;
- local_ns = (local_ns + 1) % nscount;
- if (local_ns == 0)
- retries++;
-
- continue;
-
- again:
- /* if there are searchdomains, try them or fallback as passed */
+ try_next_sdomain:
+ /* if there are searchdomains, try them */
if (!ends_with_dot) {
- int sdomains;
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- sdomains = __searchdomains;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
if (variant < sdomains - 1) {
/* next search */
variant++;
continue;
}
}
- /* next server, first search */
- local_ns = (local_ns + 1) % nscount;
- if (local_ns == 0)
- retries++;
+ try_next_server:
+ /* if there are other nameservers, try them */
+ local_ns_num++;
variant = -1;
- }
+ } /* while (retries < MAX_RETRIES) */
fail:
h_errno = NETDB_INTERNAL;
@@ -1095,116 +1076,161 @@ int attribute_hidden __dns_lookup(const char *name, int type,
close(fd);
free(lookup);
free(packet);
- /* Mess with globals while under lock */
- if (local_ns != -1) {
- __UCLIBC_MUTEX_LOCK(mylock);
- static_ns = local_ns;
- static_id = local_id;
- __UCLIBC_MUTEX_UNLOCK(mylock);
- }
return -1;
}
#endif
#ifdef L_opennameservers
-/* We use __resolv_lock to guard access to the
- * '__nameservers' and __searchdomains globals */
+//TODO: need not be recursive
+__UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
+
+/* Protected by __resolv_lock */
int __nameservers;
-char * __nameserver[MAX_SERVERS];
int __searchdomains;
-char * __searchdomain[MAX_SEARCH];
+sockaddr46_t *__nameserver;
+char **__searchdomain;
-__UCLIBC_MUTEX_INIT(__resolv_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);
-
-/*
- * we currently read formats not quite the same as that on normal
- * unix systems, we can have a list of nameservers after the keyword.
- */
+/* Helpers. Both stop on EOL, if it's '\n', it is converted to NUL first */
+static char *skip_nospace(char *p)
+{
+ while (*p != '\0' && !isspace(*p)) {
+ if (*p == '\n') {
+ *p = '\0';
+ break;
+ }
+ p++;
+ }
+ return p;
+}
+static char *skip_and_NUL_space(char *p)
+{
+ /* NB: '\n' is not isspace! */
+ while (1) {
+ char c = *p;
+ if (c == '\0' || !isspace(c))
+ break;
+ *p = '\0';
+ if (c == '\n' || c == '#')
+ break;
+ p++;
+ }
+ return p;
+}
+/* Must be called under __resolv_lock. */
void attribute_hidden __open_nameservers(void)
{
+ char szBuffer[MAXLEN_searchdomain];
FILE *fp;
int i;
-#define RESOLV_ARGS 5
- char szBuffer[128], *p, *argv[RESOLV_ARGS];
- int argc;
+ sockaddr46_t sa;
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
if (__nameservers > 0)
- goto DONE;
+ return;
- if ((fp = fopen("/etc/resolv.conf", "r")) ||
- (fp = fopen("/etc/config/resolv.conf", "r")))
- {
- while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
- for (p = szBuffer; *p && isspace(*p); p++)
- /* skip white space */;
- if (*p == '\0' || *p == '\n' || *p == '#') /* skip comments etc */
- continue;
- argc = 0;
- while (*p && argc < RESOLV_ARGS) {
- argv[argc++] = p;
- while (*p && !isspace(*p) && *p != '\n')
- p++;
- while (*p && (isspace(*p) || *p == '\n')) /* remove spaces */
- *p++ = '\0';
- }
+ fp = fopen("/etc/resolv.conf", "r");
+ if (!fp) {
+ fp = fopen("/etc/config/resolv.conf", "r");
+ if (!fp) {
+ DPRINTF("failed to open %s\n", "resolv.conf");
+ h_errno = NO_RECOVERY;
+ return;
+ }
+ }
- if (strcmp(argv[0], "nameserver") == 0) {
- for (i = 1; i < argc && __nameservers < MAX_SERVERS; i++) {
-// TODO: what if strdup fails?
- __nameserver[__nameservers++] = strdup(argv[i]);
- DPRINTF("adding nameserver %s\n", argv[i]);
- }
+ while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) {
+ void *ptr;
+ char *keyword, *p;
+
+ keyword = p = skip_and_NUL_space(szBuffer);
+ /* skip keyword */
+ p = skip_nospace(p);
+ /* find next word */
+ p = skip_and_NUL_space(p);
+
+ if (strcmp(keyword, "nameserver") == 0) {
+ /* terminate IP addr */
+ *skip_nospace(p) = '\0';
+ memset(&sa, 0, sizeof(sa));
+ if (0) /* nothing */;
+#ifdef __UCLIBC_HAS_IPV6__
+ else if (inet_pton(AF_INET6, p, &sa.sa6.sin6_addr) > 0) {
+ sa.sa6.sin6_family = AF_INET6;
+ sa.sa6.sin6_port = htons(NAMESERVER_PORT);
}
-
- /* domain and search are mutually exclusive, the last one wins */
- if (strcmp(argv[0],"domain") == 0 || strcmp(argv[0],"search") == 0) {
- while (__searchdomains > 0) {
- free(__searchdomain[--__searchdomains]);
- __searchdomain[__searchdomains] = NULL;
- }
- for (i = 1; i < argc && __searchdomains < MAX_SEARCH; i++) {
-// TODO: what if strdup fails?
- __searchdomain[__searchdomains++] = strdup(argv[i]);
- DPRINTF("adding search %s\n", argv[i]);
- }
+#endif
+#ifdef __UCLIBC_HAS_IPV4__
+ else if (inet_pton(AF_INET, p, &sa.sa4.sin_addr) > 0) {
+ sa.sa4.sin_family = AF_INET;
+ sa.sa4.sin_port = htons(NAMESERVER_PORT);
}
+#endif
+ else
+ continue; /* garbage on this line */
+ ptr = realloc(__nameserver, (__nameservers + 1) * sizeof(__nameserver[0]));
+ if (!ptr)
+ continue;
+ __nameserver = ptr;
+ __nameserver[__nameservers++] = sa; /* struct copy */
+ continue;
}
- fclose(fp);
- DPRINTF("nameservers = %d\n", __nameservers);
- goto DONE;
+ if (strcmp(keyword, "domain") == 0 || strcmp(keyword, "search") == 0) {
+ char *p1;
+ 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;
+ 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;
+ continue;
+ }
+ /* if (strcmp(keyword, "sortlist") == 0)... */
+ /* if (strcmp(keyword, "options") == 0)... */
}
- DPRINTF("failed to open %s\n", "resolv.conf");
- h_errno = NO_RECOVERY;
-
- /* rv = -1; */
-
- DONE:
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
- /* return rv; */
+ fclose(fp);
+ DPRINTF("nameservers = %d\n", __nameservers);
}
#endif
#ifdef L_closenameservers
+/* Must be called under __resolv_lock. */
void attribute_hidden __close_nameservers(void)
{
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- while (__nameservers > 0) {
- free(__nameserver[--__nameservers]);
- __nameserver[__nameservers] = NULL;
- }
- while (__searchdomains > 0) {
+ free(__nameserver);
+ __nameserver = NULL;
+ __nameservers = 0;
+ while (__searchdomains)
free(__searchdomain[--__searchdomains]);
- __searchdomain[__searchdomains] = NULL;
- }
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
+ free(__searchdomain);
+ __searchdomain = NULL;
+ /*__searchdomains = 0; - already is */
}
#endif
+
#ifdef L_gethostbyname
struct hostent *gethostbyname(const char *name)
@@ -1222,6 +1248,7 @@ struct hostent *gethostbyname(const char *name)
libc_hidden_def(gethostbyname)
#endif
+
#ifdef L_gethostbyname2
struct hostent *gethostbyname2(const char *name, int family)
@@ -1240,21 +1267,24 @@ struct hostent *gethostbyname2(const char *name, int family)
return hp;
#endif /* __UCLIBC_HAS_IPV6__ */
}
-#endif
+#endif
#ifdef L_res_init
+
/* We use __resolv_lock to guard access to global '_res' */
struct __res_state _res;
int res_init(void)
{
+ int i, n;
struct __res_state *rp = &(_res);
- __UCLIBC_MUTEX_LOCK(__resolv_lock); /* must be a recursive lock! */
+ __UCLIBC_MUTEX_LOCK(__resolv_lock);
__close_nameservers();
__open_nameservers();
+
memset(rp, 0, sizeof(*rp));
rp->options = RES_INIT;
#ifdef __UCLIBC_HAS_COMPAT_RES_STATE__
@@ -1276,26 +1306,28 @@ int res_init(void)
rp->_vcsock = -1;
#endif
- if (__searchdomains) {
- int i;
- for (i = 0; i < __searchdomains; i++)
- rp->dnsrch[i] = __searchdomain[i];
- }
-
- if (__nameservers) {
- int i;
- struct in_addr a;
- for (i = 0; i < __nameservers; i++) {
- if (inet_aton(__nameserver[i], &a)) {
- rp->nsaddr_list[i].sin_addr = a;
- rp->nsaddr_list[i].sin_family = AF_INET;
- rp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT);
- }
- }
- rp->nscount = __nameservers;
+#undef ARRAY_SIZE
+#define ARRAY_SIZE(v) (sizeof(v) / sizeof((v)[0]))
+ n = __searchdomains;
+ if (n > ARRAY_SIZE(rp->dnsrch))
+ n = ARRAY_SIZE(rp->dnsrch);
+ for (i = 0; i < n; i++)
+ rp->dnsrch[i] = __searchdomain[i];
+ i = 0;
+ 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:
+ i++;
}
+ if (n)
+ rp->nscount = n;
+ /* else stays 1 */
+#undef ARRAY_SIZE
__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
-
return 0;
}
libc_hidden_def(res_init)
@@ -1303,8 +1335,10 @@ libc_hidden_def(res_init)
#ifdef __UCLIBC_HAS_BSD_RES_CLOSE__
void res_close(void)
{
+ __UCLIBC_MUTEX_LOCK(__resolv_lock);
__close_nameservers();
memset(&_res, 0, sizeof(_res));
+ __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
}
#endif
@@ -1323,8 +1357,6 @@ int res_query(const char *dname, int class, int type,
int i;
unsigned char * packet = NULL;
struct resolv_answer a;
- int __nameserversXX;
- char ** __nameserverXX;
if (!dname || class != 1 /* CLASS_IN */) {
h_errno = NO_RECOVERY;
@@ -1332,13 +1364,7 @@ int res_query(const char *dname, int class, int type,
}
memset(&a, '\0', sizeof(a));
- __open_nameservers();
-
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- __nameserversXX = __nameservers;
- __nameserverXX = __nameserver;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
- i = __dns_lookup(dname, type, __nameserversXX, __nameserverXX, &packet, &a);
+ i = __dns_lookup(dname, type, &packet, &a);
if (i < 0) {
h_errno = TRY_AGAIN;
@@ -1381,6 +1407,8 @@ int res_search(const char *name, int class, int type, u_char *answer,
__UCLIBC_MUTEX_LOCK(__resolv_lock);
_res_options = _res.options;
__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
+//FIXME: locking is bogus
+//FIXME: our res_init never fails
if ((!name || !answer) || ((_res_options & RES_INIT) == 0 && res_init() == -1)) {
h_errno = NETDB_INTERNAL;
return -1;
@@ -1528,6 +1556,8 @@ int res_querydomain(const char *name, const char *domain, int class, int type,
__UCLIBC_MUTEX_LOCK(__resolv_lock);
_res_options = _res.options;
__UCLIBC_MUTEX_UNLOCK(__resolv_lock);
+//FIXME: locking is bogus
+//FIXME: our res_init never fails
if ((!name || !answer) || ((_res_options & RES_INIT) == 0 && res_init() == -1)) {
h_errno = NETDB_INTERNAL;
return -1;
@@ -2214,20 +2244,13 @@ int gethostbyname_r(const char * name,
}
/* talk to DNS servers */
- __open_nameservers();
{
- int __nameserversXX;
- char ** __nameserverXX;
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- __nameserversXX = __nameservers;
- __nameserverXX = __nameserver;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
a.buf = buf;
/* take into account that at least one address will be there,
* we'll need space of one in_addr + two addr_list[] elems */
a.buflen = buflen - ((sizeof(addr_list[0]) * 2 + sizeof(struct in_addr)));
a.add_count = 0;
- i = __dns_lookup(name, T_A, __nameserversXX, __nameserverXX, &packet, &a);
+ i = __dns_lookup(name, T_A, &packet, &a);
if (i < 0) {
*h_errnop = HOST_NOT_FOUND;
DPRINTF("__dns_lookup returned < 0\n");
@@ -2319,8 +2342,6 @@ int gethostbyname2_r(const char *name, int family,
struct resolv_answer a;
int i;
int nest = 0;
- int __nameserversXX;
- char ** __nameserverXX;
int wrong_af = 0;
if (family == AF_INET)
@@ -2329,7 +2350,6 @@ int gethostbyname2_r(const char *name, int family,
if (family != AF_INET6)
return EINVAL;
- __open_nameservers();
*result = NULL;
if (!name)
return EINVAL;
@@ -2405,12 +2425,7 @@ int gethostbyname2_r(const char *name, int family,
memset(&a, '\0', sizeof(a));
for (;;) {
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- __nameserversXX = __nameservers;
- __nameserverXX = __nameserver;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
-
- i = __dns_lookup(buf, T_AAAA, __nameserversXX, __nameserverXX, &packet, &a);
+ i = __dns_lookup(buf, T_AAAA, &packet, &a);
if (i < 0) {
*h_errnop = HOST_NOT_FOUND;
@@ -2478,8 +2493,6 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type,
struct resolv_answer a;
int i;
int nest = 0;
- int __nameserversXX;
- char ** __nameserverXX;
*result = NULL;
if (!addr)
@@ -2514,8 +2527,6 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type,
return i;
}
- __open_nameservers();
-
#ifdef __UCLIBC_HAS_IPV6__
qp = buf;
plen = buflen;
@@ -2592,11 +2603,7 @@ int gethostbyaddr_r(const void *addr, socklen_t len, int type,
alias[1] = 0;
for (;;) {
- __UCLIBC_MUTEX_LOCK(__resolv_lock);
- __nameserversXX = __nameservers;
- __nameserverXX = __nameserver;
- __UCLIBC_MUTEX_UNLOCK(__resolv_lock);
- i = __dns_lookup(buf, T_PTR, __nameserversXX, __nameserverXX, &packet, &a);
+ i = __dns_lookup(buf, T_PTR, &packet, &a);
if (i < 0) {
*h_errnop = HOST_NOT_FOUND;