github.com/afumu/libc@v0.0.6/musl/src/network/res_msend.c (about) 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 17 static void cleanup(void *p) 18 { 19 __syscall(SYS_close, (intptr_t)p); 20 } 21 22 static 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 30 int __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) { 80 if (fd >= 0) close(fd); 81 pthread_setcancelstate(cs, 0); 82 return -1; 83 } 84 85 /* Past this point, there are no errors. Each individual query will 86 * yield either no reply (indicated by zero length) or an answer 87 * packet which is up to the caller to interpret. */ 88 89 pthread_cleanup_push(cleanup, (void *)(intptr_t)fd); 90 pthread_setcancelstate(cs, 0); 91 92 /* Convert any IPv4 addresses in a mixed environment to v4-mapped */ 93 if (family == AF_INET6) { 94 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0); 95 for (i=0; i<nns; i++) { 96 if (ns[i].sin.sin_family != AF_INET) continue; 97 memcpy(ns[i].sin6.sin6_addr.s6_addr+12, 98 &ns[i].sin.sin_addr, 4); 99 memcpy(ns[i].sin6.sin6_addr.s6_addr, 100 "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); 101 ns[i].sin6.sin6_family = AF_INET6; 102 ns[i].sin6.sin6_flowinfo = 0; 103 ns[i].sin6.sin6_scope_id = 0; 104 } 105 } 106 107 memset(alens, 0, sizeof *alens * nqueries); 108 109 pfd.fd = fd; 110 pfd.events = POLLIN; 111 retry_interval = timeout / attempts; 112 next = 0; 113 t0 = t2 = mtime(); 114 t1 = t2 - retry_interval; 115 116 for (; t2-t0 < timeout; t2=mtime()) { 117 if (t2-t1 >= retry_interval) { 118 /* Query all configured namservers in parallel */ 119 for (i=0; i<nqueries; i++) 120 if (!alens[i]) 121 for (j=0; j<nns; j++) 122 sendto(fd, queries[i], 123 qlens[i], MSG_NOSIGNAL, 124 (void *)&ns[j], sl); 125 t1 = t2; 126 servfail_retry = 2 * nqueries; 127 } 128 129 /* Wait for a response, or until time to retry */ 130 if (poll(&pfd, 1, t1+retry_interval-t2) <= 0) continue; 131 132 while ((rlen = recvfrom(fd, answers[next], asize, 0, 133 (void *)&sa, (socklen_t[1]){sl})) >= 0) { 134 135 /* Ignore non-identifiable packets */ 136 if (rlen < 4) continue; 137 138 /* Ignore replies from addresses we didn't send to */ 139 for (j=0; j<nns && memcmp(ns+j, &sa, sl); j++); 140 if (j==nns) continue; 141 142 /* Find which query this answer goes with, if any */ 143 for (i=next; i<nqueries && ( 144 answers[next][0] != queries[i][0] || 145 answers[next][1] != queries[i][1] ); i++); 146 if (i==nqueries) continue; 147 if (alens[i]) continue; 148 149 /* Only accept positive or negative responses; 150 * retry immediately on server failure, and ignore 151 * all other codes such as refusal. */ 152 switch (answers[next][3] & 15) { 153 case 0: 154 case 3: 155 break; 156 case 2: 157 if (servfail_retry && servfail_retry--) 158 sendto(fd, queries[i], 159 qlens[i], MSG_NOSIGNAL, 160 (void *)&ns[j], sl); 161 default: 162 continue; 163 } 164 165 /* Store answer in the right slot, or update next 166 * available temp slot if it's already in place. */ 167 alens[i] = rlen; 168 if (i == next) 169 for (; next<nqueries && alens[next]; next++); 170 else 171 memcpy(answers[i], answers[next], rlen); 172 173 if (next == nqueries) goto out; 174 } 175 } 176 out: 177 pthread_cleanup_pop(1); 178 179 return 0; 180 } 181 182 int __res_msend(int nqueries, const unsigned char *const *queries, 183 const int *qlens, unsigned char *const *answers, int *alens, int asize) 184 { 185 struct resolvconf conf; 186 if (__get_resolv_conf(&conf, 0, 0) < 0) return -1; 187 return __res_msend_rc(nqueries, queries, qlens, answers, alens, asize, &conf); 188 }