113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci#include "curlcheck.h"
2513498266Sopenharmony_ci
2613498266Sopenharmony_ci#include "doh.h" /* from the lib dir */
2713498266Sopenharmony_ci
2813498266Sopenharmony_cistatic CURLcode unit_setup(void)
2913498266Sopenharmony_ci{
3013498266Sopenharmony_ci  /* whatever you want done first */
3113498266Sopenharmony_ci  return CURLE_OK;
3213498266Sopenharmony_ci}
3313498266Sopenharmony_ci
3413498266Sopenharmony_cistatic void unit_stop(void)
3513498266Sopenharmony_ci{
3613498266Sopenharmony_ci    /* done before shutting down and exiting */
3713498266Sopenharmony_ci}
3813498266Sopenharmony_ci
3913498266Sopenharmony_ci#ifndef CURL_DISABLE_DOH
4013498266Sopenharmony_ci
4113498266Sopenharmony_ciUNITTEST_START
4213498266Sopenharmony_ci
4313498266Sopenharmony_ci/*
4413498266Sopenharmony_ci * Prove detection of write overflow using a short buffer and a name
4513498266Sopenharmony_ci * of maximal valid length.
4613498266Sopenharmony_ci *
4713498266Sopenharmony_ci * Prove detection of other invalid input.
4813498266Sopenharmony_ci */
4913498266Sopenharmony_cido {
5013498266Sopenharmony_ci  static const char max[] =
5113498266Sopenharmony_ci    /* ..|....1.........2.........3.........4.........5.........6... */
5213498266Sopenharmony_ci    /* 3456789012345678901234567890123456789012345678901234567890123 */
5313498266Sopenharmony_ci    "this.is.a.maximum-length.hostname."                  /* 34:  34 */
5413498266Sopenharmony_ci    "with-no-label-of-greater-length-than-the-sixty-three-characters."
5513498266Sopenharmony_ci                                                          /* 64:  98 */
5613498266Sopenharmony_ci    "specified.in.the.RFCs."                              /* 22: 120 */
5713498266Sopenharmony_ci    "and.with.a.QNAME.encoding.whose.length.is.exactly."  /* 50: 170 */
5813498266Sopenharmony_ci    "the.maximum.length.allowed."                         /* 27: 197 */
5913498266Sopenharmony_ci    "that.is.two-hundred.and.fifty-six."                  /* 34: 231 */
6013498266Sopenharmony_ci    "including.the.last.null."                            /* 24: 255 */
6113498266Sopenharmony_ci    "";
6213498266Sopenharmony_ci  static const char toolong[] =
6313498266Sopenharmony_ci    /* ..|....1.........2.........3.........4.........5.........6... */
6413498266Sopenharmony_ci    /* 3456789012345678901234567890123456789012345678901234567890123 */
6513498266Sopenharmony_ci    "here.is.a.hostname.which.is.just.barely.too.long."   /* 49:  49 */
6613498266Sopenharmony_ci    "to.be.encoded.as.a.QNAME.of.the.maximum.allowed.length."
6713498266Sopenharmony_ci                                                          /* 55: 104 */
6813498266Sopenharmony_ci    "which.is.256.including.a.final.zero-length.label."   /* 49: 153 */
6913498266Sopenharmony_ci    "representing.the.root.node.so.that.a.name.with."     /* 47: 200 */
7013498266Sopenharmony_ci    "a.trailing.dot.may.have.up.to."                      /* 30: 230 */
7113498266Sopenharmony_ci    "255.characters.never.more."                          /* 26: 256 */
7213498266Sopenharmony_ci    "";
7313498266Sopenharmony_ci  static const char emptylabel[] =
7413498266Sopenharmony_ci    "this.is.an.otherwise-valid.hostname."
7513498266Sopenharmony_ci    ".with.an.empty.label.";
7613498266Sopenharmony_ci  static const char outsizelabel[] =
7713498266Sopenharmony_ci    "this.is.an.otherwise-valid.hostname."
7813498266Sopenharmony_ci    "with-a-label-of-greater-length-than-the-sixty-three-characters-"
7913498266Sopenharmony_ci    "specified.in.the.RFCs.";
8013498266Sopenharmony_ci  int i;
8113498266Sopenharmony_ci
8213498266Sopenharmony_ci  struct test {
8313498266Sopenharmony_ci    const char *name;
8413498266Sopenharmony_ci    const DOHcode expected_result;
8513498266Sopenharmony_ci  };
8613498266Sopenharmony_ci
8713498266Sopenharmony_ci  /* plays the role of struct dnsprobe in urldata.h */
8813498266Sopenharmony_ci  struct demo {
8913498266Sopenharmony_ci    unsigned char dohbuffer[255 + 16]; /* deliberately short buffer */
9013498266Sopenharmony_ci    unsigned char canary1;
9113498266Sopenharmony_ci    unsigned char canary2;
9213498266Sopenharmony_ci    unsigned char canary3;
9313498266Sopenharmony_ci  };
9413498266Sopenharmony_ci
9513498266Sopenharmony_ci  const struct test playlist[4] = {
9613498266Sopenharmony_ci    { toolong, DOH_DNS_NAME_TOO_LONG },  /* expect early failure */
9713498266Sopenharmony_ci    { emptylabel, DOH_DNS_BAD_LABEL },   /* also */
9813498266Sopenharmony_ci    { outsizelabel, DOH_DNS_BAD_LABEL }, /* also */
9913498266Sopenharmony_ci    { max, DOH_OK }                      /* expect buffer overwrite */
10013498266Sopenharmony_ci  };
10113498266Sopenharmony_ci
10213498266Sopenharmony_ci  for(i = 0; i < (int)(sizeof(playlist)/sizeof(*playlist)); i++) {
10313498266Sopenharmony_ci    const char *name = playlist[i].name;
10413498266Sopenharmony_ci    size_t olen = 100000;
10513498266Sopenharmony_ci    struct demo victim;
10613498266Sopenharmony_ci    DOHcode d;
10713498266Sopenharmony_ci
10813498266Sopenharmony_ci    victim.canary1 = 87; /* magic numbers, arbitrarily picked */
10913498266Sopenharmony_ci    victim.canary2 = 35;
11013498266Sopenharmony_ci    victim.canary3 = 41;
11113498266Sopenharmony_ci    d = doh_encode(name, DNS_TYPE_A, victim.dohbuffer,
11213498266Sopenharmony_ci                   sizeof(struct demo), /* allow room for overflow */
11313498266Sopenharmony_ci                   &olen);
11413498266Sopenharmony_ci
11513498266Sopenharmony_ci    fail_unless(d == playlist[i].expected_result,
11613498266Sopenharmony_ci                "result returned was not as expected");
11713498266Sopenharmony_ci    if(d == playlist[i].expected_result) {
11813498266Sopenharmony_ci      if(name == max) {
11913498266Sopenharmony_ci        fail_if(victim.canary1 == 87,
12013498266Sopenharmony_ci                "demo one-byte buffer overwrite did not happen");
12113498266Sopenharmony_ci      }
12213498266Sopenharmony_ci      else {
12313498266Sopenharmony_ci        fail_unless(victim.canary1 == 87,
12413498266Sopenharmony_ci                    "one-byte buffer overwrite has happened");
12513498266Sopenharmony_ci      }
12613498266Sopenharmony_ci      fail_unless(victim.canary2 == 35,
12713498266Sopenharmony_ci                  "two-byte buffer overwrite has happened");
12813498266Sopenharmony_ci      fail_unless(victim.canary3 == 41,
12913498266Sopenharmony_ci                  "three-byte buffer overwrite has happened");
13013498266Sopenharmony_ci    }
13113498266Sopenharmony_ci    else {
13213498266Sopenharmony_ci      if(d == DOH_OK) {
13313498266Sopenharmony_ci        fail_unless(olen <= sizeof(victim.dohbuffer), "wrote outside bounds");
13413498266Sopenharmony_ci        fail_unless(olen > strlen(name), "unrealistic low size");
13513498266Sopenharmony_ci      }
13613498266Sopenharmony_ci    }
13713498266Sopenharmony_ci  }
13813498266Sopenharmony_ci} while(0);
13913498266Sopenharmony_ci
14013498266Sopenharmony_ci/* run normal cases and try to trigger buffer length related errors */
14113498266Sopenharmony_cido {
14213498266Sopenharmony_ci  DNStype dnstype = DNS_TYPE_A;
14313498266Sopenharmony_ci  unsigned char buffer[128];
14413498266Sopenharmony_ci  const size_t buflen = sizeof(buffer);
14513498266Sopenharmony_ci  const size_t magic1 = 9765;
14613498266Sopenharmony_ci  size_t olen1 = magic1;
14713498266Sopenharmony_ci  const char *sunshine1 = "a.com";
14813498266Sopenharmony_ci  const char *dotshine1 = "a.com.";
14913498266Sopenharmony_ci  const char *sunshine2 = "aa.com";
15013498266Sopenharmony_ci  size_t olen2;
15113498266Sopenharmony_ci  DOHcode ret2;
15213498266Sopenharmony_ci  size_t olen;
15313498266Sopenharmony_ci
15413498266Sopenharmony_ci  DOHcode ret = doh_encode(sunshine1, dnstype, buffer, buflen, &olen1);
15513498266Sopenharmony_ci  fail_unless(ret == DOH_OK, "sunshine case 1 should pass fine");
15613498266Sopenharmony_ci  fail_if(olen1 == magic1, "olen has not been assigned properly");
15713498266Sopenharmony_ci  fail_unless(olen1 > strlen(sunshine1), "bad out length");
15813498266Sopenharmony_ci
15913498266Sopenharmony_ci  /* with a trailing dot, the response should have the same length */
16013498266Sopenharmony_ci  olen2 = magic1;
16113498266Sopenharmony_ci  ret2 = doh_encode(dotshine1, dnstype, buffer, buflen, &olen2);
16213498266Sopenharmony_ci  fail_unless(ret2 == DOH_OK, "dotshine case should pass fine");
16313498266Sopenharmony_ci  fail_if(olen2 == magic1, "olen has not been assigned properly");
16413498266Sopenharmony_ci  fail_unless(olen1 == olen2, "olen should not grow for a trailing dot");
16513498266Sopenharmony_ci
16613498266Sopenharmony_ci  /* add one letter, the response should be one longer */
16713498266Sopenharmony_ci  olen2 = magic1;
16813498266Sopenharmony_ci  ret2 = doh_encode(sunshine2, dnstype, buffer, buflen, &olen2);
16913498266Sopenharmony_ci  fail_unless(ret2 == DOH_OK, "sunshine case 2 should pass fine");
17013498266Sopenharmony_ci  fail_if(olen2 == magic1, "olen has not been assigned properly");
17113498266Sopenharmony_ci  fail_unless(olen1 + 1 == olen2, "olen should grow with the hostname");
17213498266Sopenharmony_ci
17313498266Sopenharmony_ci  /* pass a short buffer, should fail */
17413498266Sopenharmony_ci  ret = doh_encode(sunshine1, dnstype, buffer, olen1 - 1, &olen);
17513498266Sopenharmony_ci  fail_if(ret == DOH_OK, "short buffer should have been noticed");
17613498266Sopenharmony_ci
17713498266Sopenharmony_ci  /* pass a minimum buffer, should succeed */
17813498266Sopenharmony_ci  ret = doh_encode(sunshine1, dnstype, buffer, olen1, &olen);
17913498266Sopenharmony_ci  fail_unless(ret == DOH_OK, "minimal length buffer should be long enough");
18013498266Sopenharmony_ci  fail_unless(olen == olen1, "bad buffer length");
18113498266Sopenharmony_ci} while(0);
18213498266Sopenharmony_ciUNITTEST_STOP
18313498266Sopenharmony_ci
18413498266Sopenharmony_ci#else /* CURL_DISABLE_DOH */
18513498266Sopenharmony_ci
18613498266Sopenharmony_ciUNITTEST_START
18713498266Sopenharmony_ci/* nothing to do, just succeed */
18813498266Sopenharmony_ciUNITTEST_STOP
18913498266Sopenharmony_ci
19013498266Sopenharmony_ci#endif
191