File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / doh.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    1: /***************************************************************************
    2:  *                                  _   _ ____  _
    3:  *  Project                     ___| | | |  _ \| |
    4:  *                             / __| | | | |_) | |
    5:  *                            | (__| |_| |  _ <| |___
    6:  *                             \___|\___/|_| \_\_____|
    7:  *
    8:  * Copyright (C) 2018 - 2020, 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.haxx.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:  ***************************************************************************/
   22: 
   23: #include "curl_setup.h"
   24: 
   25: #ifndef CURL_DISABLE_DOH
   26: 
   27: #include "urldata.h"
   28: #include "curl_addrinfo.h"
   29: #include "doh.h"
   30: 
   31: #include "sendf.h"
   32: #include "multiif.h"
   33: #include "url.h"
   34: #include "share.h"
   35: #include "curl_base64.h"
   36: #include "connect.h"
   37: #include "strdup.h"
   38: /* The last 3 #include files should be in this order */
   39: #include "curl_printf.h"
   40: #include "curl_memory.h"
   41: #include "memdebug.h"
   42: 
   43: #define DNS_CLASS_IN 0x01
   44: #define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
   45: 
   46: #ifndef CURL_DISABLE_VERBOSE_STRINGS
   47: static const char * const errors[]={
   48:   "",
   49:   "Bad label",
   50:   "Out of range",
   51:   "Label loop",
   52:   "Too small",
   53:   "Out of memory",
   54:   "RDATA length",
   55:   "Malformat",
   56:   "Bad RCODE",
   57:   "Unexpected TYPE",
   58:   "Unexpected CLASS",
   59:   "No content",
   60:   "Bad ID"
   61: };
   62: 
   63: static const char *doh_strerror(DOHcode code)
   64: {
   65:   if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
   66:     return errors[code];
   67:   return "bad error code";
   68: }
   69: #endif
   70: 
   71: #ifdef DEBUGBUILD
   72: #define UNITTEST
   73: #else
   74: #define UNITTEST static
   75: #endif
   76: 
   77: /* @unittest 1655
   78:  */
   79: UNITTEST DOHcode doh_encode(const char *host,
   80:                             DNStype dnstype,
   81:                             unsigned char *dnsp, /* buffer */
   82:                             size_t len,  /* buffer size */
   83:                             size_t *olen) /* output length */
   84: {
   85:   const size_t hostlen = strlen(host);
   86:   unsigned char *orig = dnsp;
   87:   const char *hostp = host;
   88: 
   89:   /* The expected output length is 16 bytes more than the length of
   90:    * the QNAME-encoding of the host name.
   91:    *
   92:    * A valid DNS name may not contain a zero-length label, except at
   93:    * the end.  For this reason, a name beginning with a dot, or
   94:    * containing a sequence of two or more consecutive dots, is invalid
   95:    * and cannot be encoded as a QNAME.
   96:    *
   97:    * If the host name ends with a trailing dot, the corresponding
   98:    * QNAME-encoding is one byte longer than the host name. If (as is
   99:    * also valid) the hostname is shortened by the omission of the
  100:    * trailing dot, then its QNAME-encoding will be two bytes longer
  101:    * than the host name.
  102:    *
  103:    * Each [ label, dot ] pair is encoded as [ length, label ],
  104:    * preserving overall length.  A final [ label ] without a dot is
  105:    * also encoded as [ length, label ], increasing overall length
  106:    * by one. The encoding is completed by appending a zero byte,
  107:    * representing the zero-length root label, again increasing
  108:    * the overall length by one.
  109:    */
  110: 
  111:   size_t expected_len;
  112:   DEBUGASSERT(hostlen);
  113:   expected_len = 12 + 1 + hostlen + 4;
  114:   if(host[hostlen-1]!='.')
  115:     expected_len++;
  116: 
  117:   if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
  118:     return DOH_DNS_NAME_TOO_LONG;
  119: 
  120:   if(len < expected_len)
  121:     return DOH_TOO_SMALL_BUFFER;
  122: 
  123:   *dnsp++ = 0; /* 16 bit id */
  124:   *dnsp++ = 0;
  125:   *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
  126:   *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
  127:   *dnsp++ = '\0';
  128:   *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
  129:   *dnsp++ = '\0';
  130:   *dnsp++ = '\0'; /* ANCOUNT */
  131:   *dnsp++ = '\0';
  132:   *dnsp++ = '\0'; /* NSCOUNT */
  133:   *dnsp++ = '\0';
  134:   *dnsp++ = '\0'; /* ARCOUNT */
  135: 
  136:   /* encode each label and store it in the QNAME */
  137:   while(*hostp) {
  138:     size_t labellen;
  139:     char *dot = strchr(hostp, '.');
  140:     if(dot)
  141:       labellen = dot - hostp;
  142:     else
  143:       labellen = strlen(hostp);
  144:     if((labellen > 63) || (!labellen)) {
  145:       /* label is too long or too short, error out */
  146:       *olen = 0;
  147:       return DOH_DNS_BAD_LABEL;
  148:     }
  149:     /* label is non-empty, process it */
  150:     *dnsp++ = (unsigned char)labellen;
  151:     memcpy(dnsp, hostp, labellen);
  152:     dnsp += labellen;
  153:     hostp += labellen;
  154:     /* advance past dot, but only if there is one */
  155:     if(dot)
  156:       hostp++;
  157:   } /* next label */
  158: 
  159:   *dnsp++ = 0; /* append zero-length label for root */
  160: 
  161:   /* There are assigned TYPE codes beyond 255: use range [1..65535]  */
  162:   *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
  163:   *dnsp++ = (unsigned char)(255 & dnstype);      /* lower 8 bit TYPE */
  164: 
  165:   *dnsp++ = '\0'; /* upper 8 bit CLASS */
  166:   *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
  167: 
  168:   *olen = dnsp - orig;
  169: 
  170:   /* verify that our estimation of length is valid, since
  171:    * this has led to buffer overflows in this function */
  172:   DEBUGASSERT(*olen == expected_len);
  173:   return DOH_OK;
  174: }
  175: 
  176: static size_t
  177: doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
  178: {
  179:   size_t realsize = size * nmemb;
  180:   struct dohresponse *mem = (struct dohresponse *)userp;
  181: 
  182:   if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
  183:     /* suspiciously much for us */
  184:     return 0;
  185: 
  186:   mem->memory = Curl_saferealloc(mem->memory, mem->size + realsize);
  187:   if(!mem->memory)
  188:     /* out of memory! */
  189:     return 0;
  190: 
  191:   memcpy(&(mem->memory[mem->size]), contents, realsize);
  192:   mem->size += realsize;
  193: 
  194:   return realsize;
  195: }
  196: 
  197: /* called from multi.c when this DOH transfer is complete */
  198: static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
  199: {
  200:   struct Curl_easy *data = doh->set.dohfor;
  201:   /* so one of the DOH request done for the 'data' transfer is now complete! */
  202:   data->req.doh.pending--;
  203:   infof(data, "a DOH request is completed, %u to go\n", data->req.doh.pending);
  204:   if(result)
  205:     infof(data, "DOH request %s\n", curl_easy_strerror(result));
  206: 
  207:   if(!data->req.doh.pending) {
  208:     /* DOH completed */
  209:     curl_slist_free_all(data->req.doh.headers);
  210:     data->req.doh.headers = NULL;
  211:     Curl_expire(data, 0, EXPIRE_RUN_NOW);
  212:   }
  213:   return 0;
  214: }
  215: 
  216: #define ERROR_CHECK_SETOPT(x,y) \
  217: do {                                      \
  218:   result = curl_easy_setopt(doh, x, y);   \
  219:   if(result)                              \
  220:     goto error;                           \
  221: } while(0)
  222: 
  223: static CURLcode dohprobe(struct Curl_easy *data,
  224:                          struct dnsprobe *p, DNStype dnstype,
  225:                          const char *host,
  226:                          const char *url, CURLM *multi,
  227:                          struct curl_slist *headers)
  228: {
  229:   struct Curl_easy *doh = NULL;
  230:   char *nurl = NULL;
  231:   CURLcode result = CURLE_OK;
  232:   timediff_t timeout_ms;
  233:   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
  234:                          &p->dohlen);
  235:   if(d) {
  236:     failf(data, "Failed to encode DOH packet [%d]\n", d);
  237:     return CURLE_OUT_OF_MEMORY;
  238:   }
  239: 
  240:   p->dnstype = dnstype;
  241:   p->serverdoh.memory = NULL;
  242:   /* the memory will be grown as needed by realloc in the doh_write_cb
  243:      function */
  244:   p->serverdoh.size = 0;
  245: 
  246:   /* Note: this is code for sending the DoH request with GET but there's still
  247:      no logic that actually enables this. We should either add that ability or
  248:      yank out the GET code. Discuss! */
  249:   if(data->set.doh_get) {
  250:     char *b64;
  251:     size_t b64len;
  252:     result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
  253:                                    &b64, &b64len);
  254:     if(result)
  255:       goto error;
  256:     nurl = aprintf("%s?dns=%s", url, b64);
  257:     free(b64);
  258:     if(!nurl) {
  259:       result = CURLE_OUT_OF_MEMORY;
  260:       goto error;
  261:     }
  262:     url = nurl;
  263:   }
  264: 
  265:   timeout_ms = Curl_timeleft(data, NULL, TRUE);
  266:   if(timeout_ms <= 0) {
  267:     result = CURLE_OPERATION_TIMEDOUT;
  268:     goto error;
  269:   }
  270:   /* Curl_open() is the internal version of curl_easy_init() */
  271:   result = Curl_open(&doh);
  272:   if(!result) {
  273:     /* pass in the struct pointer via a local variable to please coverity and
  274:        the gcc typecheck helpers */
  275:     struct dohresponse *resp = &p->serverdoh;
  276:     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
  277:     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
  278:     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
  279:     if(!data->set.doh_get) {
  280:       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
  281:       ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
  282:     }
  283:     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
  284: #ifdef USE_NGHTTP2
  285:     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
  286: #endif
  287: #ifndef CURLDEBUG
  288:     /* enforce HTTPS if not debug */
  289:     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
  290: #else
  291:     /* in debug mode, also allow http */
  292:     ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
  293: #endif
  294:     ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
  295:     if(data->set.verbose)
  296:       ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
  297:     if(data->set.no_signal)
  298:       ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
  299: 
  300:     /* Inherit *some* SSL options from the user's transfer. This is a
  301:        best-guess as to which options are needed for compatibility. #3661 */
  302:     if(data->set.ssl.falsestart)
  303:       ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
  304:     if(data->set.ssl.primary.verifyhost)
  305:       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYHOST, 2L);
  306: #ifndef CURL_DISABLE_PROXY
  307:     if(data->set.proxy_ssl.primary.verifyhost)
  308:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYHOST, 2L);
  309:     if(data->set.proxy_ssl.primary.verifypeer)
  310:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_VERIFYPEER, 1L);
  311:     if(data->set.str[STRING_SSL_CAFILE_PROXY]) {
  312:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAINFO,
  313:         data->set.str[STRING_SSL_CAFILE_PROXY]);
  314:     }
  315:     if(data->set.str[STRING_SSL_CRLFILE_PROXY]) {
  316:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CRLFILE,
  317:         data->set.str[STRING_SSL_CRLFILE_PROXY]);
  318:     }
  319:     if(data->set.proxy_ssl.no_revoke)
  320:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
  321:     else if(data->set.proxy_ssl.revoke_best_effort)
  322:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_SSL_OPTIONS,
  323:                          CURLSSLOPT_REVOKE_BEST_EFFORT);
  324:     if(data->set.str[STRING_SSL_CAPATH_PROXY]) {
  325:       ERROR_CHECK_SETOPT(CURLOPT_PROXY_CAPATH,
  326:         data->set.str[STRING_SSL_CAPATH_PROXY]);
  327:     }
  328: #endif
  329:     if(data->set.ssl.primary.verifypeer)
  330:       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYPEER, 1L);
  331:     if(data->set.ssl.primary.verifystatus)
  332:       ERROR_CHECK_SETOPT(CURLOPT_SSL_VERIFYSTATUS, 1L);
  333:     if(data->set.str[STRING_SSL_CAFILE_ORIG]) {
  334:       ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
  335:         data->set.str[STRING_SSL_CAFILE_ORIG]);
  336:     }
  337:     if(data->set.str[STRING_SSL_CAPATH_ORIG]) {
  338:       ERROR_CHECK_SETOPT(CURLOPT_CAPATH,
  339:         data->set.str[STRING_SSL_CAPATH_ORIG]);
  340:     }
  341:     if(data->set.str[STRING_SSL_CRLFILE_ORIG]) {
  342:       ERROR_CHECK_SETOPT(CURLOPT_CRLFILE,
  343:         data->set.str[STRING_SSL_CRLFILE_ORIG]);
  344:     }
  345:     if(data->set.ssl.certinfo)
  346:       ERROR_CHECK_SETOPT(CURLOPT_CERTINFO, 1L);
  347:     if(data->set.str[STRING_SSL_RANDOM_FILE]) {
  348:       ERROR_CHECK_SETOPT(CURLOPT_RANDOM_FILE,
  349:         data->set.str[STRING_SSL_RANDOM_FILE]);
  350:     }
  351:     if(data->set.str[STRING_SSL_EGDSOCKET]) {
  352:       ERROR_CHECK_SETOPT(CURLOPT_EGDSOCKET,
  353:         data->set.str[STRING_SSL_EGDSOCKET]);
  354:     }
  355:     if(data->set.ssl.no_revoke)
  356:       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
  357:     else if(data->set.ssl.revoke_best_effort)
  358:       ERROR_CHECK_SETOPT(CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
  359:     if(data->set.ssl.fsslctx)
  360:       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_FUNCTION, data->set.ssl.fsslctx);
  361:     if(data->set.ssl.fsslctxp)
  362:       ERROR_CHECK_SETOPT(CURLOPT_SSL_CTX_DATA, data->set.ssl.fsslctxp);
  363: 
  364:     doh->set.fmultidone = Curl_doh_done;
  365:     doh->set.dohfor = data; /* identify for which transfer this is done */
  366:     p->easy = doh;
  367: 
  368:     /* add this transfer to the multi handle */
  369:     if(curl_multi_add_handle(multi, doh))
  370:       goto error;
  371:   }
  372:   else
  373:     goto error;
  374:   free(nurl);
  375:   return CURLE_OK;
  376: 
  377:   error:
  378:   free(nurl);
  379:   Curl_close(&doh);
  380:   return result;
  381: }
  382: 
  383: /*
  384:  * Curl_doh() resolves a name using DOH. It resolves a name and returns a
  385:  * 'Curl_addrinfo *' with the address information.
  386:  */
  387: 
  388: Curl_addrinfo *Curl_doh(struct connectdata *conn,
  389:                         const char *hostname,
  390:                         int port,
  391:                         int *waitp)
  392: {
  393:   struct Curl_easy *data = conn->data;
  394:   CURLcode result = CURLE_OK;
  395:   int slot;
  396:   *waitp = TRUE; /* this never returns synchronously */
  397:   (void)conn;
  398:   (void)hostname;
  399:   (void)port;
  400: 
  401:   /* start clean, consider allocating this struct on demand */
  402:   memset(&data->req.doh, 0, sizeof(struct dohdata));
  403: 
  404:   data->req.doh.host = hostname;
  405:   data->req.doh.port = port;
  406:   data->req.doh.headers =
  407:     curl_slist_append(NULL,
  408:                       "Content-Type: application/dns-message");
  409:   if(!data->req.doh.headers)
  410:     goto error;
  411: 
  412:   if(conn->ip_version != CURL_IPRESOLVE_V6) {
  413:     /* create IPv4 DOH request */
  414:     result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4],
  415:                       DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
  416:                       data->multi, data->req.doh.headers);
  417:     if(result)
  418:       goto error;
  419:     data->req.doh.pending++;
  420:   }
  421: 
  422:   if(conn->ip_version != CURL_IPRESOLVE_V4) {
  423:     /* create IPv6 DOH request */
  424:     result = dohprobe(data, &data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6],
  425:                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
  426:                       data->multi, data->req.doh.headers);
  427:     if(result)
  428:       goto error;
  429:     data->req.doh.pending++;
  430:   }
  431:   return NULL;
  432: 
  433:   error:
  434:   curl_slist_free_all(data->req.doh.headers);
  435:   data->req.doh.headers = NULL;
  436:   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
  437:     Curl_close(&data->req.doh.probe[slot].easy);
  438:   }
  439:   return NULL;
  440: }
  441: 
  442: static DOHcode skipqname(const unsigned char *doh, size_t dohlen,
  443:                          unsigned int *indexp)
  444: {
  445:   unsigned char length;
  446:   do {
  447:     if(dohlen < (*indexp + 1))
  448:       return DOH_DNS_OUT_OF_RANGE;
  449:     length = doh[*indexp];
  450:     if((length & 0xc0) == 0xc0) {
  451:       /* name pointer, advance over it and be done */
  452:       if(dohlen < (*indexp + 2))
  453:         return DOH_DNS_OUT_OF_RANGE;
  454:       *indexp += 2;
  455:       break;
  456:     }
  457:     if(length & 0xc0)
  458:       return DOH_DNS_BAD_LABEL;
  459:     if(dohlen < (*indexp + 1 + length))
  460:       return DOH_DNS_OUT_OF_RANGE;
  461:     *indexp += 1 + length;
  462:   } while(length);
  463:   return DOH_OK;
  464: }
  465: 
  466: static unsigned short get16bit(const unsigned char *doh, int index)
  467: {
  468:   return (unsigned short)((doh[index] << 8) | doh[index + 1]);
  469: }
  470: 
  471: static unsigned int get32bit(const unsigned char *doh, int index)
  472: {
  473:    /* make clang and gcc optimize this to bswap by incrementing
  474:       the pointer first. */
  475:    doh += index;
  476: 
  477:    /* avoid undefined behaviour by casting to unsigned before shifting
  478:       24 bits, possibly into the sign bit. codegen is same, but
  479:       ub sanitizer won't be upset */
  480:   return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
  481: }
  482: 
  483: static DOHcode store_a(const unsigned char *doh, int index, struct dohentry *d)
  484: {
  485:   /* silently ignore addresses over the limit */
  486:   if(d->numaddr < DOH_MAX_ADDR) {
  487:     struct dohaddr *a = &d->addr[d->numaddr];
  488:     a->type = DNS_TYPE_A;
  489:     memcpy(&a->ip.v4, &doh[index], 4);
  490:     d->numaddr++;
  491:   }
  492:   return DOH_OK;
  493: }
  494: 
  495: static DOHcode store_aaaa(const unsigned char *doh,
  496:                           int index,
  497:                           struct dohentry *d)
  498: {
  499:   /* silently ignore addresses over the limit */
  500:   if(d->numaddr < DOH_MAX_ADDR) {
  501:     struct dohaddr *a = &d->addr[d->numaddr];
  502:     a->type = DNS_TYPE_AAAA;
  503:     memcpy(&a->ip.v6, &doh[index], 16);
  504:     d->numaddr++;
  505:   }
  506:   return DOH_OK;
  507: }
  508: 
  509: static DOHcode cnameappend(struct cnamestore *c,
  510:                            const unsigned char *src,
  511:                            size_t len)
  512: {
  513:   if(!c->alloc) {
  514:     c->allocsize = len + 1;
  515:     c->alloc = malloc(c->allocsize);
  516:     if(!c->alloc)
  517:       return DOH_OUT_OF_MEM;
  518:   }
  519:   else if(c->allocsize < (c->allocsize + len + 1)) {
  520:     char *ptr;
  521:     c->allocsize += len + 1;
  522:     ptr = realloc(c->alloc, c->allocsize);
  523:     if(!ptr) {
  524:       free(c->alloc);
  525:       return DOH_OUT_OF_MEM;
  526:     }
  527:     c->alloc = ptr;
  528:   }
  529:   memcpy(&c->alloc[c->len], src, len);
  530:   c->len += len;
  531:   c->alloc[c->len] = 0; /* keep it zero terminated */
  532:   return DOH_OK;
  533: }
  534: 
  535: static DOHcode store_cname(const unsigned char *doh,
  536:                            size_t dohlen,
  537:                            unsigned int index,
  538:                            struct dohentry *d)
  539: {
  540:   struct cnamestore *c;
  541:   unsigned int loop = 128; /* a valid DNS name can never loop this much */
  542:   unsigned char length;
  543: 
  544:   if(d->numcname == DOH_MAX_CNAME)
  545:     return DOH_OK; /* skip! */
  546: 
  547:   c = &d->cname[d->numcname++];
  548:   do {
  549:     if(index >= dohlen)
  550:       return DOH_DNS_OUT_OF_RANGE;
  551:     length = doh[index];
  552:     if((length & 0xc0) == 0xc0) {
  553:       int newpos;
  554:       /* name pointer, get the new offset (14 bits) */
  555:       if((index + 1) >= dohlen)
  556:         return DOH_DNS_OUT_OF_RANGE;
  557: 
  558:       /* move to the new index */
  559:       newpos = (length & 0x3f) << 8 | doh[index + 1];
  560:       index = newpos;
  561:       continue;
  562:     }
  563:     else if(length & 0xc0)
  564:       return DOH_DNS_BAD_LABEL; /* bad input */
  565:     else
  566:       index++;
  567: 
  568:     if(length) {
  569:       DOHcode rc;
  570:       if(c->len) {
  571:         rc = cnameappend(c, (unsigned char *)".", 1);
  572:         if(rc)
  573:           return rc;
  574:       }
  575:       if((index + length) > dohlen)
  576:         return DOH_DNS_BAD_LABEL;
  577: 
  578:       rc = cnameappend(c, &doh[index], length);
  579:       if(rc)
  580:         return rc;
  581:       index += length;
  582:     }
  583:   } while(length && --loop);
  584: 
  585:   if(!loop)
  586:     return DOH_DNS_LABEL_LOOP;
  587:   return DOH_OK;
  588: }
  589: 
  590: static DOHcode rdata(const unsigned char *doh,
  591:                      size_t dohlen,
  592:                      unsigned short rdlength,
  593:                      unsigned short type,
  594:                      int index,
  595:                      struct dohentry *d)
  596: {
  597:   /* RDATA
  598:      - A (TYPE 1):  4 bytes
  599:      - AAAA (TYPE 28): 16 bytes
  600:      - NS (TYPE 2): N bytes */
  601:   DOHcode rc;
  602: 
  603:   switch(type) {
  604:   case DNS_TYPE_A:
  605:     if(rdlength != 4)
  606:       return DOH_DNS_RDATA_LEN;
  607:     rc = store_a(doh, index, d);
  608:     if(rc)
  609:       return rc;
  610:     break;
  611:   case DNS_TYPE_AAAA:
  612:     if(rdlength != 16)
  613:       return DOH_DNS_RDATA_LEN;
  614:     rc = store_aaaa(doh, index, d);
  615:     if(rc)
  616:       return rc;
  617:     break;
  618:   case DNS_TYPE_CNAME:
  619:     rc = store_cname(doh, dohlen, index, d);
  620:     if(rc)
  621:       return rc;
  622:     break;
  623:   case DNS_TYPE_DNAME:
  624:     /* explicit for clarity; just skip; rely on synthesized CNAME  */
  625:     break;
  626:   default:
  627:     /* unsupported type, just skip it */
  628:     break;
  629:   }
  630:   return DOH_OK;
  631: }
  632: 
  633: static void init_dohentry(struct dohentry *de)
  634: {
  635:   memset(de, 0, sizeof(*de));
  636:   de->ttl = INT_MAX;
  637: }
  638: 
  639: 
  640: UNITTEST DOHcode doh_decode(const unsigned char *doh,
  641:                             size_t dohlen,
  642:                             DNStype dnstype,
  643:                             struct dohentry *d)
  644: {
  645:   unsigned char rcode;
  646:   unsigned short qdcount;
  647:   unsigned short ancount;
  648:   unsigned short type = 0;
  649:   unsigned short rdlength;
  650:   unsigned short nscount;
  651:   unsigned short arcount;
  652:   unsigned int index = 12;
  653:   DOHcode rc;
  654: 
  655:   if(dohlen < 12)
  656:     return DOH_TOO_SMALL_BUFFER; /* too small */
  657:   if(!doh || doh[0] || doh[1])
  658:     return DOH_DNS_BAD_ID; /* bad ID */
  659:   rcode = doh[3] & 0x0f;
  660:   if(rcode)
  661:     return DOH_DNS_BAD_RCODE; /* bad rcode */
  662: 
  663:   qdcount = get16bit(doh, 4);
  664:   while(qdcount) {
  665:     rc = skipqname(doh, dohlen, &index);
  666:     if(rc)
  667:       return rc; /* bad qname */
  668:     if(dohlen < (index + 4))
  669:       return DOH_DNS_OUT_OF_RANGE;
  670:     index += 4; /* skip question's type and class */
  671:     qdcount--;
  672:   }
  673: 
  674:   ancount = get16bit(doh, 6);
  675:   while(ancount) {
  676:     unsigned short class;
  677:     unsigned int ttl;
  678: 
  679:     rc = skipqname(doh, dohlen, &index);
  680:     if(rc)
  681:       return rc; /* bad qname */
  682: 
  683:     if(dohlen < (index + 2))
  684:       return DOH_DNS_OUT_OF_RANGE;
  685: 
  686:     type = get16bit(doh, index);
  687:     if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
  688:        && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
  689:        && (type != dnstype))
  690:       /* Not the same type as was asked for nor CNAME nor DNAME */
  691:       return DOH_DNS_UNEXPECTED_TYPE;
  692:     index += 2;
  693: 
  694:     if(dohlen < (index + 2))
  695:       return DOH_DNS_OUT_OF_RANGE;
  696:     class = get16bit(doh, index);
  697:     if(DNS_CLASS_IN != class)
  698:       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
  699:     index += 2;
  700: 
  701:     if(dohlen < (index + 4))
  702:       return DOH_DNS_OUT_OF_RANGE;
  703: 
  704:     ttl = get32bit(doh, index);
  705:     if(ttl < d->ttl)
  706:       d->ttl = ttl;
  707:     index += 4;
  708: 
  709:     if(dohlen < (index + 2))
  710:       return DOH_DNS_OUT_OF_RANGE;
  711: 
  712:     rdlength = get16bit(doh, index);
  713:     index += 2;
  714:     if(dohlen < (index + rdlength))
  715:       return DOH_DNS_OUT_OF_RANGE;
  716: 
  717:     rc = rdata(doh, dohlen, rdlength, type, index, d);
  718:     if(rc)
  719:       return rc; /* bad rdata */
  720:     index += rdlength;
  721:     ancount--;
  722:   }
  723: 
  724:   nscount = get16bit(doh, 8);
  725:   while(nscount) {
  726:     rc = skipqname(doh, dohlen, &index);
  727:     if(rc)
  728:       return rc; /* bad qname */
  729: 
  730:     if(dohlen < (index + 8))
  731:       return DOH_DNS_OUT_OF_RANGE;
  732: 
  733:     index += 2 + 2 + 4; /* type, class and ttl */
  734: 
  735:     if(dohlen < (index + 2))
  736:       return DOH_DNS_OUT_OF_RANGE;
  737: 
  738:     rdlength = get16bit(doh, index);
  739:     index += 2;
  740:     if(dohlen < (index + rdlength))
  741:       return DOH_DNS_OUT_OF_RANGE;
  742:     index += rdlength;
  743:     nscount--;
  744:   }
  745: 
  746:   arcount = get16bit(doh, 10);
  747:   while(arcount) {
  748:     rc = skipqname(doh, dohlen, &index);
  749:     if(rc)
  750:       return rc; /* bad qname */
  751: 
  752:     if(dohlen < (index + 8))
  753:       return DOH_DNS_OUT_OF_RANGE;
  754: 
  755:     index += 2 + 2 + 4; /* type, class and ttl */
  756: 
  757:     if(dohlen < (index + 2))
  758:       return DOH_DNS_OUT_OF_RANGE;
  759: 
  760:     rdlength = get16bit(doh, index);
  761:     index += 2;
  762:     if(dohlen < (index + rdlength))
  763:       return DOH_DNS_OUT_OF_RANGE;
  764:     index += rdlength;
  765:     arcount--;
  766:   }
  767: 
  768:   if(index != dohlen)
  769:     return DOH_DNS_MALFORMAT; /* something is wrong */
  770: 
  771:   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
  772:     /* nothing stored! */
  773:     return DOH_NO_CONTENT;
  774: 
  775:   return DOH_OK; /* ok */
  776: }
  777: 
  778: #ifndef CURL_DISABLE_VERBOSE_STRINGS
  779: static void showdoh(struct Curl_easy *data,
  780:                     const struct dohentry *d)
  781: {
  782:   int i;
  783:   infof(data, "TTL: %u seconds\n", d->ttl);
  784:   for(i = 0; i < d->numaddr; i++) {
  785:     const struct dohaddr *a = &d->addr[i];
  786:     if(a->type == DNS_TYPE_A) {
  787:       infof(data, "DOH A: %u.%u.%u.%u\n",
  788:             a->ip.v4[0], a->ip.v4[1],
  789:             a->ip.v4[2], a->ip.v4[3]);
  790:     }
  791:     else if(a->type == DNS_TYPE_AAAA) {
  792:       int j;
  793:       char buffer[128];
  794:       char *ptr;
  795:       size_t len;
  796:       msnprintf(buffer, 128, "DOH AAAA: ");
  797:       ptr = &buffer[10];
  798:       len = 118;
  799:       for(j = 0; j < 16; j += 2) {
  800:         size_t l;
  801:         msnprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
  802:                   d->addr[i].ip.v6[j + 1]);
  803:         l = strlen(ptr);
  804:         len -= l;
  805:         ptr += l;
  806:       }
  807:       infof(data, "%s\n", buffer);
  808:     }
  809:   }
  810:   for(i = 0; i < d->numcname; i++) {
  811:     infof(data, "CNAME: %s\n", d->cname[i].alloc);
  812:   }
  813: }
  814: #else
  815: #define showdoh(x,y)
  816: #endif
  817: 
  818: /*
  819:  * doh2ai()
  820:  *
  821:  * This function returns a pointer to the first element of a newly allocated
  822:  * Curl_addrinfo struct linked list filled with the data from a set of DOH
  823:  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
  824:  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
  825:  *
  826:  * The memory allocated by this function *MUST* be free'd later on calling
  827:  * Curl_freeaddrinfo().  For each successful call to this function there
  828:  * must be an associated call later to Curl_freeaddrinfo().
  829:  */
  830: 
  831: static Curl_addrinfo *
  832: doh2ai(const struct dohentry *de, const char *hostname, int port)
  833: {
  834:   Curl_addrinfo *ai;
  835:   Curl_addrinfo *prevai = NULL;
  836:   Curl_addrinfo *firstai = NULL;
  837:   struct sockaddr_in *addr;
  838: #ifdef ENABLE_IPV6
  839:   struct sockaddr_in6 *addr6;
  840: #endif
  841:   CURLcode result = CURLE_OK;
  842:   int i;
  843: 
  844:   if(!de)
  845:     /* no input == no output! */
  846:     return NULL;
  847: 
  848:   for(i = 0; i < de->numaddr; i++) {
  849:     size_t ss_size;
  850:     CURL_SA_FAMILY_T addrtype;
  851:     if(de->addr[i].type == DNS_TYPE_AAAA) {
  852: #ifndef ENABLE_IPV6
  853:       /* we can't handle IPv6 addresses */
  854:       continue;
  855: #else
  856:       ss_size = sizeof(struct sockaddr_in6);
  857:       addrtype = AF_INET6;
  858: #endif
  859:     }
  860:     else {
  861:       ss_size = sizeof(struct sockaddr_in);
  862:       addrtype = AF_INET;
  863:     }
  864: 
  865:     ai = calloc(1, sizeof(Curl_addrinfo));
  866:     if(!ai) {
  867:       result = CURLE_OUT_OF_MEMORY;
  868:       break;
  869:     }
  870:     ai->ai_canonname = strdup(hostname);
  871:     if(!ai->ai_canonname) {
  872:       result = CURLE_OUT_OF_MEMORY;
  873:       free(ai);
  874:       break;
  875:     }
  876:     ai->ai_addr = calloc(1, ss_size);
  877:     if(!ai->ai_addr) {
  878:       result = CURLE_OUT_OF_MEMORY;
  879:       free(ai->ai_canonname);
  880:       free(ai);
  881:       break;
  882:     }
  883: 
  884:     if(!firstai)
  885:       /* store the pointer we want to return from this function */
  886:       firstai = ai;
  887: 
  888:     if(prevai)
  889:       /* make the previous entry point to this */
  890:       prevai->ai_next = ai;
  891: 
  892:     ai->ai_family = addrtype;
  893: 
  894:     /* we return all names as STREAM, so when using this address for TFTP
  895:        the type must be ignored and conn->socktype be used instead! */
  896:     ai->ai_socktype = SOCK_STREAM;
  897: 
  898:     ai->ai_addrlen = (curl_socklen_t)ss_size;
  899: 
  900:     /* leave the rest of the struct filled with zero */
  901: 
  902:     switch(ai->ai_family) {
  903:     case AF_INET:
  904:       addr = (void *)ai->ai_addr; /* storage area for this info */
  905:       DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
  906:       memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
  907:       addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
  908:       addr->sin_port = htons((unsigned short)port);
  909:       break;
  910: 
  911: #ifdef ENABLE_IPV6
  912:     case AF_INET6:
  913:       addr6 = (void *)ai->ai_addr; /* storage area for this info */
  914:       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
  915:       memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
  916:       addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
  917:       addr6->sin6_port = htons((unsigned short)port);
  918:       break;
  919: #endif
  920:     }
  921: 
  922:     prevai = ai;
  923:   }
  924: 
  925:   if(result) {
  926:     Curl_freeaddrinfo(firstai);
  927:     firstai = NULL;
  928:   }
  929: 
  930:   return firstai;
  931: }
  932: 
  933: #ifndef CURL_DISABLE_VERBOSE_STRINGS
  934: static const char *type2name(DNStype dnstype)
  935: {
  936:   return (dnstype == DNS_TYPE_A)?"A":"AAAA";
  937: }
  938: #endif
  939: 
  940: UNITTEST void de_cleanup(struct dohentry *d)
  941: {
  942:   int i = 0;
  943:   for(i = 0; i < d->numcname; i++) {
  944:     free(d->cname[i].alloc);
  945:   }
  946: }
  947: 
  948: CURLcode Curl_doh_is_resolved(struct connectdata *conn,
  949:                               struct Curl_dns_entry **dnsp)
  950: {
  951:   CURLcode result;
  952:   struct Curl_easy *data = conn->data;
  953:   *dnsp = NULL; /* defaults to no response */
  954: 
  955:   if(!data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
  956:      !data->req.doh.probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
  957:     failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
  958:     return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
  959:       CURLE_COULDNT_RESOLVE_HOST;
  960:   }
  961:   else if(!data->req.doh.pending) {
  962:     DOHcode rc[DOH_PROBE_SLOTS];
  963:     struct dohentry de;
  964:     int slot;
  965:     /* remove DOH handles from multi handle and close them */
  966:     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
  967:       curl_multi_remove_handle(data->multi, data->req.doh.probe[slot].easy);
  968:       Curl_close(&data->req.doh.probe[slot].easy);
  969:     }
  970:     /* parse the responses, create the struct and return it! */
  971:     init_dohentry(&de);
  972:     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
  973:       rc[slot] = doh_decode(data->req.doh.probe[slot].serverdoh.memory,
  974:                             data->req.doh.probe[slot].serverdoh.size,
  975:                             data->req.doh.probe[slot].dnstype,
  976:                             &de);
  977:       Curl_safefree(data->req.doh.probe[slot].serverdoh.memory);
  978:       if(rc[slot]) {
  979:         infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
  980:               type2name(data->req.doh.probe[slot].dnstype),
  981:               data->req.doh.host);
  982:       }
  983:     } /* next slot */
  984: 
  985:     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
  986:     if(!rc[DOH_PROBE_SLOT_IPADDR_V4] || !rc[DOH_PROBE_SLOT_IPADDR_V6]) {
  987:       /* we have an address, of one kind or other */
  988:       struct Curl_dns_entry *dns;
  989:       struct Curl_addrinfo *ai;
  990: 
  991:       infof(data, "DOH Host name: %s\n", data->req.doh.host);
  992:       showdoh(data, &de);
  993: 
  994:       ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
  995:       if(!ai) {
  996:         de_cleanup(&de);
  997:         return CURLE_OUT_OF_MEMORY;
  998:       }
  999: 
 1000:       if(data->share)
 1001:         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 1002: 
 1003:       /* we got a response, store it in the cache */
 1004:       dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
 1005: 
 1006:       if(data->share)
 1007:         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
 1008: 
 1009:       if(!dns) {
 1010:         /* returned failure, bail out nicely */
 1011:         Curl_freeaddrinfo(ai);
 1012:       }
 1013:       else {
 1014:         conn->async.dns = dns;
 1015:         *dnsp = dns;
 1016:         result = CURLE_OK;      /* address resolution OK */
 1017:       }
 1018:     } /* address processing done */
 1019: 
 1020:     /* Now process any build-specific attributes retrieved from DNS */
 1021: 
 1022:     /* All done */
 1023:     de_cleanup(&de);
 1024:     return result;
 1025: 
 1026:   } /* !data->req.doh.pending */
 1027: 
 1028:   /* else wait for pending DOH transactions to complete */
 1029:   return CURLE_OK;
 1030: }
 1031: 
 1032: #endif /* CURL_DISABLE_DOH */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>