int __dns_doqueries(unsigned char *dest, const char *name, int *rr, int rrcnt) { time_t t0 = time(0); int fd; FILE *f, _f; unsigned char _buf[256]; char line[64], *s, *z; union { struct sockaddr_in sin; struct sockaddr_in6 sin6; } sa = {0}, ns[3] = {{0}}; socklen_t sl = sizeof sa.sin; int nns = 0; int family = AF_INET; unsigned char q[280] = "", *r = dest; int ql; int rlen; int got = 0, failed = 0; int errcode = EAI_AGAIN; int i, j; struct timespec ts; struct pollfd pfd; int id; int cs; pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); /* Construct query template - RR and ID will be filled later */ if (strlen(name)-1 >= 254U) return EAI_NONAME; q[2] = q[5] = 1; strcpy((char *)q+13, name); for (i=13; q[i]; i=j+1) { for (j=i; q[j] && q[j] != '.'; j++); if (j-i-1u > 62u) return EAI_NONAME; q[i-1] = j-i; } q[i+3] = 1; ql = i+4; /* Make a reasonably unpredictable id */ clock_gettime(CLOCK_REALTIME, &ts); id = ts.tv_nsec + ts.tv_nsec/65536UL & 0xffff; /* Get nameservers from resolv.conf, fallback to localhost */ f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); if (f) for (nns=0; nns<3 && fgets(line, sizeof line, f); ) { if (strncmp(line, "nameserver", 10) || !isspace(line[10])) continue; for (s=line+11; isspace(*s); s++); for (z=s; *z && !isspace(*z); z++); *z=0; if (__ipparse(ns+nns, AF_UNSPEC, s) < 0) continue; ns[nns].sin.sin_port = htons(53); if (ns[nns++].sin.sin_family == AF_INET6) { family = AF_INET6; sl = sizeof sa.sin6; } } if (f) __fclose_ca(f); if (!nns) { ns[0].sin.sin_family = family = AF_INET; ns[0].sin.sin_port = htons(53); ns[0].sin.sin_addr.s_addr = htonl(0x7f000001); nns=1; sl = sizeof sa.sin; } /* Get local address and open/bind a socket */ sa.sin.sin_family = family; fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); /* Handle case where system lacks IPv6 support */ if (fd < 0 && errno == EAFNOSUPPORT) { if (family != AF_INET6) return EAI_SYSTEM; fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); family = AF_INET; } if (fd < 0) return EAI_SYSTEM; /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ if (family == AF_INET6) { setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint, struct addrinfo **res) { int flags = hint ? hint->ai_flags : 0; int family = hint ? hint->ai_family : AF_UNSPEC; int type = hint ? hint->ai_socktype : 0; int proto = hint ? hint->ai_protocol : 0; unsigned long port = 0; struct aibuf *buf; union sa sa = {{0}}; unsigned char reply[1024]; int i, j; char line[512]; FILE *f, _f; unsigned char _buf[1024]; char *z; int result; int cnt; if (host && strlen(host)>255) return EAI_NONAME; if (serv && strlen(serv)>32) return EAI_SERVICE; if (type && !proto) proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; if (!type && proto) type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; if (serv) { if (!*serv) return EAI_SERVICE; port = strtoul(serv, &z, 10); if (!*z && port > 65535) return EAI_SERVICE; if (!port) { size_t servlen = strlen(serv); char *end = line; if (flags & AI_NUMERICSERV) return EAI_SERVICE; f = __fopen_rb_ca("/etc/services", &_f, _buf, sizeof _buf); if (!f) return EAI_SERVICE; while (fgets(line, sizeof line, f)) { if (strncmp(line, serv, servlen) || !isspace(line[servlen])) continue; port = strtoul(line+servlen, &end, 10); if (strncmp(end, proto==IPPROTO_UDP ? "/udp" : "/tcp", 4)) continue; break; } __fclose_ca(f); if (feof(f)) return EAI_SERVICE; } port = htons(port); } if (!host) { if (family == AF_UNSPEC) family = AF_INET; buf = calloc(sizeof *buf, 1+EXTRA); if (!buf) return EAI_MEMORY; buf->ai.ai_protocol = proto; buf->ai.ai_socktype = type; buf->ai.ai_addr = (void *)&buf->sa; buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; buf->ai.ai_family = family; buf->sa.sin.sin_family = family; buf->sa.sin.sin_port = port; if (!(flags & AI_PASSIVE)) { if (family == AF_INET) { 0[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=127; 3[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=1; } else buf[0].sa.sin6.sin6_addr.s6_addr[15] = 1; } *res = &buf->ai; return 0; } if (!*host) return EAI_NONAME; /* Try as a numeric address */ if (__ipparse(&sa, family, host) >= 0) { buf = calloc(sizeof *buf, 1+EXTRA); if (!buf) return EAI_MEMORY; family = sa.sin.sin_family; buf->ai.ai_protocol = proto; buf->ai.ai_socktype = type; buf->ai.ai_addr = (void *)&buf->sa; buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; buf->ai.ai_family = family; buf->ai.ai_canonname = (char *)host; buf->sa = sa; buf->sa.sin.sin_port = port; *res = &buf->ai; return 0; } if (flags & AI_NUMERICHOST) return EAI_NONAME; f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); if (f) while (fgets(line, sizeof line, f)) { char *p; size_t l = strlen(host); if ((p=strchr(line, '#'))) *p++='\n', *p=0; for(p=line+1; (p=strstr(p, host)) && (!isspace(p[-1]) || !isspace(p[l])); p++); if (!p) continue; __fclose_ca(f); /* Isolate IP address to parse */ for (p=line; *p && !isspace(*p); p++); *p++ = 0; if (__ipparse(&sa, family, line) < 0) return EAI_NONAME; /* Allocate and fill result buffer */ buf = calloc(sizeof *buf, 1+EXTRA); if (!buf) return EAI_MEMORY; family = sa.sin.sin_family; buf->ai.ai_protocol = proto; buf->ai.ai_socktype = type; buf->ai.ai_addr = (void *)&buf->sa; buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; buf->ai.ai_family = family; buf->sa = sa; buf->sa.sin.sin_port = port; /* Extract first name as canonical name */ for (; *p && isspace(*p); p++); buf->ai.ai_canonname = (void *)(buf+1); snprintf(buf->ai.ai_canonname, 256, "%s", p); for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++); *p = 0; if (!is_valid(buf->ai.ai_canonname)) buf->ai.ai_canonname = 0; *res = &buf->ai; return 0; } if (f) __fclose_ca(f); #if 0 f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); if (f) while (fgets(line, sizeof line, f)) { if (!isspace(line[10]) || (strncmp(line, "search", 6) && strncmp(line, "domain", 6))) continue; } if (f) __fclose_ca(f); #endif /* Perform one or more DNS queries for host */ memset(reply, 0, sizeof reply); result = __dns_query(reply, host, family, 0); if (result < 0) return result; cnt = __dns_count_addrs(reply, result); if (cnt <= 0) return EAI_NONAME; buf = calloc(sizeof *buf, cnt+EXTRA); if (!buf) return EAI_MEMORY; i = 0; if (family != AF_INET6) { j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0); while (j--) buf[i++].sa.sin.sin_family = AF_INET; } if (family != AF_INET) { j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0); while (j--) buf[i++].sa.sin.sin_family = AF_INET6; } if (result>1) { j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0); while (j--) buf[i++].sa.sin.sin_family = AF_INET; j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0); while (j--) buf[i++].sa.sin.sin_family = AF_INET6; } if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) < 0) strcpy((void *)&buf[cnt], host); for (i=0; i<cnt; i++) { buf[i].ai.ai_protocol = proto; buf[i].ai.ai_socktype = type; buf[i].ai.ai_addr = (void *)&buf[i].sa; buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; buf[i].ai.ai_family = buf[i].sa.sin.sin_family; buf[i].sa.sin.sin_port = port; buf[i].ai.ai_next = &buf[i+1].ai; buf[i].ai.ai_canonname = (void *)&buf[cnt]; } buf[cnt-1].ai.ai_next = 0; *res = &buf->ai; return 0; }