1 1.1 christos /* $NetBSD: fuzz.c,v 1.8 2025/01/26 16:24:33 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* 4 1.1 christos * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 1.1 christos * 6 1.7 christos * SPDX-License-Identifier: MPL-2.0 7 1.7 christos * 8 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 9 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 10 1.6 christos * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 1.1 christos * 12 1.1 christos * See the COPYRIGHT file distributed with this work for additional 13 1.1 christos * information regarding copyright ownership. 14 1.1 christos */ 15 1.1 christos 16 1.3 christos #include <inttypes.h> 17 1.3 christos #include <stdbool.h> 18 1.1 christos 19 1.1 christos #include <named/fuzz.h> 20 1.1 christos 21 1.1 christos #ifdef ENABLE_AFL 22 1.5 christos #include <arpa/inet.h> 23 1.4 christos #include <errno.h> 24 1.5 christos #include <pthread.h> 25 1.5 christos #include <signal.h> 26 1.5 christos #include <stdlib.h> 27 1.5 christos #include <string.h> 28 1.5 christos #include <unistd.h> 29 1.1 christos 30 1.1 christos #include <isc/condition.h> 31 1.8 christos #include <isc/loop.h> 32 1.1 christos #include <isc/mutex.h> 33 1.1 christos #include <isc/thread.h> 34 1.1 christos #include <isc/util.h> 35 1.5 christos 36 1.1 christos #include <dns/log.h> 37 1.1 christos 38 1.5 christos #include <named/globals.h> 39 1.5 christos #include <named/log.h> 40 1.5 christos #include <named/server.h> 41 1.1 christos 42 1.1 christos /* 43 1.1 christos * We are using pthreads directly because we might be using it with 44 1.1 christos * unthreaded version of BIND, where all thread functions are 45 1.1 christos * mocks. Since AFL for now only works on Linux it's not a problem. 46 1.1 christos */ 47 1.1 christos static pthread_cond_t cond; 48 1.1 christos static pthread_mutex_t mutex; 49 1.3 christos static bool ready; 50 1.1 christos 51 1.1 christos /* 52 1.1 christos * In "client:" mode, this thread reads fuzzed query messages from AFL 53 1.1 christos * from standard input and sends it to named's listening port (DNS) that 54 1.1 christos * is passed in the -A client:<address>:<port> option. It can be used to 55 1.1 christos * test named from the client side. 56 1.1 christos */ 57 1.1 christos static void * 58 1.1 christos fuzz_thread_client(void *arg) { 59 1.1 christos char *host; 60 1.1 christos char *port; 61 1.1 christos struct sockaddr_in servaddr; 62 1.1 christos int sockfd; 63 1.1 christos void *buf; 64 1.1 christos 65 1.1 christos UNUSED(arg); 66 1.1 christos 67 1.1 christos /* 68 1.1 christos * Parse named -A argument in the "address:port" syntax. Due to 69 1.1 christos * the syntax used, this only supports IPv4 addresses. 70 1.1 christos */ 71 1.1 christos host = strdup(named_g_fuzz_addr); 72 1.1 christos RUNTIME_CHECK(host != NULL); 73 1.1 christos 74 1.1 christos port = strchr(host, ':'); 75 1.1 christos RUNTIME_CHECK(port != NULL); 76 1.1 christos *port = 0; 77 1.1 christos ++port; 78 1.1 christos 79 1.5 christos memset(&servaddr, 0, sizeof(servaddr)); 80 1.1 christos servaddr.sin_family = AF_INET; 81 1.1 christos RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1); 82 1.1 christos servaddr.sin_port = htons(atoi(port)); 83 1.1 christos 84 1.1 christos free(host); 85 1.1 christos 86 1.1 christos /* 87 1.1 christos * Wait for named to start. This is set in run_server() in the 88 1.1 christos * named thread. 89 1.1 christos */ 90 1.1 christos while (!named_g_run_done) { 91 1.1 christos usleep(10000); 92 1.1 christos } 93 1.1 christos 94 1.1 christos sockfd = socket(AF_INET, SOCK_DGRAM, 0); 95 1.1 christos RUNTIME_CHECK(sockfd != -1); 96 1.1 christos 97 1.1 christos buf = malloc(65536); 98 1.1 christos RUNTIME_CHECK(buf != NULL); 99 1.1 christos 100 1.1 christos /* 101 1.1 christos * Processing fuzzed packets 100,000 times before shutting down 102 1.1 christos * the app. 103 1.1 christos */ 104 1.3 christos #ifdef __AFL_LOOP 105 1.3 christos for (int loop = 0; loop < 100000; loop++) { 106 1.5 christos #else /* ifdef __AFL_LOOP */ 107 1.3 christos { 108 1.5 christos #endif /* ifdef __AFL_LOOP */ 109 1.1 christos ssize_t length; 110 1.1 christos ssize_t sent; 111 1.1 christos 112 1.1 christos length = read(0, buf, 65536); 113 1.1 christos if (length <= 0) { 114 1.1 christos usleep(1000000); 115 1.3 christos goto next; 116 1.1 christos } 117 1.1 christos 118 1.1 christos /* 119 1.1 christos * Ignore packets that are larger than 4096 bytes. 120 1.1 christos */ 121 1.1 christos if (length > 4096) { 122 1.1 christos /* 123 1.1 christos * AFL_CMIN doesn't support persistent mode, so 124 1.1 christos * shutdown the server. 125 1.1 christos */ 126 1.1 christos if (getenv("AFL_CMIN")) { 127 1.1 christos free(buf); 128 1.1 christos close(sockfd); 129 1.1 christos named_server_flushonshutdown(named_g_server, 130 1.3 christos false); 131 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 132 1.8 christos return NULL; 133 1.1 christos } 134 1.1 christos raise(SIGSTOP); 135 1.3 christos goto next; 136 1.1 christos } 137 1.1 christos 138 1.1 christos RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); 139 1.1 christos 140 1.3 christos ready = false; 141 1.1 christos 142 1.1 christos sent = sendto(sockfd, buf, length, 0, 143 1.5 christos (struct sockaddr *)&servaddr, sizeof(servaddr)); 144 1.1 christos RUNTIME_CHECK(sent == length); 145 1.1 christos 146 1.1 christos /* 147 1.1 christos * Read the reply message from named to unclog it. Don't 148 1.1 christos * bother if there isn't a reply. 149 1.1 christos */ 150 1.5 christos (void)recvfrom(sockfd, buf, 65536, MSG_DONTWAIT, NULL, NULL); 151 1.1 christos 152 1.5 christos while (!ready) { 153 1.1 christos pthread_cond_wait(&cond, &mutex); 154 1.5 christos } 155 1.1 christos 156 1.1 christos RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); 157 1.5 christos next:; 158 1.1 christos } 159 1.1 christos 160 1.1 christos free(buf); 161 1.1 christos close(sockfd); 162 1.1 christos 163 1.3 christos named_server_flushonshutdown(named_g_server, false); 164 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 165 1.1 christos 166 1.8 christos return NULL; 167 1.1 christos } 168 1.1 christos 169 1.1 christos /* 170 1.1 christos * In "resolver:" mode, this thread reads fuzzed reply messages from AFL 171 1.1 christos * from standard input. It also sets up a listener as a remote 172 1.1 christos * authoritative server and sends a driver query to the client side of 173 1.1 christos * named(resolver). When named(resolver) connects to this authoritative 174 1.1 christos * server, this thread writes the fuzzed reply message from AFL to it. 175 1.1 christos * 176 1.1 christos * -A resolver:<saddress>:<sport>:<raddress>:<rport> 177 1.1 christos * 178 1.1 christos * Here, <saddress>:<sport> is where named(resolver) is listening on. 179 1.1 christos * <raddress>:<rport> is where the thread is supposed to setup the 180 1.1 christos * authoritative server. This address should be configured via the root 181 1.1 christos * zone to be the authoritiative server for aaaaaaaaaa.example. 182 1.1 christos * 183 1.1 christos * named(resolver) when being fuzzed will not cache answers. 184 1.1 christos */ 185 1.1 christos static void * 186 1.1 christos fuzz_thread_resolver(void *arg) { 187 1.1 christos char *sqtype, *shost, *sport, *rhost, *rport; 188 1.1 christos struct sockaddr_in servaddr, recaddr, recvaddr; 189 1.1 christos /* 190 1.1 christos * Query for aaaaaaaaaa.example./A in wire format with RD=1, 191 1.1 christos * EDNS and DO=1. 0x88, 0x0c at the start is the ID field which 192 1.1 christos * will be updated for each query. 193 1.1 christos */ 194 1.5 christos char respacket[] = { 0x88, 0x0c, 0x01, 0x20, 0x00, 0x01, 0x00, 0x00, 195 1.5 christos 0x00, 0x00, 0x00, 0x01, 0x0a, 0x61, 0x61, 0x61, 196 1.5 christos 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x07, 197 1.5 christos 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 198 1.5 christos 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x29, 0x10, 199 1.5 christos 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 }; 200 1.1 christos /* 201 1.1 christos * Response for example./DNSKEY in wire format. Note that RRSIGs 202 1.1 christos * were generated with this DNSKEY that are used as seeds for 203 1.1 christos * AFL in the DNSSEC fuzzing job. So the DNSKEY content of this 204 1.1 christos * message must not change, or the corresponding RRSIGs will 205 1.1 christos * have to be updated. 0x8d, 0xf6 at the start is the ID field 206 1.1 christos * which will be made to match the query. 207 1.1 christos */ 208 1.3 christos const uint8_t dnskey_wf[] = { 209 1.5 christos 0x8d, 0xf6, 0x84, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 210 1.5 christos 0x00, 0x01, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 211 1.5 christos 0x00, 0x00, 0x30, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x30, 0x00, 212 1.5 christos 0x01, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x08, 0x01, 0x00, 0x03, 213 1.5 christos 0x08, 0x03, 0x01, 0x00, 0x01, 0xbd, 0x81, 0xdc, 0x7f, 0x16, 214 1.5 christos 0xd4, 0x81, 0x7c, 0x1f, 0x9f, 0x6a, 0x68, 0xdd, 0xd4, 0xda, 215 1.5 christos 0x48, 0xd9, 0x1c, 0xbd, 0xa6, 0x46, 0x1a, 0xf0, 0xb4, 0xb9, 216 1.5 christos 0xec, 0x3d, 0x6c, 0x0b, 0x57, 0xc7, 0xd6, 0x54, 0x66, 0xe6, 217 1.5 christos 0x6c, 0xd5, 0x90, 0x3a, 0x78, 0x7d, 0x7f, 0x78, 0x80, 0xa2, 218 1.5 christos 0x89, 0x61, 0x6d, 0x8a, 0x2b, 0xcd, 0x0a, 0x77, 0x7a, 0xad, 219 1.5 christos 0xc9, 0x61, 0x53, 0x53, 0x8c, 0x99, 0x72, 0x86, 0x14, 0x74, 220 1.5 christos 0x9c, 0x49, 0x2a, 0x47, 0x23, 0xf7, 0x02, 0x07, 0x73, 0x1c, 221 1.5 christos 0x5c, 0x2e, 0xb4, 0x9a, 0xa4, 0xd7, 0x98, 0x42, 0xc3, 0xd2, 222 1.5 christos 0xfe, 0xbf, 0xf3, 0xb3, 0x6a, 0x52, 0x92, 0xd5, 0xfa, 0x47, 223 1.5 christos 0x00, 0xe3, 0xd9, 0x59, 0x31, 0x95, 0x48, 0x40, 0xfc, 0x06, 224 1.5 christos 0x73, 0x90, 0xc6, 0x73, 0x96, 0xba, 0x29, 0x91, 0xe2, 0xac, 225 1.5 christos 0xa3, 0xa5, 0x6d, 0x91, 0x6d, 0x52, 0xb9, 0x34, 0xba, 0x68, 226 1.5 christos 0x4f, 0xad, 0xf0, 0xc3, 0xf3, 0x1d, 0x6d, 0x61, 0x76, 0xe5, 227 1.5 christos 0x3d, 0xa3, 0x9b, 0x2a, 0x0c, 0x92, 0xb3, 0x78, 0x6b, 0xf1, 228 1.5 christos 0x20, 0xd6, 0x90, 0xb7, 0xac, 0xe2, 0xf8, 0x2b, 0x94, 0x10, 229 1.5 christos 0x79, 0xce, 0xa8, 0x60, 0x42, 0xea, 0x6a, 0x18, 0x2f, 0xc0, 230 1.5 christos 0xd8, 0x05, 0x0a, 0x3b, 0x06, 0x0f, 0x02, 0x7e, 0xff, 0x33, 231 1.5 christos 0x46, 0xee, 0xb6, 0x21, 0x25, 0x90, 0x63, 0x4b, 0x3b, 0x5e, 232 1.5 christos 0xb2, 0x72, 0x3a, 0xcb, 0x91, 0x41, 0xf4, 0x20, 0x50, 0x78, 233 1.5 christos 0x1c, 0x93, 0x95, 0xda, 0xfa, 0xae, 0x85, 0xc5, 0xd7, 0x6b, 234 1.5 christos 0x92, 0x0c, 0x70, 0x6b, 0xe4, 0xb7, 0x29, 0x3a, 0x2e, 0x18, 235 1.5 christos 0x88, 0x82, 0x33, 0x7c, 0xa8, 0xea, 0xb8, 0x31, 0x8f, 0xaf, 236 1.5 christos 0x50, 0xc5, 0x9c, 0x08, 0x56, 0x8f, 0x09, 0x76, 0x4e, 0xdf, 237 1.5 christos 0x97, 0x75, 0x9d, 0x00, 0x52, 0x7f, 0xdb, 0xec, 0x30, 0xcb, 238 1.5 christos 0x1c, 0x4c, 0x2a, 0x21, 0x93, 0xc4, 0x6d, 0x85, 0xa9, 0x40, 239 1.5 christos 0x3b, 0xc0, 0x0c, 0x00, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x01, 240 1.5 christos 0x2c, 0x01, 0x1b, 0x00, 0x30, 0x08, 0x01, 0x00, 0x00, 0x01, 241 1.5 christos 0x2c, 0x67, 0x74, 0x85, 0x80, 0x58, 0xb3, 0xc5, 0x17, 0x36, 242 1.5 christos 0x90, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 243 1.5 christos 0x45, 0xac, 0xd3, 0x82, 0x69, 0xf3, 0x10, 0x3a, 0x97, 0x2c, 244 1.5 christos 0x6a, 0xa9, 0x78, 0x99, 0xea, 0xb0, 0xcc, 0xf7, 0xaf, 0x33, 245 1.5 christos 0x51, 0x5b, 0xdf, 0x77, 0x04, 0x18, 0x14, 0x99, 0x61, 0xeb, 246 1.5 christos 0x8d, 0x76, 0x3f, 0xd1, 0x71, 0x14, 0x43, 0x80, 0x53, 0xc2, 247 1.5 christos 0x3b, 0x9f, 0x09, 0x4f, 0xb3, 0x51, 0x04, 0x89, 0x0e, 0xc8, 248 1.5 christos 0x54, 0x12, 0xcd, 0x07, 0x20, 0xbe, 0x94, 0xc2, 0xda, 0x99, 249 1.5 christos 0xdd, 0x1e, 0xf8, 0xb0, 0x84, 0x2e, 0xf9, 0x19, 0x35, 0x36, 250 1.5 christos 0xf5, 0xd0, 0x5d, 0x82, 0x18, 0x74, 0xa0, 0x00, 0xb6, 0x15, 251 1.5 christos 0x57, 0x40, 0x5f, 0x78, 0x2d, 0x27, 0xac, 0xc7, 0x8a, 0x29, 252 1.5 christos 0x55, 0xa9, 0xcd, 0xbc, 0xf7, 0x3e, 0xff, 0xae, 0x1a, 0x5a, 253 1.5 christos 0x1d, 0xac, 0x0d, 0x78, 0x0e, 0x08, 0x33, 0x6c, 0x59, 0x70, 254 1.5 christos 0x40, 0xb9, 0x65, 0xbd, 0x35, 0xbb, 0x9a, 0x70, 0xdc, 0x93, 255 1.5 christos 0x66, 0xb0, 0xef, 0xfe, 0xf0, 0x32, 0xa6, 0xee, 0xb7, 0x03, 256 1.5 christos 0x89, 0xa2, 0x4d, 0xe0, 0xf1, 0x20, 0xdf, 0x39, 0xe8, 0xe3, 257 1.5 christos 0xcc, 0x95, 0xe9, 0x9a, 0xad, 0xbf, 0xbd, 0x7c, 0xf7, 0xd7, 258 1.5 christos 0xde, 0x47, 0x9e, 0xf6, 0x17, 0xbb, 0x84, 0xa9, 0xed, 0xf2, 259 1.5 christos 0x45, 0x61, 0x6d, 0x13, 0x0b, 0x06, 0x29, 0x50, 0xde, 0xfd, 260 1.5 christos 0x42, 0xb0, 0x66, 0x2c, 0x1c, 0x2b, 0x63, 0xcb, 0x4e, 0xb9, 261 1.5 christos 0x31, 0xc4, 0xea, 0xd2, 0x07, 0x3a, 0x08, 0x79, 0x19, 0x4b, 262 1.5 christos 0x4c, 0x50, 0x97, 0x02, 0xd7, 0x26, 0x41, 0x2f, 0xdd, 0x57, 263 1.5 christos 0xaa, 0xb0, 0xa0, 0x21, 0x4e, 0x74, 0xb6, 0x97, 0x4b, 0x8b, 264 1.5 christos 0x09, 0x9c, 0x3d, 0x29, 0xfb, 0x12, 0x27, 0x47, 0x8f, 0xb8, 265 1.5 christos 0xc5, 0x8e, 0x65, 0xcd, 0xca, 0x2f, 0xba, 0xf5, 0x3e, 0xec, 266 1.5 christos 0x56, 0xc3, 0xc9, 0xa1, 0x62, 0x7d, 0xf2, 0x9f, 0x90, 0x16, 267 1.5 christos 0x1d, 0xbf, 0x97, 0x28, 0xe1, 0x92, 0xb1, 0x53, 0xab, 0xc4, 268 1.5 christos 0xe0, 0x99, 0xbb, 0x19, 0x90, 0x7c, 0x00, 0x00, 0x29, 0x10, 269 1.1 christos 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00 270 1.1 christos }; 271 1.1 christos 272 1.1 christos int sockfd; 273 1.1 christos int listenfd; 274 1.1 christos int loop; 275 1.3 christos uint16_t qtype; 276 1.1 christos char *buf, *rbuf; 277 1.1 christos char *nameptr; 278 1.1 christos unsigned int i; 279 1.3 christos uint8_t llen; 280 1.3 christos uint64_t seed; 281 1.1 christos 282 1.1 christos UNUSED(arg); 283 1.1 christos 284 1.1 christos /* 285 1.1 christos * Parse named -A argument in the "qtype:saddress:sport:raddress:rport" 286 1.1 christos * syntax. Due to the syntax used, this only supports IPv4 addresses. 287 1.1 christos */ 288 1.1 christos sqtype = strdup(named_g_fuzz_addr); 289 1.1 christos RUNTIME_CHECK(sqtype != NULL); 290 1.1 christos 291 1.1 christos shost = strchr(sqtype, ':'); 292 1.1 christos RUNTIME_CHECK(shost != NULL); 293 1.1 christos *shost = 0; 294 1.1 christos shost++; 295 1.1 christos 296 1.1 christos sport = strchr(shost, ':'); 297 1.1 christos RUNTIME_CHECK(sport != NULL); 298 1.1 christos *sport = 0; 299 1.1 christos sport++; 300 1.1 christos 301 1.1 christos rhost = strchr(sport, ':'); 302 1.1 christos RUNTIME_CHECK(rhost != NULL); 303 1.1 christos *rhost = 0; 304 1.1 christos rhost++; 305 1.1 christos 306 1.1 christos rport = strchr(rhost, ':'); 307 1.1 christos RUNTIME_CHECK(rport != NULL); 308 1.1 christos *rport = 0; 309 1.1 christos rport++; 310 1.1 christos 311 1.1 christos /* 312 1.1 christos * Patch in the qtype into the question section of respacket. 313 1.1 christos */ 314 1.1 christos qtype = atoi(sqtype); 315 1.1 christos respacket[32] = (qtype >> 8) & 0xff; 316 1.1 christos respacket[33] = qtype & 0xff; 317 1.1 christos 318 1.5 christos memset(&servaddr, 0, sizeof(servaddr)); 319 1.1 christos servaddr.sin_family = AF_INET; 320 1.1 christos RUNTIME_CHECK(inet_pton(AF_INET, shost, &servaddr.sin_addr) == 1); 321 1.1 christos servaddr.sin_port = htons(atoi(sport)); 322 1.1 christos 323 1.5 christos memset(&recaddr, 0, sizeof(recaddr)); 324 1.1 christos recaddr.sin_family = AF_INET; 325 1.1 christos RUNTIME_CHECK(inet_pton(AF_INET, rhost, &recaddr.sin_addr) == 1); 326 1.1 christos recaddr.sin_port = htons(atoi(rport)); 327 1.1 christos 328 1.1 christos free(sqtype); 329 1.1 christos 330 1.1 christos /* 331 1.1 christos * Wait for named to start. This is set in run_server() in the 332 1.1 christos * named thread. 333 1.1 christos */ 334 1.1 christos while (!named_g_run_done) { 335 1.1 christos usleep(10000); 336 1.1 christos } 337 1.1 christos 338 1.1 christos sockfd = socket(AF_INET, SOCK_DGRAM, 0); 339 1.1 christos RUNTIME_CHECK(sockfd != -1); 340 1.1 christos 341 1.1 christos listenfd = socket(AF_INET, SOCK_DGRAM, 0); 342 1.1 christos RUNTIME_CHECK(listenfd != -1); 343 1.1 christos 344 1.1 christos RUNTIME_CHECK(bind(listenfd, (struct sockaddr *)&recaddr, 345 1.1 christos sizeof(struct sockaddr_in)) == 0); 346 1.1 christos 347 1.1 christos buf = malloc(65536); 348 1.1 christos rbuf = malloc(65536); 349 1.1 christos RUNTIME_CHECK(buf != NULL); 350 1.1 christos RUNTIME_CHECK(rbuf != NULL); 351 1.1 christos 352 1.1 christos seed = 42; 353 1.1 christos 354 1.1 christos /* 355 1.1 christos * Processing fuzzed packets 100,000 times before shutting down 356 1.1 christos * the app. 357 1.1 christos */ 358 1.1 christos for (loop = 0; loop < 100000; loop++) { 359 1.1 christos ssize_t length; 360 1.1 christos ssize_t sent; 361 1.1 christos unsigned short id; 362 1.1 christos socklen_t socklen; 363 1.1 christos 364 1.1 christos memset(buf, 0, 12); 365 1.1 christos length = read(0, buf, 65536); 366 1.1 christos if (length <= 0) { 367 1.1 christos usleep(1000000); 368 1.1 christos continue; 369 1.1 christos } 370 1.1 christos 371 1.1 christos if (length > 4096) { 372 1.1 christos if (getenv("AFL_CMIN")) { 373 1.1 christos free(buf); 374 1.1 christos free(rbuf); 375 1.1 christos close(sockfd); 376 1.1 christos close(listenfd); 377 1.1 christos named_server_flushonshutdown(named_g_server, 378 1.3 christos false); 379 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 380 1.8 christos return NULL; 381 1.1 christos } 382 1.1 christos raise(SIGSTOP); 383 1.1 christos continue; 384 1.1 christos } 385 1.1 christos 386 1.1 christos if (length < 12) { 387 1.5 christos length = 12; 388 1.1 christos } 389 1.1 christos 390 1.1 christos RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); 391 1.1 christos 392 1.3 christos ready = false; 393 1.1 christos 394 1.1 christos /* Use a unique query ID. */ 395 1.1 christos seed = 1664525 * seed + 1013904223; 396 1.1 christos id = seed & 0xffff; 397 1.1 christos respacket[0] = (id >> 8) & 0xff; 398 1.1 christos respacket[1] = id & 0xff; 399 1.1 christos 400 1.1 christos /* 401 1.1 christos * Flush any pending data on the authoritative server. 402 1.1 christos */ 403 1.1 christos socklen = sizeof(recvaddr); 404 1.5 christos (void)recvfrom(listenfd, rbuf, 65536, MSG_DONTWAIT, 405 1.5 christos (struct sockaddr *)&recvaddr, &socklen); 406 1.1 christos 407 1.1 christos /* 408 1.1 christos * Send a fixed client query to named(resolver) of 409 1.1 christos * aaaaaaaaaa.example./A. This is the starting query 410 1.1 christos * driver. 411 1.1 christos */ 412 1.1 christos sent = sendto(sockfd, respacket, sizeof(respacket), 0, 413 1.5 christos (struct sockaddr *)&servaddr, sizeof(servaddr)); 414 1.1 christos RUNTIME_CHECK(sent == sizeof(respacket)); 415 1.1 christos 416 1.1 christos /* 417 1.1 christos * named(resolver) will process the query above and send 418 1.1 christos * an upstream query to the authoritative server. We 419 1.1 christos * handle that here as the upstream authoritative server 420 1.1 christos * on listenfd. 421 1.1 christos */ 422 1.1 christos socklen = sizeof(recvaddr); 423 1.1 christos sent = recvfrom(listenfd, rbuf, 65536, 0, 424 1.5 christos (struct sockaddr *)&recvaddr, &socklen); 425 1.1 christos RUNTIME_CHECK(sent > 0); 426 1.1 christos 427 1.1 christos /* 428 1.1 christos * Copy QID and set QR so that response is always 429 1.1 christos * accepted by named(resolver). 430 1.1 christos */ 431 1.1 christos buf[0] = rbuf[0]; 432 1.1 christos buf[1] = rbuf[1]; 433 1.1 christos buf[2] |= 0x80; 434 1.1 christos 435 1.1 christos /* 436 1.1 christos * NOTE: We are not copying the QNAME or setting 437 1.1 christos * rcode=NOERROR each time. So the resolver may fail the 438 1.1 christos * client query (driver) / wander due to this. AA flag 439 1.1 christos * may also not be set based on the contents of the AFL 440 1.1 christos * fuzzed packet. 441 1.1 christos */ 442 1.1 christos 443 1.1 christos /* 444 1.1 christos * A hack - set QTYPE to the one from query so that we 445 1.1 christos * can easily share packets between instances. If we 446 1.1 christos * write over something else we'll get FORMERR anyway. 447 1.1 christos */ 448 1.1 christos 449 1.1 christos /* Skip DNS header to get to the name */ 450 1.1 christos nameptr = buf + 12; 451 1.1 christos 452 1.1 christos /* Skip the name to get to the qtype */ 453 1.1 christos i = 0; 454 1.5 christos while (((llen = nameptr[i]) != 0) && (i < 255) && 455 1.1 christos (((nameptr + i + 1 + llen) - buf) < length)) 456 1.5 christos { 457 1.1 christos i += 1 + llen; 458 1.5 christos } 459 1.1 christos 460 1.1 christos if (i <= 255) { 461 1.1 christos nameptr += 1 + i; 462 1.1 christos /* Patch the qtype */ 463 1.1 christos if ((nameptr - buf) < (length - 2)) { 464 1.1 christos *nameptr++ = (qtype >> 8) & 0xff; 465 1.1 christos *nameptr++ = qtype & 0xff; 466 1.1 christos } 467 1.1 christos /* Patch the qclass */ 468 1.1 christos if ((nameptr - buf) < (length - 2)) { 469 1.1 christos *nameptr++ = 0; 470 1.1 christos *nameptr++ = 1; 471 1.1 christos } 472 1.1 christos } 473 1.1 christos 474 1.1 christos /* 475 1.1 christos * Send the reply to named(resolver). 476 1.1 christos */ 477 1.1 christos sent = sendto(listenfd, buf, length, 0, 478 1.5 christos (struct sockaddr *)&recvaddr, sizeof(recvaddr)); 479 1.1 christos RUNTIME_CHECK(sent == length); 480 1.1 christos 481 1.1 christos /* We might get additional questions here (e.g. for CNAME). */ 482 1.1 christos for (;;) { 483 1.1 christos fd_set fds; 484 1.1 christos struct timeval tv; 485 1.1 christos int rv; 486 1.1 christos int max; 487 1.1 christos 488 1.1 christos FD_ZERO(&fds); 489 1.1 christos FD_SET(listenfd, &fds); 490 1.1 christos FD_SET(sockfd, &fds); 491 1.1 christos tv.tv_sec = 10; 492 1.1 christos tv.tv_usec = 0; 493 1.5 christos max = (listenfd > sockfd ? listenfd : sockfd) + 1; 494 1.1 christos 495 1.1 christos rv = select(max, &fds, NULL, NULL, &tv); 496 1.1 christos RUNTIME_CHECK(rv > 0); 497 1.1 christos 498 1.1 christos if (FD_ISSET(sockfd, &fds)) { 499 1.1 christos /* 500 1.1 christos * It's the reply from named(resolver) 501 1.1 christos * to the client(query driver), so we're 502 1.1 christos * done. 503 1.1 christos */ 504 1.5 christos (void)recvfrom(sockfd, buf, 65536, 0, NULL, 505 1.5 christos NULL); 506 1.1 christos break; 507 1.1 christos } 508 1.1 christos 509 1.1 christos /* 510 1.1 christos * We've got additional question (eg. due to 511 1.1 christos * CNAME). Bounce it - setting QR flag and 512 1.1 christos * NOERROR rcode and sending it back. 513 1.1 christos */ 514 1.1 christos length = recvfrom(listenfd, buf, 65536, 0, 515 1.5 christos (struct sockaddr *)&recvaddr, 516 1.5 christos &socklen); 517 1.1 christos 518 1.1 christos /* 519 1.1 christos * If this is a DNSKEY query, send the DNSKEY, 520 1.1 christos * otherwise, bounce the query. 521 1.1 christos */ 522 1.1 christos 523 1.1 christos /* Skip DNS header to get to the name */ 524 1.1 christos nameptr = buf + 12; 525 1.1 christos 526 1.1 christos /* Skip the name to get to the qtype */ 527 1.1 christos i = 0; 528 1.5 christos while (((llen = nameptr[i]) != 0) && (i < 255) && 529 1.1 christos (((nameptr + i + 1 + llen) - buf) < length)) 530 1.5 christos { 531 1.1 christos i += 1 + llen; 532 1.5 christos } 533 1.1 christos 534 1.1 christos if (i <= 255) { 535 1.1 christos nameptr += 1 + i; 536 1.1 christos /* 537 1.1 christos * Patch in the DNSKEY reply without 538 1.1 christos * touching the ID field. Note that we 539 1.1 christos * don't compare the name in the 540 1.1 christos * question section in the query, but we 541 1.1 christos * don't expect to receive any query for 542 1.1 christos * type DNSKEY but for the name 543 1.1 christos * "example." 544 1.1 christos */ 545 1.1 christos if ((nameptr - buf) < (length - 2)) { 546 1.3 christos uint8_t hb, lb; 547 1.1 christos hb = *nameptr++; 548 1.1 christos lb = *nameptr++; 549 1.1 christos qtype = (hb << 8) | lb; 550 1.1 christos 551 1.1 christos if (qtype == 48) { 552 1.1 christos memmove(buf + 2, dnskey_wf + 2, 553 1.5 christos sizeof(dnskey_wf) - 2); 554 1.5 christos length = sizeof(dnskey_wf); 555 1.1 christos } 556 1.1 christos } 557 1.1 christos } 558 1.1 christos 559 1.1 christos buf[2] |= 0x80; 560 1.1 christos buf[3] &= 0xF0; 561 1.1 christos sent = sendto(listenfd, buf, length, 0, 562 1.5 christos (struct sockaddr *)&recvaddr, 563 1.1 christos sizeof(recvaddr)); 564 1.1 christos RUNTIME_CHECK(sent == length); 565 1.1 christos } 566 1.1 christos 567 1.5 christos while (!ready) { 568 1.1 christos pthread_cond_wait(&cond, &mutex); 569 1.5 christos } 570 1.1 christos 571 1.1 christos RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); 572 1.1 christos } 573 1.1 christos 574 1.1 christos free(buf); 575 1.3 christos free(rbuf); 576 1.1 christos close(sockfd); 577 1.1 christos close(listenfd); 578 1.3 christos named_server_flushonshutdown(named_g_server, false); 579 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 580 1.1 christos 581 1.3 christos #ifdef __AFL_LOOP 582 1.1 christos /* 583 1.1 christos * This is here just for the signature, that's how AFL detects 584 1.1 christos * if it's a 'persistent mode' binary. It has to occur somewhere 585 1.1 christos * in the file, that's all. < wpk_> AFL checks the binary for 586 1.1 christos * this signature ("##SIG_AFL_PERSISTENT##") and runs the binary 587 1.1 christos * in persistent mode if it's present. 588 1.1 christos */ 589 1.1 christos __AFL_LOOP(0); 590 1.5 christos #endif /* ifdef __AFL_LOOP */ 591 1.1 christos 592 1.8 christos return NULL; 593 1.1 christos } 594 1.1 christos 595 1.1 christos /* 596 1.1 christos * In "tcp:", "http:" and "rndc:" modes, this thread reads fuzzed query 597 1.1 christos * blobs from AFL from standard input and sends it to the corresponding 598 1.1 christos * TCP listening port of named (port 53 DNS, or the HTTP statistics 599 1.1 christos * channels listener or the rndc port) that is passed in the -A 600 1.1 christos * <mode>:<address>:<port> option. It can be used to test named from the 601 1.1 christos * client side. 602 1.1 christos */ 603 1.1 christos static void * 604 1.1 christos fuzz_thread_tcp(void *arg) { 605 1.1 christos char *host; 606 1.1 christos char *port; 607 1.1 christos struct sockaddr_in servaddr; 608 1.1 christos int sockfd; 609 1.1 christos char *buf; 610 1.1 christos int loop; 611 1.1 christos 612 1.1 christos UNUSED(arg); 613 1.1 christos 614 1.1 christos /* 615 1.1 christos * Parse named -A argument in the "address:port" syntax. Due to 616 1.1 christos * the syntax used, this only supports IPv4 addresses. 617 1.1 christos */ 618 1.1 christos host = strdup(named_g_fuzz_addr); 619 1.1 christos RUNTIME_CHECK(host != NULL); 620 1.1 christos 621 1.1 christos port = strchr(host, ':'); 622 1.1 christos RUNTIME_CHECK(port != NULL); 623 1.1 christos *port = 0; 624 1.1 christos ++port; 625 1.1 christos 626 1.5 christos memset(&servaddr, 0, sizeof(servaddr)); 627 1.1 christos servaddr.sin_family = AF_INET; 628 1.1 christos RUNTIME_CHECK(inet_pton(AF_INET, host, &servaddr.sin_addr) == 1); 629 1.1 christos servaddr.sin_port = htons(atoi(port)); 630 1.1 christos 631 1.1 christos free(host); 632 1.1 christos 633 1.1 christos /* 634 1.1 christos * Wait for named to start. This is set in run_server() in the 635 1.1 christos * named thread. 636 1.1 christos */ 637 1.1 christos while (!named_g_run_done) { 638 1.1 christos usleep(10000); 639 1.1 christos } 640 1.1 christos 641 1.1 christos buf = malloc(65539); 642 1.1 christos RUNTIME_CHECK(buf != NULL); 643 1.1 christos 644 1.1 christos /* 645 1.1 christos * Processing fuzzed packets 100,000 times before shutting down 646 1.1 christos * the app. 647 1.1 christos */ 648 1.1 christos for (loop = 0; loop < 100000; loop++) { 649 1.1 christos ssize_t length; 650 1.1 christos ssize_t sent; 651 1.1 christos int yes; 652 1.1 christos int r; 653 1.1 christos 654 1.1 christos if (named_g_fuzz_type == isc_fuzz_tcpclient) { 655 1.1 christos /* 656 1.1 christos * To fuzz DNS TCP client we have to put 16-bit 657 1.1 christos * message length preceding the start of packet. 658 1.1 christos */ 659 1.5 christos length = read(0, buf + 2, 65535); 660 1.1 christos buf[0] = (length >> 8) & 0xff; 661 1.1 christos buf[1] = length & 0xff; 662 1.1 christos length += 2; 663 1.1 christos } else { 664 1.1 christos /* 665 1.1 christos * Other types of TCP clients such as HTTP, etc. 666 1.1 christos */ 667 1.1 christos length = read(0, buf, 65535); 668 1.1 christos } 669 1.1 christos if (length <= 0) { 670 1.1 christos usleep(1000000); 671 1.1 christos continue; 672 1.1 christos } 673 1.1 christos if (named_g_fuzz_type == isc_fuzz_http) { 674 1.1 christos /* 675 1.1 christos * This guarantees that the request will be 676 1.1 christos * processed. 677 1.1 christos */ 678 1.1 christos INSIST(length <= 65535); 679 1.5 christos buf[length++] = '\r'; 680 1.5 christos buf[length++] = '\n'; 681 1.5 christos buf[length++] = '\r'; 682 1.5 christos buf[length++] = '\n'; 683 1.1 christos } 684 1.1 christos 685 1.1 christos RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); 686 1.1 christos 687 1.3 christos ready = false; 688 1.1 christos yes = 1; 689 1.1 christos sockfd = socket(AF_INET, SOCK_STREAM, 0); 690 1.1 christos 691 1.1 christos RUNTIME_CHECK(sockfd != -1); 692 1.5 christos RUNTIME_CHECK(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, 693 1.5 christos sizeof(int)) == 0); 694 1.1 christos 695 1.1 christos do { 696 1.5 christos r = connect(sockfd, (struct sockaddr *)&servaddr, 697 1.1 christos sizeof(servaddr)); 698 1.5 christos if (r != 0) { 699 1.1 christos usleep(10000); 700 1.5 christos } 701 1.1 christos } while (r != 0); 702 1.1 christos 703 1.1 christos /* 704 1.1 christos * Send the fuzzed query blob to the target server. 705 1.1 christos */ 706 1.1 christos sent = write(sockfd, buf, length); 707 1.1 christos RUNTIME_CHECK(sent == length); 708 1.1 christos 709 1.1 christos close(sockfd); 710 1.1 christos 711 1.5 christos while (!ready) { 712 1.1 christos pthread_cond_wait(&cond, &mutex); 713 1.5 christos } 714 1.1 christos 715 1.1 christos RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); 716 1.1 christos } 717 1.1 christos 718 1.1 christos free(buf); 719 1.1 christos close(sockfd); 720 1.3 christos named_server_flushonshutdown(named_g_server, false); 721 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 722 1.1 christos 723 1.8 christos return NULL; 724 1.1 christos } 725 1.1 christos 726 1.1 christos #endif /* ENABLE_AFL */ 727 1.1 christos 728 1.1 christos /* 729 1.1 christos * named has finished processing a message and has sent the 730 1.1 christos * reply. Signal the fuzz thread using the condition variable, to read 731 1.1 christos * and process the next item from AFL. 732 1.1 christos */ 733 1.1 christos void 734 1.1 christos named_fuzz_notify(void) { 735 1.1 christos #ifdef ENABLE_AFL 736 1.1 christos if (getenv("AFL_CMIN")) { 737 1.3 christos named_server_flushonshutdown(named_g_server, false); 738 1.8 christos isc_loopmgr_shutdown(named_g_loopmgr); 739 1.1 christos return; 740 1.1 christos } 741 1.1 christos 742 1.1 christos raise(SIGSTOP); 743 1.1 christos 744 1.1 christos RUNTIME_CHECK(pthread_mutex_lock(&mutex) == 0); 745 1.1 christos 746 1.3 christos ready = true; 747 1.1 christos 748 1.1 christos RUNTIME_CHECK(pthread_cond_signal(&cond) == 0); 749 1.1 christos RUNTIME_CHECK(pthread_mutex_unlock(&mutex) == 0); 750 1.1 christos #endif /* ENABLE_AFL */ 751 1.1 christos } 752 1.1 christos 753 1.1 christos void 754 1.1 christos named_fuzz_setup(void) { 755 1.1 christos #ifdef ENABLE_AFL 756 1.1 christos if (getenv("__AFL_PERSISTENT") || getenv("AFL_CMIN")) { 757 1.1 christos pthread_t thread; 758 1.1 christos void *(fn) = NULL; 759 1.1 christos 760 1.1 christos switch (named_g_fuzz_type) { 761 1.1 christos case isc_fuzz_client: 762 1.1 christos fn = fuzz_thread_client; 763 1.1 christos break; 764 1.1 christos 765 1.1 christos case isc_fuzz_http: 766 1.1 christos case isc_fuzz_tcpclient: 767 1.1 christos case isc_fuzz_rndc: 768 1.1 christos fn = fuzz_thread_tcp; 769 1.1 christos break; 770 1.1 christos 771 1.1 christos case isc_fuzz_resolver: 772 1.1 christos fn = fuzz_thread_resolver; 773 1.1 christos break; 774 1.1 christos 775 1.1 christos default: 776 1.1 christos RUNTIME_CHECK(fn != NULL); 777 1.1 christos } 778 1.1 christos 779 1.1 christos RUNTIME_CHECK(pthread_mutex_init(&mutex, NULL) == 0); 780 1.1 christos RUNTIME_CHECK(pthread_cond_init(&cond, NULL) == 0); 781 1.1 christos RUNTIME_CHECK(pthread_create(&thread, NULL, fn, NULL) == 0); 782 1.1 christos } 783 1.1 christos #endif /* ENABLE_AFL */ 784 1.1 christos } 785