quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

dnsd.c (17142B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 #include "first.h"
     25 
     26 static int dnsd_wrotepidfile = 0;
     27 static int dnsd_wroteportfile = 0;
     28 
     29 static unsigned short get16bit(const unsigned char **pkt,
     30                                size_t *size)
     31 {
     32   const unsigned char *p = *pkt;
     33   (*pkt) += 2;
     34   *size -= 2;
     35   return (unsigned short)((p[0] << 8) | p[1]);
     36 }
     37 
     38 static char name[256];
     39 
     40 static int qname(const unsigned char **pkt, size_t *size)
     41 {
     42   unsigned char length;
     43   int o = 0;
     44   const unsigned char *p = *pkt;
     45   do {
     46     int i;
     47     length = *p++;
     48     if(*size < length)
     49       /* too long */
     50       return 1;
     51     if(length && o)
     52       name[o++] = '.';
     53     for(i = 0; i < length; i++) {
     54       name[o++] = *p++;
     55     }
     56   } while(length);
     57   *size -= (p - *pkt);
     58   *pkt = p;
     59   name[o++] = '\0';
     60   return 0;
     61 }
     62 
     63 #define QTYPE_A 1
     64 #define QTYPE_AAAA 28
     65 #define QTYPE_HTTPS 0x41
     66 
     67 /*
     68  * Handle initial connection protocol.
     69  *
     70  * Return query (qname + type + class), type and id.
     71  */
     72 static int store_incoming(const unsigned char *data, size_t size,
     73                           unsigned char *qbuf, size_t *qlen,
     74                           unsigned short *qtype, unsigned short *idp)
     75 {
     76   FILE *server;
     77   char dumpfile[256];
     78 #if 0
     79   size_t i;
     80 #endif
     81   unsigned short qd;
     82   const unsigned char *qptr;
     83   size_t qsize;
     84 
     85   *qlen = 0;
     86   *qtype = 0;
     87   *idp = 0;
     88 
     89   snprintf(dumpfile, sizeof(dumpfile), "%s/dnsd.input", logdir);
     90 
     91   /* Open request dump file. */
     92   server = fopen(dumpfile, "ab");
     93   if(!server) {
     94     int error = errno;
     95     logmsg("fopen() failed with error (%d) %s", error, strerror(error));
     96     logmsg("Error opening file '%s'", dumpfile);
     97     return -1;
     98   }
     99 
    100   /*
    101                                     1  1  1  1  1  1
    102       0  1  2  3  4  5  6  7  8  9  0  1  2  3  4  5
    103     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    104     |                      ID                       |
    105     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    106     |QR|   Opcode  |AA|TC|RD|RA|   Z    |   RCODE   |
    107     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    108     |                    QDCOUNT                    |
    109     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    110     |                    ANCOUNT                    |
    111     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    112     |                    NSCOUNT                    |
    113     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    114     |                    ARCOUNT                    |
    115     +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
    116    */
    117   *idp = get16bit(&data, &size);
    118   data += 2; /* skip the next 16 bits */
    119   size -= 2;
    120 #if 0
    121   fprintf(server, "QR: %x\n", (id & 0x8000) > 15);
    122   fprintf(server, "OPCODE: %x\n", (id & 0x7800) >> 11);
    123   fprintf(server, "TC: %x\n", (id & 0x200) >> 9);
    124   fprintf(server, "RD: %x\n", (id & 0x100) >> 8);
    125   fprintf(server, "Z: %x\n", (id & 0x70) >> 4);
    126   fprintf(server, "RCODE: %x\n", (id & 0x0f));
    127 #endif
    128   qd = get16bit(&data, &size);
    129   fprintf(server, "QDCOUNT: %04x\n", qd);
    130 
    131   data += 6; /* skip ANCOUNT, NSCOUNT and ARCOUNT */
    132   size -= 6;
    133 
    134   /* store pointer and size at the QD point */
    135   qsize = size;
    136   qptr = data;
    137 
    138   if(!qname(&data, &size)) {
    139     fprintf(server, "QNAME: %s\n", name);
    140     qd = get16bit(&data, &size);
    141     fprintf(server, "QTYPE: %04x\n", qd);
    142     *qtype = qd;
    143     logmsg("Question for '%s' type %x", name, qd);
    144 
    145     qd = get16bit(&data, &size);
    146     logmsg("QCLASS: %04x\n", qd);
    147 
    148     *qlen = qsize - size; /* total size of the query */
    149     memcpy(qbuf, qptr, *qlen);
    150   }
    151   else
    152     logmsg("Bad input qname");
    153 #if 0
    154   for(i = 0; i < size; i++) {
    155     fprintf(server, "%02d", (unsigned int)data[i]);
    156   }
    157   fprintf(server, "\n");
    158 #endif
    159 
    160   fclose(server);
    161 
    162   return 0;
    163 }
    164 
    165 static void add_answer(unsigned char *bytes, size_t *w,
    166                        const unsigned char *a, size_t alen,
    167                        unsigned short qtype)
    168 {
    169   size_t i = *w;
    170 
    171   /* add answer */
    172   bytes[i++] = 0xc0;
    173   bytes[i++] = 0x0c; /* points to the query at this fixed packet index */
    174 
    175   /* QTYPE */
    176   bytes[i++] = (unsigned char)(qtype >> 8);
    177   bytes[i++] = (unsigned char)(qtype & 0xff);
    178 
    179   /* QCLASS IN */
    180   bytes[i++] = 0x00;
    181   bytes[i++] = 0x01;
    182 
    183   /* TTL, Time to live: 2580 (43 minutes) */
    184   bytes[i++] = 0x00;
    185   bytes[i++] = 0x00;
    186   bytes[i++] = 0x0a;
    187   bytes[i++] = 0x14;
    188 
    189   /* QTYPE size */
    190   bytes[i++] = (unsigned char)(alen >> 8);
    191   bytes[i++] = (unsigned char)(alen & 0xff);
    192 
    193   memcpy(&bytes[i], a, alen);
    194   i += alen;
    195 
    196   *w = i;
    197 }
    198 
    199 #ifdef _WIN32
    200 #define SENDTO3 int
    201 #else
    202 #define SENDTO3 size_t
    203 #endif
    204 
    205 #define INSTRUCTIONS "dnsd.cmd"
    206 
    207 #define MAX_ALPN 5
    208 
    209 static unsigned char ipv4_pref[4];
    210 static unsigned char ipv6_pref[16];
    211 static unsigned char alpn_pref[MAX_ALPN];
    212 static int alpn_count;
    213 static unsigned char ancount_a;
    214 static unsigned char ancount_aaaa;
    215 
    216 /* this is an answer to a question */
    217 static int send_response(curl_socket_t sock,
    218                          const struct sockaddr *addr, curl_socklen_t addrlen,
    219                          unsigned char *qbuf, size_t qlen,
    220                          unsigned short qtype, unsigned short id)
    221 {
    222   ssize_t rc;
    223   size_t i;
    224   int a;
    225   char addrbuf[128]; /* IP address buffer */
    226   unsigned char bytes[256] = {
    227     0x80, 0xea, /* ID, overwrite */
    228     0x81, 0x80,
    229     /*
    230     Flags: 0x8180 Standard query response, No error
    231         1... .... .... .... = Response: Message is a response
    232         .000 0... .... .... = Opcode: Standard query (0)
    233         .... .0.. .... .... = Authoritative: Server is not an authority for
    234                               domain
    235         .... ..0. .... .... = Truncated: Message is not truncated
    236         .... ...1 .... .... = Recursion desired: Do query recursively
    237         .... .... 1... .... = Recursion available: Server can do recursive
    238                               queries
    239         .... .... .0.. .... = Z: reserved (0)
    240         .... .... ..0. .... = Answer authenticated: Answer/authority portion
    241                               was not authenticated by the server
    242         .... .... ...0 .... = Non-authenticated data: Unacceptable
    243         .... .... .... 0000 = Reply code: No error (0)
    244     */
    245     0x0, 0x1, /* QDCOUNT a single question */
    246     0x0, 0x0, /* ANCOUNT number of answers */
    247     0x0, 0x0, /* NSCOUNT */
    248     0x0, 0x0  /* ARCOUNT */
    249   };
    250 
    251   bytes[0] = (unsigned char)(id >> 8);
    252   bytes[1] = (unsigned char)(id & 0xff);
    253 
    254   if(qlen > (sizeof(bytes) - 12))
    255     return -1;
    256 
    257   /* append query, includes QTYPE and QCLASS */
    258   memcpy(&bytes[12], qbuf, qlen);
    259 
    260   i = 12 + qlen;
    261 
    262   switch(qtype) {
    263   case QTYPE_A:
    264     bytes[7] = ancount_a;
    265     for(a = 0; a < ancount_a; a++) {
    266       const unsigned char *store = ipv4_pref;
    267       add_answer(bytes, &i, store, sizeof(ipv4_pref), QTYPE_A);
    268       logmsg("Sending back A (%x) '%s'", QTYPE_A,
    269              curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf)));
    270     }
    271     break;
    272   case QTYPE_AAAA:
    273     bytes[7] = ancount_aaaa;
    274     for(a = 0; a < ancount_aaaa; a++) {
    275       const unsigned char *store = ipv6_pref;
    276       add_answer(bytes, &i, store, sizeof(ipv6_pref), QTYPE_AAAA);
    277       logmsg("Sending back AAAA (%x) '%s'", QTYPE_AAAA,
    278              curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf)));
    279     }
    280     break;
    281   case QTYPE_HTTPS:
    282     bytes[7] = 1; /* one answer */
    283     break;
    284   }
    285 
    286 #ifdef __AMIGA__
    287   /* Amiga breakage */
    288   (void)rc;
    289   (void)sock;
    290   (void)addr;
    291   (void)addrlen;
    292   fprintf(stderr, "Not working\n");
    293   return -1;
    294 #else
    295   rc = sendto(sock, (const void *)bytes, (SENDTO3) i, 0, addr, addrlen);
    296   if(rc != (ssize_t)i) {
    297     fprintf(stderr, "failed sending %d bytes\n", (int)i);
    298   }
    299 #endif
    300   return 0;
    301 }
    302 
    303 
    304 static void read_instructions(void)
    305 {
    306   char file[256];
    307   FILE *f;
    308   snprintf(file, sizeof(file), "%s/" INSTRUCTIONS, logdir);
    309   f = fopen(file, FOPEN_READTEXT);
    310   if(f) {
    311     char buf[256];
    312     ancount_aaaa = ancount_a = 0;
    313     alpn_count = 0;
    314     while(fgets(buf, sizeof(buf), f)) {
    315       char *p = strchr(buf, '\n');
    316       if(p) {
    317         int rc;
    318         *p = 0;
    319         if(!strncmp("A: ", buf, 3)) {
    320           rc = curlx_inet_pton(AF_INET, &buf[3], ipv4_pref);
    321           ancount_a = (rc == 1);
    322         }
    323         else if(!strncmp("AAAA: ", buf, 6)) {
    324           char *p6 = &buf[6];
    325           if(*p6 == '[') {
    326             char *pt = strchr(p6, ']');
    327             if(pt)
    328               *pt = 0;
    329             p6++;
    330           }
    331           rc = curlx_inet_pton(AF_INET6, p6, ipv6_pref);
    332           ancount_aaaa = (rc == 1);
    333         }
    334         else if(!strncmp("ALPN: ", buf, 6)) {
    335           char *ap = &buf[6];
    336           rc = 0;
    337           while(*ap) {
    338             if('h' == *ap) {
    339               ap++;
    340               if(*ap >= '1' && *ap <= '3') {
    341                 if(alpn_count < MAX_ALPN)
    342                   alpn_pref[alpn_count++] = *ap;
    343               }
    344               else
    345                 break;
    346             }
    347             else
    348               break;
    349           }
    350         }
    351         else {
    352           rc = 0;
    353         }
    354         if(rc != 1) {
    355           logmsg("Bad line in %s: '%s'\n", file, buf);
    356         }
    357       }
    358     }
    359     fclose(f);
    360   }
    361   else
    362     logmsg("Error opening file '%s'", file);
    363 }
    364 
    365 static int test_dnsd(int argc, char **argv)
    366 {
    367   srvr_sockaddr_union_t me;
    368   ssize_t n = 0;
    369   int arg = 1;
    370   unsigned short port = 9123; /* UDP */
    371   curl_socket_t sock = CURL_SOCKET_BAD;
    372   int flag;
    373   int rc;
    374   int error;
    375   int result = 0;
    376 
    377   pidname = ".dnsd.pid";
    378   serverlogfile = "log/dnsd.log";
    379   serverlogslocked = 0;
    380 
    381   while(argc > arg) {
    382     if(!strcmp("--verbose", argv[arg])) {
    383       arg++;
    384       /* nothing yet */
    385     }
    386     else if(!strcmp("--version", argv[arg])) {
    387       printf("dnsd IPv4%s\n",
    388 #ifdef USE_IPV6
    389              "/IPv6"
    390 #else
    391              ""
    392 #endif
    393              );
    394       return 0;
    395     }
    396     else if(!strcmp("--pidfile", argv[arg])) {
    397       arg++;
    398       if(argc > arg)
    399         pidname = argv[arg++];
    400     }
    401     else if(!strcmp("--portfile", argv[arg])) {
    402       arg++;
    403       if(argc > arg)
    404         portname = argv[arg++];
    405     }
    406     else if(!strcmp("--logfile", argv[arg])) {
    407       arg++;
    408       if(argc > arg)
    409         serverlogfile = argv[arg++];
    410     }
    411     else if(!strcmp("--logdir", argv[arg])) {
    412       arg++;
    413       if(argc > arg)
    414         logdir = argv[arg++];
    415     }
    416     else if(!strcmp("--ipv4", argv[arg])) {
    417 #ifdef USE_IPV6
    418       ipv_inuse = "IPv4";
    419       use_ipv6 = FALSE;
    420 #endif
    421       arg++;
    422     }
    423     else if(!strcmp("--ipv6", argv[arg])) {
    424 #ifdef USE_IPV6
    425       ipv_inuse = "IPv6";
    426       use_ipv6 = TRUE;
    427 #endif
    428       arg++;
    429     }
    430     else if(!strcmp("--port", argv[arg])) {
    431       arg++;
    432       if(argc > arg) {
    433         char *endptr;
    434         unsigned long ulnum = strtoul(argv[arg], &endptr, 10);
    435         port = util_ultous(ulnum);
    436         arg++;
    437       }
    438     }
    439     else {
    440       if(argv[arg])
    441         fprintf(stderr, "unknown option: %s\n", argv[arg]);
    442       puts("Usage: dnsd [option]\n"
    443            " --version\n"
    444            " --logfile [file]\n"
    445            " --logdir [directory]\n"
    446            " --pidfile [file]\n"
    447            " --portfile [file]\n"
    448            " --ipv4\n"
    449            " --ipv6\n"
    450            " --port [port]\n");
    451       return 0;
    452     }
    453   }
    454 
    455   snprintf(loglockfile, sizeof(loglockfile), "%s/%s/dnsd-%s.lock",
    456             logdir, SERVERLOGS_LOCKDIR, ipv_inuse);
    457 
    458 #ifdef _WIN32
    459   if(win32_init())
    460     return 2;
    461 #endif
    462 
    463 #ifdef USE_IPV6
    464   if(!use_ipv6)
    465 #endif
    466     sock = socket(AF_INET, SOCK_DGRAM, 0);
    467 #ifdef USE_IPV6
    468   else
    469     sock = socket(AF_INET6, SOCK_DGRAM, 0);
    470 #endif
    471 
    472   if(CURL_SOCKET_BAD == sock) {
    473     error = SOCKERRNO;
    474     logmsg("Error creating socket (%d) %s", error, sstrerror(error));
    475     result = 1;
    476     goto dnsd_cleanup;
    477   }
    478 
    479   flag = 1;
    480   if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
    481             (void *)&flag, sizeof(flag))) {
    482     error = SOCKERRNO;
    483     logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s",
    484            error, sstrerror(error));
    485     result = 1;
    486     goto dnsd_cleanup;
    487   }
    488 
    489 #ifdef USE_IPV6
    490   if(!use_ipv6) {
    491 #endif
    492     memset(&me.sa4, 0, sizeof(me.sa4));
    493     me.sa4.sin_family = AF_INET;
    494     me.sa4.sin_addr.s_addr = INADDR_ANY;
    495     me.sa4.sin_port = htons(port);
    496     rc = bind(sock, &me.sa, sizeof(me.sa4));
    497 #ifdef USE_IPV6
    498   }
    499   else {
    500     memset(&me.sa6, 0, sizeof(me.sa6));
    501     me.sa6.sin6_family = AF_INET6;
    502     me.sa6.sin6_addr = in6addr_any;
    503     me.sa6.sin6_port = htons(port);
    504     rc = bind(sock, &me.sa, sizeof(me.sa6));
    505   }
    506 #endif /* USE_IPV6 */
    507   if(rc) {
    508     error = SOCKERRNO;
    509     logmsg("Error binding socket on port %hu (%d) %s", port, error,
    510            sstrerror(error));
    511     result = 1;
    512     goto dnsd_cleanup;
    513   }
    514 
    515   if(!port) {
    516     /* The system was supposed to choose a port number, figure out which
    517        port we actually got and update the listener port value with it. */
    518     curl_socklen_t la_size;
    519     srvr_sockaddr_union_t localaddr;
    520 #ifdef USE_IPV6
    521     if(!use_ipv6)
    522 #endif
    523       la_size = sizeof(localaddr.sa4);
    524 #ifdef USE_IPV6
    525     else
    526       la_size = sizeof(localaddr.sa6);
    527 #endif
    528     memset(&localaddr.sa, 0, (size_t)la_size);
    529     if(getsockname(sock, &localaddr.sa, &la_size) < 0) {
    530       error = SOCKERRNO;
    531       logmsg("getsockname() failed with error (%d) %s",
    532              error, sstrerror(error));
    533       sclose(sock);
    534       goto dnsd_cleanup;
    535     }
    536     switch(localaddr.sa.sa_family) {
    537     case AF_INET:
    538       port = ntohs(localaddr.sa4.sin_port);
    539       break;
    540 #ifdef USE_IPV6
    541     case AF_INET6:
    542       port = ntohs(localaddr.sa6.sin6_port);
    543       break;
    544 #endif
    545     default:
    546       break;
    547     }
    548     if(!port) {
    549       /* Real failure, listener port shall not be zero beyond this point. */
    550       logmsg("Apparently getsockname() succeeded, with listener port zero.");
    551       logmsg("A valid reason for this failure is a binary built without");
    552       logmsg("proper network library linkage. This might not be the only");
    553       logmsg("reason, but double check it before anything else.");
    554       result = 2;
    555       goto dnsd_cleanup;
    556     }
    557   }
    558 
    559   dnsd_wrotepidfile = write_pidfile(pidname);
    560   if(!dnsd_wrotepidfile) {
    561     result = 1;
    562     goto dnsd_cleanup;
    563   }
    564 
    565   if(portname) {
    566     dnsd_wroteportfile = write_portfile(portname, port);
    567     if(!dnsd_wroteportfile) {
    568       result = 1;
    569       goto dnsd_cleanup;
    570     }
    571   }
    572 
    573   logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port);
    574 
    575   for(;;) {
    576     unsigned short id = 0;
    577     unsigned char inbuffer[1500];
    578     srvr_sockaddr_union_t from;
    579     curl_socklen_t fromlen;
    580     unsigned char qbuf[256]; /* query storage */
    581     size_t qlen = 0; /* query size */
    582     unsigned short qtype = 0;
    583     fromlen = sizeof(from);
    584 #ifdef USE_IPV6
    585     if(!use_ipv6)
    586 #endif
    587       fromlen = sizeof(from.sa4);
    588 #ifdef USE_IPV6
    589     else
    590       fromlen = sizeof(from.sa6);
    591 #endif
    592     n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0,
    593                           &from.sa, &fromlen);
    594     if(got_exit_signal)
    595       break;
    596     if(n < 0) {
    597       logmsg("recvfrom");
    598       result = 3;
    599       break;
    600     }
    601 
    602     /* read once per incoming query, which is probably more than one
    603        per test case */
    604     read_instructions();
    605 
    606     store_incoming(inbuffer, n, qbuf, &qlen, &qtype, &id);
    607 
    608     set_advisor_read_lock(loglockfile);
    609     serverlogslocked = 1;
    610 
    611     send_response(sock, &from.sa, fromlen, qbuf, qlen, qtype, id);
    612 
    613     if(got_exit_signal)
    614       break;
    615 
    616     if(serverlogslocked) {
    617       serverlogslocked = 0;
    618       clear_advisor_read_lock(loglockfile);
    619     }
    620 
    621     logmsg("end of one transfer");
    622 
    623   }
    624 
    625 dnsd_cleanup:
    626 
    627 #if 0
    628   if((peer != sock) && (peer != CURL_SOCKET_BAD))
    629     sclose(peer);
    630 #endif
    631 
    632   if(sock != CURL_SOCKET_BAD)
    633     sclose(sock);
    634 
    635   if(got_exit_signal)
    636     logmsg("signalled to die");
    637 
    638   if(dnsd_wrotepidfile)
    639     unlink(pidname);
    640   if(dnsd_wroteportfile)
    641     unlink(portname);
    642 
    643   if(serverlogslocked) {
    644     serverlogslocked = 0;
    645     clear_advisor_read_lock(loglockfile);
    646   }
    647 
    648   restore_signal_handlers(true);
    649 
    650   if(got_exit_signal) {
    651     logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)",
    652            ipv_inuse, (int)port, (long)our_getpid(), exit_signal);
    653     /*
    654      * To properly set the return status of the process we
    655      * must raise the same signal SIGINT or SIGTERM that we
    656      * caught and let the old handler take care of it.
    657      */
    658     raise(exit_signal);
    659   }
    660 
    661   logmsg("========> dnsd quits");
    662   return result;
    663 }