unit1655.c (7104B)
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 "unitcheck.h" 25 26 #include "doh.h" /* from the lib dir */ 27 28 static CURLcode test_unit1655(char *arg) 29 { 30 UNITTEST_BEGIN_SIMPLE 31 32 #ifndef CURL_DISABLE_DOH 33 /* 34 * Prove detection of write overflow using a short buffer and a name 35 * of maximal valid length. 36 * 37 * Prove detection of other invalid input. 38 */ 39 do { 40 static const char max[] = 41 /* ..|....1.........2.........3.........4.........5.........6... */ 42 /* 3456789012345678901234567890123456789012345678901234567890123 */ 43 "this.is.a.maximum-length.hostname." /* 34: 34 */ 44 "with-no-label-of-greater-length-than-the-sixty-three-characters." 45 /* 64: 98 */ 46 "specified.in.the.RFCs." /* 22: 120 */ 47 "and.with.a.QNAME.encoding.whose.length.is.exactly." /* 50: 170 */ 48 "the.maximum.length.allowed." /* 27: 197 */ 49 "that.is.two-hundred.and.fifty-six." /* 34: 231 */ 50 "including.the.last.null." /* 24: 255 */ 51 ""; 52 static const char toolong[] = 53 /* ..|....1.........2.........3.........4.........5.........6... */ 54 /* 3456789012345678901234567890123456789012345678901234567890123 */ 55 "here.is.a.hostname.which.is.just.barely.too.long." /* 49: 49 */ 56 "to.be.encoded.as.a.QNAME.of.the.maximum.allowed.length." 57 /* 55: 104 */ 58 "which.is.256.including.a.final.zero-length.label." /* 49: 153 */ 59 "representing.the.root.node.so.that.a.name.with." /* 47: 200 */ 60 "a.trailing.dot.may.have.up.to." /* 30: 230 */ 61 "255.characters.never.more." /* 26: 256 */ 62 ""; 63 static const char emptylabel[] = 64 "this.is.an.otherwise-valid.hostname." 65 ".with.an.empty.label."; 66 static const char outsizelabel[] = 67 "this.is.an.otherwise-valid.hostname." 68 "with-a-label-of-greater-length-than-the-sixty-three-characters-" 69 "specified.in.the.RFCs."; 70 int i; 71 72 struct test { 73 const char *name; 74 const DOHcode expected_result; 75 }; 76 77 /* plays the role of struct dnsprobe in urldata.h */ 78 struct demo { 79 unsigned char dohbuffer[255 + 16]; /* deliberately short buffer */ 80 unsigned char canary1; 81 unsigned char canary2; 82 unsigned char canary3; 83 }; 84 85 const struct test playlist[4] = { 86 { toolong, DOH_DNS_NAME_TOO_LONG }, /* expect early failure */ 87 { emptylabel, DOH_DNS_BAD_LABEL }, /* also */ 88 { outsizelabel, DOH_DNS_BAD_LABEL }, /* also */ 89 { max, DOH_OK } /* expect buffer overwrite */ 90 }; 91 92 for(i = 0; i < (int)(CURL_ARRAYSIZE(playlist)); i++) { 93 const char *name = playlist[i].name; 94 size_t olen = 100000; 95 struct demo victim; 96 DOHcode d; 97 98 victim.canary1 = 87; /* magic numbers, arbitrarily picked */ 99 victim.canary2 = 35; 100 victim.canary3 = 41; 101 d = doh_req_encode(name, DNS_TYPE_A, victim.dohbuffer, 102 sizeof(struct demo), /* allow room for overflow */ 103 &olen); 104 105 fail_unless(d == playlist[i].expected_result, 106 "result returned was not as expected"); 107 if(d == playlist[i].expected_result) { 108 if(name == max) { 109 fail_if(victim.canary1 == 87, 110 "demo one-byte buffer overwrite did not happen"); 111 } 112 else { 113 fail_unless(victim.canary1 == 87, 114 "one-byte buffer overwrite has happened"); 115 } 116 fail_unless(victim.canary2 == 35, 117 "two-byte buffer overwrite has happened"); 118 fail_unless(victim.canary3 == 41, 119 "three-byte buffer overwrite has happened"); 120 } 121 else { 122 if(d == DOH_OK) { 123 fail_unless(olen <= sizeof(victim.dohbuffer), 124 "wrote outside bounds"); 125 fail_unless(olen > strlen(name), "unrealistic low size"); 126 } 127 } 128 } 129 } while(0); 130 131 /* run normal cases and try to trigger buffer length related errors */ 132 do { 133 DNStype dnstype = DNS_TYPE_A; 134 unsigned char buffer[128]; 135 const size_t buflen = sizeof(buffer); 136 const size_t magic1 = 9765; 137 size_t olen1 = magic1; 138 static const char *sunshine1 = "a.com"; 139 static const char *dotshine1 = "a.com."; 140 static const char *sunshine2 = "aa.com"; 141 size_t olen2; 142 DOHcode ret2; 143 size_t olen; 144 145 DOHcode ret = doh_req_encode(sunshine1, dnstype, buffer, buflen, &olen1); 146 fail_unless(ret == DOH_OK, "sunshine case 1 should pass fine"); 147 fail_if(olen1 == magic1, "olen has not been assigned properly"); 148 fail_unless(olen1 > strlen(sunshine1), "bad out length"); 149 150 /* with a trailing dot, the response should have the same length */ 151 olen2 = magic1; 152 ret2 = doh_req_encode(dotshine1, dnstype, buffer, buflen, &olen2); 153 fail_unless(ret2 == DOH_OK, "dotshine case should pass fine"); 154 fail_if(olen2 == magic1, "olen has not been assigned properly"); 155 fail_unless(olen1 == olen2, "olen should not grow for a trailing dot"); 156 157 /* add one letter, the response should be one longer */ 158 olen2 = magic1; 159 ret2 = doh_req_encode(sunshine2, dnstype, buffer, buflen, &olen2); 160 fail_unless(ret2 == DOH_OK, "sunshine case 2 should pass fine"); 161 fail_if(olen2 == magic1, "olen has not been assigned properly"); 162 fail_unless(olen1 + 1 == olen2, "olen should grow with the hostname"); 163 164 /* pass a short buffer, should fail */ 165 ret = doh_req_encode(sunshine1, dnstype, buffer, olen1 - 1, &olen); 166 fail_if(ret == DOH_OK, "short buffer should have been noticed"); 167 168 /* pass a minimum buffer, should succeed */ 169 ret = doh_req_encode(sunshine1, dnstype, buffer, olen1, &olen); 170 fail_unless(ret == DOH_OK, "minimal length buffer should be long enough"); 171 fail_unless(olen == olen1, "bad buffer length"); 172 } while(0); 173 174 #endif /* CURL_DISABLE_DOH */ 175 176 UNITTEST_END_SIMPLE 177 }