• 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 
727         return n_replies;
728 }
729 
730 static ns_t *
731 add_ns(resolve_ctx_t *ctx, const char *addr)
732 {
733         char portstr[sizeof("65535")], *p;
734         addr_t a = { };
735         struct addrinfo *ai, *aip, hints = {
736                 .ai_flags = AI_NUMERICSERV,
737                 .ai_socktype = SOCK_DGRAM
738         };
739 
740         if (parse_nsaddr(addr, &a)) {
741                 /* Maybe we got a domain name, attempt to resolve it using the standard
742                  * resolver routines */
743 
744                 p = strchr(addr, '#');
745                 snprintf(portstr, sizeof(portstr), "%hu",
746                          (unsigned short)(p ? strtoul(p, NULL, 10) : default_port));
747 
748                 if (!getaddrinfo(addr, portstr, &hints, &ai)) {
749                         for (aip = ai; aip; aip = aip->ai_next) {
750                                 if (aip->ai_addr->sa_family != AF_INET &&
751                                     aip->ai_addr->sa_family != AF_INET6)
752                                         continue;
753 
754                                 ctx->ns = xrealloc(ctx->ns, sizeof(*ctx->ns) * (ctx->n_ns + 1));
755                                 ctx->ns[ctx->n_ns].name = addr;
756                                 ctx->ns[ctx->n_ns].addr.len = aip->ai_addrlen;
757 
758                                 memcpy(&ctx->ns[ctx->n_ns].addr.u.sa, aip->ai_addr, aip->ai_addrlen);
759 
760                                 ctx->n_ns++;
761                         }
762 
763                         freeaddrinfo(ai);
764 
765                         return &ctx->ns[ctx->n_ns];
766                 }
767 
768                 return NULL;
769         }
770 
771         ctx->ns = xrealloc(ctx->ns, sizeof(*ctx->ns) * (ctx->n_ns + 1));
772         ctx->ns[ctx->n_ns].addr = a;
773         ctx->ns[ctx->n_ns].name = addr;
774 
775         return &ctx->ns[ctx->n_ns++];
776 }
777 
778 static int
779 parse_resolvconf(resolve_ctx_t *ctx)
780 {
781         int prev_n_ns = ctx->n_ns;
782         char line[128], *p;
783         FILE *resolv;
784         bool ok;
785 
786         if ((resolv = fopen("/etc/resolv.conf", "r")) != NULL) {
787                 while (fgets(line, sizeof(line), resolv)) {
788                         p = strtok(line, " \t\n");
789 
790                         if (!p || strcmp(p, "nameserver"))
791                                 continue;
792 
793                         p = strtok(NULL, " \t\n");
794 
795                         if (!p)
796                                 continue;
797 
798                         p = xstrdup(p);
799                         ok = add_ns(ctx, p);
800 
801                         free(p);
802 
803                         if (!ok)
804                                 break;
805                 }
806 
807                 fclose(resolv);
808         }
809 
810         return ctx->n_ns - prev_n_ns;
811 }
812 
813 static query_t *
814 add_query(resolve_ctx_t *ctx, int type, const char *dname)
815 {
816         opt_rr_t *opt;
817         ssize_t qlen;
818 
819         ctx->queries = xrealloc(ctx->queries, sizeof(*ctx->queries) * (ctx->n_queries + 1));
820 
821         memset(&ctx->queries[ctx->n_queries], 0, sizeof(*ctx->queries));
822 
823         qlen = res_mkquery(QUERY, dname, C_IN, type, NULL, 0, NULL,
824                            ctx->queries[ctx->n_queries].query,
825                            sizeof(ctx->queries[ctx->n_queries].query));
826 
827         /* add OPT record */
828         if (ctx->edns_maxsize != 0 && qlen + sizeof(opt_rr_t) <= sizeof(ctx->queries[ctx->n_queries].query)) {
829                 ctx->queries[ctx->n_queries].query[11] = 1;
830 
831                 opt = (opt_rr_t *)&ctx->queries[ctx->n_queries].query[qlen];
832                 opt->root_domain = 0;
833                 opt->type = htons(41);
834                 opt->edns_maxsize = htons(ctx->edns_maxsize);
835                 opt->extended_rcode = 0;
836                 opt->edns_version = 0;
837                 opt->z = htons(0);
838                 opt->data_length = htons(0);
839 
840                 qlen += sizeof(opt_rr_t);
841         }
842 
843         ctx->queries[ctx->n_queries].qlen = qlen;
844         ctx->queries[ctx->n_queries].name = xstrdup(dname);
845         ctx->queries[ctx->n_queries].rcode = -1;
846 
847         return &ctx->queries[ctx->n_queries++];
848 }
849 
850 static bool
851 check_types(uc_value_t *typenames, uint32_t *types)
852 {
853         size_t i;
854 
855         *types = 0;
856 
857         for_each_item(typenames, typename) {
858                 if (ucv_type(typename) != UC_STRING)
859                         err_return(EINVAL, "Query type value not a string");
860 
861                 for (i = 0; qtypes[i].name; i++) {
862                         if (!strcasecmp(ucv_string_get(typename), qtypes[i].name)) {
863                                 *types |= (1 << i);
864                                 break;
865                         }
866                 }
867 
868                 if (!qtypes[i].name)
869                         err_return(EINVAL, "Unrecognized query type '%s'",
870                                    ucv_string_get(typename));
871         }
872 
873         return true;
874 }
875 
876 static void
877 add_queries(resolve_ctx_t *ctx, uc_value_t *name)
878 {
879         char *s = ucv_string_get(name);
880         char *ptr;
881         size_t i;
882 
883         if (ctx->qtypes == 0) {
884                 ptr = make_ptr(s);
885 
886                 if (ptr) {
887                         add_query(ctx, ns_t_ptr, ptr);
888                 }
889                 else {
890                         add_query(ctx, ns_t_a, s);
891                         add_query(ctx, ns_t_aaaa, s);
892                 }
893         }
894         else {
895                 for (i = 0; qtypes[i].name; i++) {
896                         if (ctx->qtypes & (1 << i)) {
897                                 if (qtypes[i].type == ns_t_ptr) {
898                                         ptr = make_ptr(s);
899                                         add_query(ctx, ns_t_ptr, ptr ? ptr : s);
900                                 }
901                                 else {
902                                         add_query(ctx, qtypes[i].type, s);
903                                 }
904                         }
905                 }
906         }
907 }
908 
909 static bool
910 parse_options(resolve_ctx_t *ctx, uc_value_t *opts)
911 {
912         uc_value_t *v;
913 
914         if (!check_types(ucv_object_get(opts, "type", NULL), &ctx->qtypes))
915                 return false;
916 
917         for_each_item(ucv_object_get(opts, "nameserver", NULL), server) {
918                 if (ucv_type(server) != UC_STRING)
919                         err_return(EINVAL, "Nameserver value not a string");
920 
921                 if (!add_ns(ctx, ucv_string_get(server)))
922                         err_return(EINVAL, "Unable to resolve nameserver address '%s'",
923                                    ucv_string_get(server));
924         }
925 
926         /* Find NS servers in resolv.conf if none provided */
927         if (ctx->n_ns == 0)
928                 parse_resolvconf(ctx);
929 
930         /* Fall back to localhost if we could not find NS in resolv.conf */
931         if (ctx->n_ns == 0)
932                 add_ns(ctx, "127.0.0.1");
933 
934         v = ucv_object_get(opts, "retries", NULL);
935 
936         if (ucv_type(v) == UC_INTEGER)
937                 ctx->retries = ucv_uint64_get(v);
938         else if (v)
939                 err_return(EINVAL, "Retries value not an integer");
940 
941         v = ucv_object_get(opts, "timeout", NULL);
942 
943         if (ucv_type(v) == UC_INTEGER)
944                 ctx->timeout = ucv_uint64_get(v);
945         else if (v)
946                 err_return(EINVAL, "Timeout value not an integer");
947 
948         v = ucv_object_get(opts, "edns_maxsize", NULL);
949 
950         if (ucv_type(v) == UC_INTEGER)
951                 ctx->edns_maxsize = ucv_uint64_get(v);
952         else if (v)
953                 err_return(EINVAL, "EDNS max size not an integer");
954 
955         return true;
956 }
957 
958 static uc_value_t *
959 uc_resolv_query(uc_vm_t *vm, size_t nargs)
960 {
961         resolve_ctx_t ctx = { .retries = 2, .timeout = 5000, .edns_maxsize = 4096 };
962         uc_value_t *names = uc_fn_arg(0);
963         uc_value_t *opts = uc_fn_arg(1);
964         uc_value_t *res_obj = NULL;
965 
966         if (!parse_options(&ctx, opts))
967                 goto err;
968 
969         for_each_item(names, name) {
970                 if (ucv_type(name) != UC_STRING) {
971                         set_error(EINVAL, "Domain name value not a string");
972                         goto err;
973                 }
974 
975                 add_queries(&ctx, name);
976         }
977 
978         res_obj = ucv_object_new(vm);
979 
980         if (send_queries(&ctx, vm, res_obj) == 0)
981                 set_error(ETIMEDOUT, "Server did not respond");
982 
983 err:
984         while (ctx.n_queries)
985                 free(ctx.queries[--ctx.n_queries].name);
986 
987         free(ctx.queries);
988         free(ctx.ns);
989 
990         return res_obj;
991 }
992 
993 static uc_value_t *
994 uc_resolv_error(uc_vm_t *vm, size_t nargs)
995 {
996         uc_stringbuf_t *buf;
997         const char *s;
998 
999         if (last_error.code == 0)
1000                 return NULL;
1001 
1002         buf = ucv_stringbuf_new();
1003 
1004         s = strerror(last_error.code);
1005 
1006         ucv_stringbuf_addstr(buf, s, strlen(s));
1007 
1008         if (last_error.msg)
1009                 ucv_stringbuf_printf(buf, ": %s", last_error.msg);
1010 
1011         set_error(0, NULL);
1012 
1013         return ucv_stringbuf_finish(buf);
1014 }
1015 
1016 
1017 static const uc_function_list_t resolv_fns[] = {
1018         { "query",      uc_resolv_query },
1019         { "error",      uc_resolv_error },
1020 };
1021 
1022 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1023 {
1024         uc_function_list_register(scope, resolv_fns);
1025 }
1026 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt