• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/resolv.c

  1 /*
  2  * nslookup_lede - musl compatible replacement for busybox nslookup
  3  *
  4  * Copyright (C) 2017 Jo-Philipp Wich <jo@mein.io>
  5  *
  6  * Permission to use, copy, modify, and/or distribute this software for any
  7  * purpose with or without fee is hereby granted, provided that the above
  8  * copyright notice and this permission notice appear in all copies.
  9  *
 10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 17  */
 18 
 19 #include <stdio.h>
 20 #include <resolv.h>
 21 #include <string.h>
 22 #include <errno.h>
 23 #include <time.h>
 24 #include <poll.h>
 25 #include <unistd.h>
 26 #include <stdlib.h>
 27 #include <sys/socket.h>
 28 #include <arpa/inet.h>
 29 #include <net/if.h>
 30 #include <netdb.h>
 31 #include <fcntl.h>
 32 
 33 #include "ucode/module.h"
 34 
 35 #define for_each_item(arr, item) \
 36         for (uc_value_t *_idx = NULL, *item = (ucv_type(arr) == UC_ARRAY) ? ucv_array_get(arr, 0) : arr; \
 37              (uintptr_t)_idx < (ucv_type(arr) == UC_ARRAY ? ucv_array_length(arr) : (arr != NULL)); \
 38              _idx = (void *)((uintptr_t)_idx + 1), item = ucv_array_get(arr, (uintptr_t)_idx))
 39 
 40 #define err_return(code, ...) do { set_error(code, __VA_ARGS__); return NULL; } while(0)
 41 
 42 static struct {
 43         int code;
 44         char *msg;
 45 } last_error;
 46 
 47 __attribute__((format(printf, 2, 3))) static void
 48 set_error(int errcode, const char *fmt, ...) {
 49         va_list ap;
 50 
 51         free(last_error.msg);
 52 
 53         last_error.code = errcode;
 54         last_error.msg = NULL;
 55 
 56         if (fmt) {
 57                 va_start(ap, fmt);
 58                 xvasprintf(&last_error.msg, fmt, ap);
 59                 va_end(ap);
 60         }
 61 }
 62 
 63 typedef struct {
 64         socklen_t len;
 65         union {
 66                 struct sockaddr sa;
 67                 struct sockaddr_in sin;
 68                 struct sockaddr_in6 sin6;
 69         } u;
 70 } addr_t;
 71 
 72 typedef struct {
 73         const char *name;
 74         addr_t addr;
 75 } ns_t;
 76 
 77 typedef struct {
 78         char *name;
 79         size_t qlen, rlen;
 80         unsigned char query[512];
 81         int rcode;
 82 } query_t;
 83 
 84 typedef struct __attribute__((packed)) {
 85         uint8_t root_domain;
 86         uint16_t type;
 87         uint16_t edns_maxsize;
 88         uint8_t extended_rcode;
 89         uint8_t edns_version;
 90         uint16_t z;
 91         uint16_t data_length;
 92 } opt_rr_t;
 93 
 94 typedef struct {
 95         uint32_t qtypes;
 96         size_t n_ns;
 97         ns_t *ns;
 98         size_t n_queries;
 99         query_t *queries;
100         uint32_t retries;
101         uint32_t timeout;
102         uint16_t edns_maxsize;
103 }  resolve_ctx_t;
104 
105 
106 static struct {
107         int type;
108         const char *name;
109 } qtypes[] = {
110         { ns_t_soa,   "SOA"   },
111         { ns_t_ns,    "NS"    },
112         { ns_t_a,     "A"     },
113         { ns_t_aaaa,  "AAAA"  },
114         { ns_t_cname, "CNAME" },
115         { ns_t_mx,    "MX"    },
116         { ns_t_txt,   "TXT"   },
117         { ns_t_srv,   "SRV"   },
118         { ns_t_ptr,   "PTR"   },
119         { ns_t_any,   "ANY"   },
120         { }
121 };
122 
123 static const char *rcodes[] = {
124         "NOERROR",
125         "FORMERR",
126         "SERVFAIL",
127         "NXDOMAIN",
128         "NOTIMP",
129         "REFUSED",
130         "YXDOMAIN",
131         "YXRRSET",
132         "NXRRSET",
133         "NOTAUTH",
134         "NOTZONE",
135         "RESERVED11",
136         "RESERVED12",
137         "RESERVED13",
138         "RESERVED14",
139         "RESERVED15",
140         "BADVERS"
141 };
142 
143 static unsigned int default_port = 53;
144 
145 
146 static uc_value_t *
147 init_obj(uc_vm_t *vm, uc_value_t *obj, const char *key, uc_type_t type)
148 {
149         uc_value_t *existing;
150 
151         existing = ucv_object_get(obj, key, NULL);
152 
153         if (existing == NULL) {
154                 switch (type) {
155                 case UC_ARRAY:
156                         existing = ucv_array_new(vm);
157                         break;
158 
159                 case UC_OBJECT:
160                         existing = ucv_object_new(vm);
161                         break;
162 
163                 default:
164                         return NULL;
165                 }
166 
167                 ucv_object_add(obj, key, existing);
168         }
169 
170         return existing;
171 }
172 
173 static int
174 parse_reply(uc_vm_t *vm, uc_value_t *res_obj, const unsigned char *msg, size_t len)
175 {
176         ns_msg handle;
177         ns_rr rr;
178         int i, n, rdlen;
179         const char *key = NULL;
180         char astr[INET6_ADDRSTRLEN], dname[MAXDNAME];
181         const unsigned char *cp;
182         uc_value_t *name_obj, *type_arr, *item;
183 
184         if (ns_initparse(msg, len, &handle) != 0) {
185                 set_error(errno, "Unable to parse reply packet");
186 
187                 return -1;
188         }
189 
190         for (i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
191                 if (ns_parserr(&handle, ns_s_an, i, &rr) != 0) {
192                         set_error(errno, "Unable to parse resource record");
193 
194                         return -1;
195                 }
196 
197                 name_obj = init_obj(vm, res_obj, ns_rr_name(rr), UC_OBJECT);
198 
199                 rdlen = ns_rr_rdlen(rr);
200 
201                 switch (ns_rr_type(rr))
202                 {
203                 case ns_t_a:
204                         if (rdlen != 4) {
205                                 set_error(EBADMSG, "Invalid A record length");
206 
207                                 return -1;
208                         }
209 
210                         type_arr = init_obj(vm, name_obj, "A", UC_ARRAY);
211 
212                         inet_ntop(AF_INET, ns_rr_rdata(rr), astr, sizeof(astr));
213                         ucv_array_push(type_arr, ucv_string_new(astr));
214                         break;
215 
216                 case ns_t_aaaa:
217                         if (rdlen != 16) {
218                                 set_error(EBADMSG, "Invalid AAAA record length");
219 
220                                 return -1;
221                         }
222 
223                         type_arr = init_obj(vm, name_obj, "AAAA", UC_ARRAY);
224 
225                         inet_ntop(AF_INET6, ns_rr_rdata(rr), astr, sizeof(astr));
226                         ucv_array_push(type_arr, ucv_string_new(astr));
227                         break;
228 
229                 case ns_t_ns:
230                         if (!key)
231                                 key = "NS";
232                         /* fall through */
233 
234                 case ns_t_cname:
235                         if (!key)
236                                 key = "CNAME";
237                         /* fall through */
238 
239                 case ns_t_ptr:
240                         if (!key)
241                                 key = "PTR";
242 
243                         if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
244                                 ns_rr_rdata(rr), dname, sizeof(dname)) < 0) {
245                                 set_error(errno, "Unable to uncompress domain name");
246 
247                                 return -1;
248                         }
249 
250                         type_arr = init_obj(vm, name_obj, key, UC_ARRAY);
251                         n = ucv_array_length(type_arr);
252                         item = n ? ucv_array_get(type_arr, n - 1) : NULL;
253 
254                         if (!n || strcmp(ucv_string_get(item), dname))
255                                 ucv_array_push(type_arr, ucv_string_new(dname));
256 
257                         break;
258 
259                 case ns_t_mx:
260                         if (rdlen < 2) {
261                                 set_error(EBADMSG, "MX record too short");
262 
263                                 return -1;
264                         }
265 
266                         n = ns_get16(ns_rr_rdata(rr));
267 
268                         if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
269                                 ns_rr_rdata(rr) + 2, dname, sizeof(dname)) < 0) {
270                                 set_error(errno, "Unable to uncompress MX domain");
271 
272                                 return -1;
273                         }
274 
275                         type_arr = init_obj(vm, name_obj, "MX", UC_ARRAY);
276                         item = ucv_array_new_length(vm, 2);
277                         ucv_array_push(item, ucv_int64_new(n));
278                         ucv_array_push(item, ucv_string_new(dname));
279                         ucv_array_push(type_arr, item);
280                         break;
281 
282                 case ns_t_txt:
283                         if (rdlen < 1) {
284                                 set_error(EBADMSG, "TXT record too short");
285 
286                                 return -1;
287                         }
288 
289                         n = *(unsigned char *)ns_rr_rdata(rr);
290 
291                         if (n > 0) {
292                                 memset(dname, 0, sizeof(dname));
293                                 memcpy(dname, ns_rr_rdata(rr) + 1, n);
294 
295                                 type_arr = init_obj(vm, name_obj, "TXT", UC_ARRAY);
296                                 ucv_array_push(type_arr, ucv_string_new(dname));
297                         }
298                         break;
299 
300                 case ns_t_srv:
301                         if (rdlen < 6) {
302                                 set_error(EBADMSG, "SRV record too short");
303 
304                                 return -1;
305                         }
306 
307                         cp = ns_rr_rdata(rr);
308                         n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
309                                                cp + 6, dname, sizeof(dname));
310 
311                         if (n < 0) {
312                                 set_error(errno, "Unable to uncompress domain name");
313 
314                                 return -1;
315                         }
316 
317                         type_arr = init_obj(vm, name_obj, "SRV", UC_ARRAY);
318                         item = ucv_array_new_length(vm, 4);
319                         ucv_array_push(item, ucv_int64_new(ns_get16(cp)));
320                         ucv_array_push(item, ucv_int64_new(ns_get16(cp + 2)));
321                         ucv_array_push(item, ucv_int64_new(ns_get16(cp + 4)));
322                         ucv_array_push(item, ucv_string_new(dname));
323                         ucv_array_push(type_arr, item);
324                         break;
325 
326                 case ns_t_soa:
327                         if (rdlen < 20) {
328                                 set_error(EBADMSG, "SOA record too short");
329 
330                                 return -1;
331                         }
332 
333                         type_arr = init_obj(vm, name_obj, "SOA", UC_ARRAY);
334                         item = ucv_array_new_length(vm, 7);
335 
336                         cp = ns_rr_rdata(rr);
337                         n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
338                                                cp, dname, sizeof(dname));
339 
340                         if (n < 0) {
341                                 set_error(errno, "Unable to uncompress domain name");
342                                 ucv_put(item);
343 
344                                 return -1;
345                         }
346 
347                         ucv_array_push(item, ucv_string_new(dname)); /* origin */
348                         cp += n;
349 
350                         n = ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle),
351                                                cp, dname, sizeof(dname));
352 
353                         if (n < 0) {
354                                 set_error(errno, "Unable to uncompress domain name");
355                                 ucv_put(item);
356 
357                                 return -1;
358                         }
359 
360                         ucv_array_push(item, ucv_string_new(dname)); /* mail addr */
361                         cp += n;
362 
363                         ucv_array_push(item, ucv_int64_new(ns_get32(cp))); /* serial */
364                         cp += 4;
365 
366                         ucv_array_push(item, ucv_int64_new(ns_get32(cp))); /* refresh */
367                         cp += 4;
368 
369                         ucv_array_push(item, ucv_int64_new(ns_get32(cp))); /* retry */
370                         cp += 4;
371 
372                         ucv_array_push(item, ucv_int64_new(ns_get32(cp))); /* expire */
373                         cp += 4;
374 
375                         ucv_array_push(item, ucv_int64_new(ns_get32(cp))); /* minimum */
376 
377                         ucv_array_push(type_arr, item);
378                         break;
379 
380                 default:
381                         break;
382                 }
383         }
384 
385         return i;
386 }
387 
388 static int
389 parse_nsaddr(const char *addrstr, addr_t *lsa)
390 {
391         char *eptr, *hash, ifname[IFNAMSIZ];
392         unsigned int port = default_port;
393         unsigned int scope = 0;
394 
395         hash = strchr(addrstr, '#');
396 
397         if (hash) {
398                 *hash++ = '\0';
399                 port = strtoul(hash, &eptr, 10);
400 
401                 if (eptr == hash || *eptr != '\0' || port > 65535) {
402                         errno = EINVAL;
403                         return -1;
404                 }
405         }
406 
407         hash = strchr(addrstr, '%');
408 
409         if (hash) {
410                 for (eptr = ++hash; *eptr != '\0' && *eptr != '#'; eptr++) {
411                         if ((eptr - hash) >= IFNAMSIZ) {
412                                 errno = ENODEV;
413                                 return -1;
414                         }
415 
416                         ifname[eptr - hash] = *eptr;
417                 }
418 
419                 ifname[eptr - hash] = '\0';
420                 scope = if_nametoindex(ifname);
421 
422                 if (scope == 0) {
423                         errno = ENODEV;
424                         return -1;
425                 }
426         }
427 
428         if (inet_pton(AF_INET6, addrstr, &lsa->u.sin6.sin6_addr)) {
429                 lsa->u.sin6.sin6_family = AF_INET6;
430                 lsa->u.sin6.sin6_port = htons(port);
431                 lsa->u.sin6.sin6_scope_id = scope;
432                 lsa->len = sizeof(lsa->u.sin6);
433                 return 0;
434         }
435 
436         if (!scope && inet_pton(AF_INET, addrstr, &lsa->u.sin.sin_addr)) {
437                 lsa->u.sin.sin_family = AF_INET;
438                 lsa->u.sin.sin_port = htons(port);
439                 lsa->len = sizeof(lsa->u.sin);
440                 return 0;
441         }
442 
443         errno = EINVAL;
444         return -1;
445 }
446 
447 static char *
448 make_ptr(const char *addrstr)
449 {
450         const char *hexdigit = "0123456789abcdef";
451         static char ptrstr[73];
452         unsigned char addr[16];
453         char *ptr = ptrstr;
454         int i;
455 
456         if (inet_pton(AF_INET6, addrstr, addr)) {
457                 if (memcmp(addr, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12) != 0) {
458                         for (i = 0; i < 16; i++) {
459                                 *ptr++ = hexdigit[(unsigned char)addr[15 - i] & 0xf];
460                                 *ptr++ = '.';
461                                 *ptr++ = hexdigit[(unsigned char)addr[15 - i] >> 4];
462                                 *ptr++ = '.';
463                         }
464                         strcpy(ptr, "ip6.arpa");
465                 }
466                 else {
467                         sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa",
468                                 addr[15], addr[14], addr[13], addr[12]);
469                 }
470 
471                 return ptrstr;
472         }
473 
474         if (inet_pton(AF_INET, addrstr, addr)) {
475                 sprintf(ptr, "%u.%u.%u.%u.in-addr.arpa",
476                         addr[3], addr[2], addr[1], addr[0]);
477                 return ptrstr;
478         }
479 
480         return NULL;
481 }
482 
483 static unsigned long
484 mtime(void)
485 {
486         struct timespec ts;
487         clock_gettime(CLOCK_REALTIME, &ts);
488 
489         return (unsigned long)ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
490 }
491 
492 static void
493 to_v4_mapped(addr_t *a)
494 {
495         if (a->u.sa.sa_family != AF_INET)
496                 return;
497 
498         memcpy(a->u.sin6.sin6_addr.s6_addr + 12,
499                &a->u.sin.sin_addr, 4);
500 
501         memcpy(a->u.sin6.sin6_addr.s6_addr,
502                "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
503 
504         a->u.sin6.sin6_family = AF_INET6;
505         a->u.sin6.sin6_flowinfo = 0;
506         a->u.sin6.sin6_scope_id = 0;
507         a->len = sizeof(a->u.sin6);
508 }
509 
510 static void
511 add_status(uc_vm_t *vm, uc_value_t *res_obj, const char *name, const char *rcode)
512 {
513         uc_value_t *name_obj = init_obj(vm, res_obj, name, UC_OBJECT);
514 
515         ucv_object_add(name_obj, "rcode", ucv_string_new(rcode));
516 }
517 
518 /*
519  * Function logic borrowed & modified from musl libc, res_msend.c
520  */
521 
522 static int
523 send_queries(resolve_ctx_t *ctx, uc_vm_t *vm, uc_value_t *res_obj)
524 {
525         int fd, flags;
526         int servfail_retry = 0;
527         addr_t from = { };
528         int one = 1;
529         int recvlen = 0;
530         int n_replies = 0;
531         struct pollfd pfd;
532         unsigned long t0, t1, t2, timeout = ctx->timeout, retry_interval;
533         unsigned int nn, qn, next_query = 0;
534         struct { unsigned char *buf; size_t len; } reply_buf = { 0 };
535 
536         from.u.sa.sa_family = AF_INET;
537         from.len = sizeof(from.u.sin);
538 
539         for (nn = 0; nn < ctx->n_ns; nn++) {
540                 if (ctx->ns[nn].addr.u.sa.sa_family == AF_INET6) {
541                         from.u.sa.sa_family = AF_INET6;
542                         from.len = sizeof(from.u.sin6);
543                         break;
544                 }
545         }
546 
547 #ifdef __APPLE__
548         flags = SOCK_DGRAM;
549 #else
550         flags = SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK;
551 #endif
552 
553         /* Get local address and open/bind a socket */
554         fd = socket(from.u.sa.sa_family, flags, 0);
555 
556         /* Handle case where system lacks IPv6 support */
557         if (fd < 0 && from.u.sa.sa_family == AF_INET6 && errno == EAFNOSUPPORT) {
558                 fd = socket(AF_INET, flags, 0);
559                 from.u.sa.sa_family = AF_INET;
560         }
561 
562         if (fd < 0) {
563                 set_error(errno, "Unable to open UDP socket");
564 
565                 return -1;
566         }
567 
568 #ifdef __APPLE__
569         flags = fcntl(fd, F_GETFD);
570 
571         if (flags < 0) {
572                 set_error(errno, "Unable to acquire socket descriptor flags");
573                 close(fd);
574 
575                 return -1;
576         }
577 
578         if (fcntl(fd, F_SETFD, flags|O_CLOEXEC|O_NONBLOCK) < 0) {
579                 set_error(errno, "Unable to set socket descriptor flags");
580                 close(fd);
581 
582                 return -1;
583         }
584 #endif
585 
586         if (bind(fd, &from.u.sa, from.len) < 0) {
587                 set_error(errno, "Unable to bind UDP socket");
588                 close(fd);
589 
590                 return -1;
591         }
592 
593         /* Convert any IPv4 addresses in a mixed environment to v4-mapped */
594         if (from.u.sa.sa_family == AF_INET6) {
595                 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one));
596 
597                 for (nn = 0; nn < ctx->n_ns; nn++)
598                         to_v4_mapped(&ctx->ns[nn].addr);
599         }
600 
601         pfd.fd = fd;
602         pfd.events = POLLIN;
603         retry_interval = timeout / ctx->retries;
604         t0 = t2 = mtime();
605         t1 = t2 - retry_interval;
606 
607         for (; t2 - t0 < timeout; t2 = mtime()) {
608                 if (t2 - t1 >= retry_interval) {
609                         for (qn = 0; qn < ctx->n_queries; qn++) {
610                                 if (ctx->queries[qn].rcode == 0 || ctx->queries[qn].rcode == 3)
611                                         continue;
612 
613                                 for (nn = 0; nn < ctx->n_ns; nn++) {
614                                         sendto(fd, ctx->queries[qn].query, ctx->queries[qn].qlen,
615                                                MSG_NOSIGNAL, &ctx->ns[nn].addr.u.sa, ctx->ns[nn].addr.len);
616                                 }
617                         }
618 
619                         t1 = t2;
620                         servfail_retry = 2 * ctx->n_queries;
621                 }
622 
623                 /* Wait for a response, or until time to retry */
624                 switch (poll(&pfd, 1, t1+retry_interval-t2)) {
625                 case 0:
626                         /* timeout */
627                         for (qn = 0; qn < ctx->n_queries; qn++) {
628                                 if (ctx->queries[qn].rcode != -1)
629                                         continue;
630 
631                                 for (nn = 0; nn < ctx->n_ns; nn++)
632                                         add_status(vm, res_obj, ctx->queries[qn].name, "TIMEOUT");
633                         }
634 
635                         continue;
636 
637                 case -1:
638                         /* error */
639                         continue;
640                 }
641 
642                 while (1) {
643                         recvlen = recvfrom(fd, NULL, 0, MSG_PEEK|MSG_TRUNC, &from.u.sa, &from.len);
644 
645                         /* read error */
646                         if (recvlen < 0)
647                                 break;
648 
649                         if ((size_t)recvlen > reply_buf.len) {
650                                 reply_buf.buf = xrealloc(reply_buf.buf, recvlen);
651                                 reply_buf.len = recvlen;
652                         }
653 
654                         recvlen = recvfrom(fd, reply_buf.buf, recvlen, 0, &from.u.sa, &from.len);
655 
656                         /* Ignore non-identifiable packets */
657                         if (recvlen < 4)
658                                 continue;
659 
660                         /* Ignore replies from addresses we didn't send to */
661                         for (nn = 0; nn < ctx->n_ns; nn++)
662                                 if (memcmp(&from.u.sa, &ctx->ns[nn].addr.u.sa, from.len) == 0)
663                                         break;
664 
665                         if (nn >= ctx->n_ns)
666                                 continue;
667 
668                         /* Find which query this answer goes with, if any */
669                         for (qn = next_query; qn < ctx->n_queries; qn++)
670                                 if (!memcmp(reply_buf.buf, ctx->queries[qn].query, 2))
671                                         break;
672 
673                         /* Do not overwrite previous replies from other servers
674                          * but allow overwriting preexisting NXDOMAIN reply */
675                         if (qn >= ctx->n_queries ||
676                             ctx->queries[qn].rcode == 0 ||
677                             (ctx->queries[qn].rcode == 3 && (reply_buf.buf[3] & 15) != 0))
678                                 continue;
679 
680                         ctx->queries[qn].rcode = reply_buf.buf[3] & 15;
681 
682                         switch (ctx->queries[qn].rcode) {
683                         case 0:
684                                 ucv_object_delete(
685                                         ucv_object_get(res_obj, ctx->queries[qn].name, NULL),
686                                         "rcodes");
687 
688                                 break;
689 
690                         case 2:
691                                 /* Retry immediately on server failure. */
692                                 if (servfail_retry && servfail_retry--)
693                                         sendto(fd, ctx->queries[qn].query, ctx->queries[qn].qlen,
694                                                MSG_NOSIGNAL, &ctx->ns[nn].addr.u.sa, ctx->ns[nn].addr.len);
695 
696                                 /* fall through */
697 
698                         default:
699                                 add_status(vm, res_obj, ctx->queries[qn].name,
700                                            rcodes[ctx->queries[qn].rcode]);
701                         }
702 
703                         /* Store answer */
704                         n_replies++;
705 
706                         ctx->queries[qn].rlen = recvlen;
707 
708                         parse_reply(vm, res_obj, reply_buf.buf, recvlen);
709 
710                         if (qn == next_query) {
711                                 while (next_query < ctx->n_queries) {
712                                         if (ctx->queries[next_query].rcode == -1)
713                                                 break;
714 
715                                         next_query++;
716                                 }
717                         }
718 
719                         if (next_query >= ctx->n_queries)
720                                 goto out;
721                 }
722         }
723 
724 out:
725         free(reply_buf.buf);
726         close(fd);
727 
728         return n_replies;
729 }
730 
731 static ns_t *
732 add_ns(resolve_ctx_t *ctx, const char *addr)
733 {
734         char portstr[sizeof("65535")], *p;
735         addr_t a = { };
736         struct addrinfo *ai, *aip, hints = {
737                 .ai_flags = AI_NUMERICSERV,
738                 .ai_socktype = SOCK_DGRAM
739         };
740 
741         if (parse_nsaddr(addr, &a)) {
742                 /* Maybe we got a domain name, attempt to resolve it using the standard
743                  * resolver routines */
744 
745                 p = strchr(addr, '#');
746                 snprintf(portstr, sizeof(portstr), "%hu",
747                          (unsigned short)(p ? strtoul(p, NULL, 10) : default_port));
748 
749                 if (!getaddrinfo(addr, portstr, &hints, &ai)) {
750                         for (aip = ai; aip; aip = aip->ai_next) {
751                                 if (aip->ai_addr->sa_family != AF_INET &&
752                                     aip->ai_addr->sa_family != AF_INET6)
753                                         continue;
754 
755                                 ctx->ns = xrealloc(ctx->ns, sizeof(*ctx->ns) * (ctx->n_ns + 1));
756                                 ctx->ns[ctx->n_ns].name = addr;
757                                 ctx->ns[ctx->n_ns].addr.len = aip->ai_addrlen;
758 
759                                 memcpy(&ctx->ns[ctx->n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen);
760 
761                                 ctx->n_ns++;
762                         }
763 
764                         freeaddrinfo(ai);
765 
766                         return &ctx->ns[ctx->n_ns];
767                 }
768 
769                 return NULL;
770         }
771 
772         ctx->ns = xrealloc(ctx->ns, sizeof(*ctx->ns) * (ctx->n_ns + 1));
773         ctx->ns[ctx->n_ns].addr = a;
774         ctx->ns[ctx->n_ns].name = addr;
775 
776         return &ctx->ns[ctx->n_ns++];
777 }
778 
779 static int
780 parse_resolvconf(resolve_ctx_t *ctx)
781 {
782         int prev_n_ns = ctx->n_ns;
783         char line[128], *p;
784         FILE *resolv;
785         bool ok;
786 
787         if ((resolv = fopen("/etc/resolv.conf", "r")) != NULL) {
788                 while (fgets(line, sizeof(line), resolv)) {
789                         p = strtok(line, " \t\n");
790 
791                         if (!p || strcmp(p, "nameserver"))
792                                 continue;
793 
794                         p = strtok(NULL, " \t\n");
795 
796                         if (!p)
797                                 continue;
798 
799                         p = xstrdup(p);
800                         ok = add_ns(ctx, p);
801 
802                         free(p);
803 
804                         if (!ok)
805                                 break;
806                 }
807 
808                 fclose(resolv);
809         }
810 
811         return ctx->n_ns - prev_n_ns;
812 }
813 
814 static query_t *
815 add_query(resolve_ctx_t *ctx, int type, const char *dname)
816 {
817         opt_rr_t *opt;
818         ssize_t qlen;
819 
820         ctx->queries = xrealloc(ctx->queries, sizeof(*ctx->queries) * (ctx->n_queries + 1));
821 
822         memset(&ctx->queries[ctx->n_queries], 0, sizeof(*ctx->queries));
823 
824         qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
825                            ctx->queries[ctx->n_queries].query,
826                            sizeof(ctx->queries[ctx->n_queries].query));
827 
828         /* add OPT record */
829         if (ctx->edns_maxsize != 0 && qlen + sizeof(opt_rr_t) <= sizeof(ctx->queries[ctx->n_queries].query)) {
830                 ctx->queries[ctx->n_queries].query[11] = 1;
831 
832                 opt = (opt_rr_t *)&ctx->queries[ctx->n_queries].query[qlen];
833                 opt->root_domain = 0;
834                 opt->type = htons(41);
835                 opt->edns_maxsize = htons(ctx->edns_maxsize);
836                 opt->extended_rcode = 0;
837                 opt->edns_version = 0;
838                 opt->z = htons(0);
839                 opt->data_length = htons(0);
840 
841                 qlen += sizeof(opt_rr_t);
842         }
843 
844         ctx->queries[ctx->n_queries].qlen = qlen;
845         ctx->queries[ctx->n_queries].name = xstrdup(dname);
846         ctx->queries[ctx->n_queries].rcode = -1;
847 
848         return &ctx->queries[ctx->n_queries++];
849 }
850 
851 static bool
852 check_types(uc_value_t *typenames, uint32_t *types)
853 {
854         size_t i;
855 
856         *types = 0;
857 
858         for_each_item(typenames, typename) {
859                 if (ucv_type(typename) != UC_STRING)
860                         err_return(EINVAL, "Query type value not a string");
861 
862                 for (i = 0; qtypes[i].name; i++) {
863                         if (!strcasecmp(ucv_string_get(typename), qtypes[i].name)) {
864                                 *types |= (1 << i);
865                                 break;
866                         }
867                 }
868 
869                 if (!qtypes[i].name)
870                         err_return(EINVAL, "Unrecognized query type '%s'",
871                                    ucv_string_get(typename));
872         }
873 
874         return true;
875 }
876 
877 static void
878 add_queries(resolve_ctx_t *ctx, uc_value_t *name)
879 {
880         char *s = ucv_string_get(name);
881         char *ptr;
882         size_t i;
883 
884         if (ctx->qtypes == 0) {
885                 ptr = make_ptr(s);
886 
887                 if (ptr) {
888                         add_query(ctx, ns_t_ptr, ptr);
889                 }
890                 else {
891                         add_query(ctx, ns_t_a, s);
892                         add_query(ctx, ns_t_aaaa, s);
893                 }
894         }
895         else {
896                 for (i = 0; qtypes[i].name; i++) {
897                         if (ctx->qtypes & (1 << i)) {
898                                 if (qtypes[i].type == ns_t_ptr) {
899                                         ptr = make_ptr(s);
900                                         add_query(ctx, ns_t_ptr, ptr ? ptr : s);
901                                 }
902                                 else {
903                                         add_query(ctx, qtypes[i].type, s);
904                                 }
905                         }
906                 }
907         }
908 }
909 
910 static bool
911 parse_options(resolve_ctx_t *ctx, uc_value_t *opts)
912 {
913         uc_value_t *v;
914 
915         if (!check_types(ucv_object_get(opts, "type", NULL), &ctx->qtypes))
916                 return false;
917 
918         for_each_item(ucv_object_get(opts, "nameserver", NULL), server) {
919                 if (ucv_type(server) != UC_STRING)
920                         err_return(EINVAL, "Nameserver value not a string");
921 
922                 if (!add_ns(ctx, ucv_string_get(server)))
923                         err_return(EINVAL, "Unable to resolve nameserver address '%s'",
924                                    ucv_string_get(server));
925         }
926 
927         /* Find NS servers in resolv.conf if none provided */
928         if (ctx->n_ns == 0)
929                 parse_resolvconf(ctx);
930 
931         /* Fall back to localhost if we could not find NS in resolv.conf */
932         if (ctx->n_ns == 0)
933                 add_ns(ctx, "127.0.0.1");
934 
935         v = ucv_object_get(opts, "retries", NULL);
936 
937         if (ucv_type(v) == UC_INTEGER)
938                 ctx->retries = ucv_uint64_get(v);
939         else if (v)
940                 err_return(EINVAL, "Retries value not an integer");
941 
942         v = ucv_object_get(opts, "timeout", NULL);
943 
944         if (ucv_type(v) == UC_INTEGER)
945                 ctx->timeout = ucv_uint64_get(v);
946         else if (v)
947                 err_return(EINVAL, "Timeout value not an integer");
948 
949         v = ucv_object_get(opts, "edns_maxsize", NULL);
950 
951         if (ucv_type(v) == UC_INTEGER)
952                 ctx->edns_maxsize = ucv_uint64_get(v);
953         else if (v)
954                 err_return(EINVAL, "EDNS max size not an integer");
955 
956         return true;
957 }
958 
959 static uc_value_t *
960 uc_resolv_query(uc_vm_t *vm, size_t nargs)
961 {
962         resolve_ctx_t ctx = { .retries = 2, .timeout = 5000, .edns_maxsize = 4096 };
963         uc_value_t *names = uc_fn_arg(0);
964         uc_value_t *opts = uc_fn_arg(1);
965         uc_value_t *res_obj = NULL;
966 
967         if (!parse_options(&ctx, opts))
968                 goto err;
969 
970         for_each_item(names, name) {
971                 if (ucv_type(name) != UC_STRING) {
972                         set_error(EINVAL, "Domain name value not a string");
973                         goto err;
974                 }
975 
976                 add_queries(&ctx, name);
977         }
978 
979         res_obj = ucv_object_new(vm);
980 
981         if (send_queries(&ctx, vm, res_obj) == 0)
982                 set_error(ETIMEDOUT, "Server did not respond");
983 
984 err:
985         while (ctx.n_queries)
986                 free(ctx.queries[--ctx.n_queries].name);
987 
988         free(ctx.queries);
989         free(ctx.ns);
990 
991         return res_obj;
992 }
993 
994 static uc_value_t *
995 uc_resolv_error(uc_vm_t *vm, size_t nargs)
996 {
997         uc_stringbuf_t *buf;
998         const char *s;
999 
1000         if (last_error.code == 0)
1001                 return NULL;
1002 
1003         buf = ucv_stringbuf_new();
1004 
1005         s = strerror(last_error.code);
1006 
1007         ucv_stringbuf_addstr(buf, s, strlen(s));
1008 
1009         if (last_error.msg)
1010                 ucv_stringbuf_printf(buf, ": %s", last_error.msg);
1011 
1012         set_error(0, NULL);
1013 
1014         return ucv_stringbuf_finish(buf);
1015 }
1016 
1017 
1018 static const uc_function_list_t resolv_fns[] = {
1019         { "query",      uc_resolv_query },
1020         { "error",      uc_resolv_error },
1021 };
1022 
1023 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1024 {
1025         uc_function_list_register(scope, resolv_fns);
1026 }
1027 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt