1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24#include "curlcheck.h" 25 26#include "doh.h" /* from the lib dir */ 27 28static CURLcode unit_setup(void) 29{ 30 /* whatever you want done first */ 31 return CURLE_OK; 32} 33 34static void unit_stop(void) 35{ 36 /* done before shutting down and exiting */ 37} 38 39#ifndef CURL_DISABLE_DOH 40 41UNITTEST_START 42 43/* 44 * Prove detection of write overflow using a short buffer and a name 45 * of maximal valid length. 46 * 47 * Prove detection of other invalid input. 48 */ 49do { 50 static const char max[] = 51 /* ..|....1.........2.........3.........4.........5.........6... */ 52 /* 3456789012345678901234567890123456789012345678901234567890123 */ 53 "this.is.a.maximum-length.hostname." /* 34: 34 */ 54 "with-no-label-of-greater-length-than-the-sixty-three-characters." 55 /* 64: 98 */ 56 "specified.in.the.RFCs." /* 22: 120 */ 57 "and.with.a.QNAME.encoding.whose.length.is.exactly." /* 50: 170 */ 58 "the.maximum.length.allowed." /* 27: 197 */ 59 "that.is.two-hundred.and.fifty-six." /* 34: 231 */ 60 "including.the.last.null." /* 24: 255 */ 61 ""; 62 static const char toolong[] = 63 /* ..|....1.........2.........3.........4.........5.........6... */ 64 /* 3456789012345678901234567890123456789012345678901234567890123 */ 65 "here.is.a.hostname.which.is.just.barely.too.long." /* 49: 49 */ 66 "to.be.encoded.as.a.QNAME.of.the.maximum.allowed.length." 67 /* 55: 104 */ 68 "which.is.256.including.a.final.zero-length.label." /* 49: 153 */ 69 "representing.the.root.node.so.that.a.name.with." /* 47: 200 */ 70 "a.trailing.dot.may.have.up.to." /* 30: 230 */ 71 "255.characters.never.more." /* 26: 256 */ 72 ""; 73 static const char emptylabel[] = 74 "this.is.an.otherwise-valid.hostname." 75 ".with.an.empty.label."; 76 static const char outsizelabel[] = 77 "this.is.an.otherwise-valid.hostname." 78 "with-a-label-of-greater-length-than-the-sixty-three-characters-" 79 "specified.in.the.RFCs."; 80 int i; 81 82 struct test { 83 const char *name; 84 const DOHcode expected_result; 85 }; 86 87 /* plays the role of struct dnsprobe in urldata.h */ 88 struct demo { 89 unsigned char dohbuffer[255 + 16]; /* deliberately short buffer */ 90 unsigned char canary1; 91 unsigned char canary2; 92 unsigned char canary3; 93 }; 94 95 const struct test playlist[4] = { 96 { toolong, DOH_DNS_NAME_TOO_LONG }, /* expect early failure */ 97 { emptylabel, DOH_DNS_BAD_LABEL }, /* also */ 98 { outsizelabel, DOH_DNS_BAD_LABEL }, /* also */ 99 { max, DOH_OK } /* expect buffer overwrite */ 100 }; 101 102 for(i = 0; i < (int)(sizeof(playlist)/sizeof(*playlist)); i++) { 103 const char *name = playlist[i].name; 104 size_t olen = 100000; 105 struct demo victim; 106 DOHcode d; 107 108 victim.canary1 = 87; /* magic numbers, arbitrarily picked */ 109 victim.canary2 = 35; 110 victim.canary3 = 41; 111 d = doh_encode(name, DNS_TYPE_A, victim.dohbuffer, 112 sizeof(struct demo), /* allow room for overflow */ 113 &olen); 114 115 fail_unless(d == playlist[i].expected_result, 116 "result returned was not as expected"); 117 if(d == playlist[i].expected_result) { 118 if(name == max) { 119 fail_if(victim.canary1 == 87, 120 "demo one-byte buffer overwrite did not happen"); 121 } 122 else { 123 fail_unless(victim.canary1 == 87, 124 "one-byte buffer overwrite has happened"); 125 } 126 fail_unless(victim.canary2 == 35, 127 "two-byte buffer overwrite has happened"); 128 fail_unless(victim.canary3 == 41, 129 "three-byte buffer overwrite has happened"); 130 } 131 else { 132 if(d == DOH_OK) { 133 fail_unless(olen <= sizeof(victim.dohbuffer), "wrote outside bounds"); 134 fail_unless(olen > strlen(name), "unrealistic low size"); 135 } 136 } 137 } 138} while(0); 139 140/* run normal cases and try to trigger buffer length related errors */ 141do { 142 DNStype dnstype = DNS_TYPE_A; 143 unsigned char buffer[128]; 144 const size_t buflen = sizeof(buffer); 145 const size_t magic1 = 9765; 146 size_t olen1 = magic1; 147 const char *sunshine1 = "a.com"; 148 const char *dotshine1 = "a.com."; 149 const char *sunshine2 = "aa.com"; 150 size_t olen2; 151 DOHcode ret2; 152 size_t olen; 153 154 DOHcode ret = doh_encode(sunshine1, dnstype, buffer, buflen, &olen1); 155 fail_unless(ret == DOH_OK, "sunshine case 1 should pass fine"); 156 fail_if(olen1 == magic1, "olen has not been assigned properly"); 157 fail_unless(olen1 > strlen(sunshine1), "bad out length"); 158 159 /* with a trailing dot, the response should have the same length */ 160 olen2 = magic1; 161 ret2 = doh_encode(dotshine1, dnstype, buffer, buflen, &olen2); 162 fail_unless(ret2 == DOH_OK, "dotshine case should pass fine"); 163 fail_if(olen2 == magic1, "olen has not been assigned properly"); 164 fail_unless(olen1 == olen2, "olen should not grow for a trailing dot"); 165 166 /* add one letter, the response should be one longer */ 167 olen2 = magic1; 168 ret2 = doh_encode(sunshine2, dnstype, buffer, buflen, &olen2); 169 fail_unless(ret2 == DOH_OK, "sunshine case 2 should pass fine"); 170 fail_if(olen2 == magic1, "olen has not been assigned properly"); 171 fail_unless(olen1 + 1 == olen2, "olen should grow with the hostname"); 172 173 /* pass a short buffer, should fail */ 174 ret = doh_encode(sunshine1, dnstype, buffer, olen1 - 1, &olen); 175 fail_if(ret == DOH_OK, "short buffer should have been noticed"); 176 177 /* pass a minimum buffer, should succeed */ 178 ret = doh_encode(sunshine1, dnstype, buffer, olen1, &olen); 179 fail_unless(ret == DOH_OK, "minimal length buffer should be long enough"); 180 fail_unless(olen == olen1, "bad buffer length"); 181} while(0); 182UNITTEST_STOP 183 184#else /* CURL_DISABLE_DOH */ 185 186UNITTEST_START 187/* nothing to do, just succeed */ 188UNITTEST_STOP 189 190#endif 191