Annotation of embedaddon/curl/lib/doh.c, revision 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>