Annotation of embedaddon/curl/lib/doh.c, revision 1.1.1.1

1.1       misho       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>