1#include <sys/socket.h> 2#include <netinet/in.h> 3#include <netdb.h> 4#include <arpa/inet.h> 5#include <stdint.h> 6#include <string.h> 7#include <poll.h> 8#include <time.h> 9#include <ctype.h> 10#include <unistd.h> 11#include <errno.h> 12#include <pthread.h> 13#include "stdio_impl.h" 14#include "syscall.h" 15#include "lookup.h" 16 17static void cleanup(void *p) 18{ 19 __syscall(SYS_close, (intptr_t)p); 20} 21 22static unsigned long mtime() 23{ 24 struct timespec ts; 25 clock_gettime(CLOCK_REALTIME, &ts); 26 return (unsigned long)ts.tv_sec * 1000 27 + ts.tv_nsec / 1000000; 28} 29 30int __res_msend_rc(int nqueries, const unsigned char *const *queries, 31 const int *qlens, unsigned char *const *answers, int *alens, int asize, 32 const struct resolvconf *conf) 33{ 34 int fd; 35 int timeout, attempts, retry_interval, servfail_retry; 36 union { 37 struct sockaddr_in sin; 38 struct sockaddr_in6 sin6; 39 } sa = {0}, ns[MAXNS] = {{0}}; 40 socklen_t sl = sizeof sa.sin; 41 int nns = 0; 42 int family = AF_INET; 43 int rlen; 44 int next; 45 int i, j; 46 int cs; 47 struct pollfd pfd; 48 unsigned long t0, t1, t2; 49 50 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); 51 52 timeout = 1000*conf->timeout; 53 attempts = conf->attempts; 54 55 for (nns=0; nns<conf->nns; nns++) { 56 const struct address *iplit = &conf->ns[nns]; 57 if (iplit->family == AF_INET) { 58 memcpy(&ns[nns].sin.sin_addr, iplit->addr, 4); 59 ns[nns].sin.sin_port = htons(53); 60 ns[nns].sin.sin_family = AF_INET; 61 } else { 62 sl = sizeof sa.sin6; 63 memcpy(&ns[nns].sin6.sin6_addr, iplit->addr, 16); 64 ns[nns].sin6.sin6_port = htons(53); 65 ns[nns].sin6.sin6_scope_id = iplit->scopeid; 66 ns[nns].sin6.sin6_family = family = AF_INET6; 67 } 68 } 69 70 /* Get local address and open/bind a socket */ 71 sa.sin.sin_family = family; 72 fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 73 74 /* Handle case where system lacks IPv6 support */ 75 if (fd < 0 && family == AF_INET6 && errno == EAFNOSUPPORT) { 76 fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0); 77 family = AF_INET; 78 } 79 if (fd < 0 || bind(fd, (void *)&sa, sl) < 0) return -1; 80 81 /* Past this point, there are no errors. Each individual query will 82 * yield either no reply (indicated by zero length) or an answer 83 * packet which is up to the caller to interpret. */ 84 85 pthread_cleanup_push(cleanup, (void *)(intptr_t)fd); 86 pthread_setcancelstate(cs, 0); 87 88 /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ 89 if (family == AF_INET6) { 90 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0); 91 for (i=0; i<nns; i++) { 92 if (ns[i].sin.sin_family != AF_INET) continue; 93 memcpy(ns[i].sin6.sin6_addr.s6_addr+12, 94 &ns[i].sin.sin_addr, 4); 95 memcpy(ns[i].sin6.sin6_addr.s6_addr, 96 "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); 97 ns[i].sin6.sin6_family = AF_INET6; 98 ns[i].sin6.sin6_flowinfo = 0; 99 ns[i].sin6.sin6_scope_id = 0; 100 } 101 } 102 103 memset(alens, 0, sizeof *alens * nqueries); 104 105 pfd.fd = fd; 106 pfd.events = POLLIN; 107 retry_interval = timeout / attempts; 108 next = 0; 109 t0 = t2 = mtime(); 110 t1 = t2 - retry_interval; 111 112 for (; t2-t0 < timeout; t2=mtime()) { 113 if (t2-t1 >= retry_interval) { 114 /* Query all configured namservers in parallel */ 115 for (i=0; i<nqueries; i++) 116 if (!alens[i]) 117 for (j=0; j<nns; j++) 118 sendto(fd, queries[i], 119 qlens[i], MSG_NOSIGNAL, 120 (void *)&ns[j], sl); 121 t1 = t2; 122 servfail_retry = 2 * nqueries; 123 } 124 125 /* Wait for a response, or until time to retry */ 126 if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) continue; 127 128 while ((rlen = recvfrom(fd, answers[next], asize, 0, 129 (void *)&sa, (socklen_t[1]){sl})) >= 0) { 130 131 /* Ignore non-identifiable packets */ 132 if (rlen < 4) continue; 133 134 /* Ignore replies from addresses we didn't send to */ 135 for (j=0; j<nns && memcmp(ns+j, &sa, sl); j++); 136 if (j==nns) continue; 137 138 /* Find which query this answer goes with, if any */ 139 for (i=next; i<nqueries && ( 140 answers[next][0] != queries[i][0] || 141 answers[next][1] != queries[i][1] ); i++); 142 if (i==nqueries) continue; 143 if (alens[i]) continue; 144 145 /* Only accept positive or negative responses; 146 * retry immediately on server failure, and ignore 147 * all other codes such as refusal. */ 148 switch (answers[next][3] & 15) { 149 case 0: 150 case 3: 151 break; 152 case 2: 153 if (servfail_retry && servfail_retry--) 154 sendto(fd, queries[i], 155 qlens[i], MSG_NOSIGNAL, 156 (void *)&ns[j], sl); 157 default: 158 continue; 159 } 160 161 /* Store answer in the right slot, or update next 162 * available temp slot if it's already in place. */ 163 alens[i] = rlen; 164 if (i == next) 165 for (; next<nqueries && alens[next]; next++); 166 else 167 memcpy(answers[i], answers[next], rlen); 168 169 if (next == nqueries) goto out; 170 } 171 } 172out: 173 pthread_cleanup_pop(1); 174 175 return 0; 176} 177 178int __res_msend(int nqueries, const unsigned char *const *queries, 179 const int *qlens, unsigned char *const *answers, int *alens, int asize) 180{ 181 struct resolvconf conf; 182 if (__get_resolv_conf(&conf, 0, 0) < 0) return -1; 183 return __res_msend_rc(nqueries, queries, qlens, answers, alens, asize, &conf); 184} 185