dnsd.c (17142B)
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 "first.h" 25 26 static int dnsd_wrotepidfile = 0; 27 static int dnsd_wroteportfile = 0; 28 29 static unsigned short get16bit(const unsigned char **pkt, 30 size_t *size) 31 { 32 const unsigned char *p = *pkt; 33 (*pkt) += 2; 34 *size -= 2; 35 return (unsigned short)((p[0] << 8) | p[1]); 36 } 37 38 static char name[256]; 39 40 static int qname(const unsigned char **pkt, size_t *size) 41 { 42 unsigned char length; 43 int o = 0; 44 const unsigned char *p = *pkt; 45 do { 46 int i; 47 length = *p++; 48 if(*size < length) 49 /* too long */ 50 return 1; 51 if(length && o) 52 name[o++] = '.'; 53 for(i = 0; i < length; i++) { 54 name[o++] = *p++; 55 } 56 } while(length); 57 *size -= (p - *pkt); 58 *pkt = p; 59 name[o++] = '\0'; 60 return 0; 61 } 62 63 #define QTYPE_A 1 64 #define QTYPE_AAAA 28 65 #define QTYPE_HTTPS 0x41 66 67 /* 68 * Handle initial connection protocol. 69 * 70 * Return query (qname + type + class), type and id. 71 */ 72 static int store_incoming(const unsigned char *data, size_t size, 73 unsigned char *qbuf, size_t *qlen, 74 unsigned short *qtype, unsigned short *idp) 75 { 76 FILE *server; 77 char dumpfile[256]; 78 #if 0 79 size_t i; 80 #endif 81 unsigned short qd; 82 const unsigned char *qptr; 83 size_t qsize; 84 85 *qlen = 0; 86 *qtype = 0; 87 *idp = 0; 88 89 snprintf(dumpfile, sizeof(dumpfile), "%s/dnsd.input", logdir); 90 91 /* Open request dump file. */ 92 server = fopen(dumpfile, "ab"); 93 if(!server) { 94 int error = errno; 95 logmsg("fopen() failed with error (%d) %s", error, strerror(error)); 96 logmsg("Error opening file '%s'", dumpfile); 97 return -1; 98 } 99 100 /* 101 1 1 1 1 1 1 102 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 103 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 104 | ID | 105 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 106 |QR| Opcode |AA|TC|RD|RA| Z | RCODE | 107 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 108 | QDCOUNT | 109 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 110 | ANCOUNT | 111 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 112 | NSCOUNT | 113 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 114 | ARCOUNT | 115 +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 116 */ 117 *idp = get16bit(&data, &size); 118 data += 2; /* skip the next 16 bits */ 119 size -= 2; 120 #if 0 121 fprintf(server, "QR: %x\n", (id & 0x8000) > 15); 122 fprintf(server, "OPCODE: %x\n", (id & 0x7800) >> 11); 123 fprintf(server, "TC: %x\n", (id & 0x200) >> 9); 124 fprintf(server, "RD: %x\n", (id & 0x100) >> 8); 125 fprintf(server, "Z: %x\n", (id & 0x70) >> 4); 126 fprintf(server, "RCODE: %x\n", (id & 0x0f)); 127 #endif 128 qd = get16bit(&data, &size); 129 fprintf(server, "QDCOUNT: %04x\n", qd); 130 131 data += 6; /* skip ANCOUNT, NSCOUNT and ARCOUNT */ 132 size -= 6; 133 134 /* store pointer and size at the QD point */ 135 qsize = size; 136 qptr = data; 137 138 if(!qname(&data, &size)) { 139 fprintf(server, "QNAME: %s\n", name); 140 qd = get16bit(&data, &size); 141 fprintf(server, "QTYPE: %04x\n", qd); 142 *qtype = qd; 143 logmsg("Question for '%s' type %x", name, qd); 144 145 qd = get16bit(&data, &size); 146 logmsg("QCLASS: %04x\n", qd); 147 148 *qlen = qsize - size; /* total size of the query */ 149 memcpy(qbuf, qptr, *qlen); 150 } 151 else 152 logmsg("Bad input qname"); 153 #if 0 154 for(i = 0; i < size; i++) { 155 fprintf(server, "%02d", (unsigned int)data[i]); 156 } 157 fprintf(server, "\n"); 158 #endif 159 160 fclose(server); 161 162 return 0; 163 } 164 165 static void add_answer(unsigned char *bytes, size_t *w, 166 const unsigned char *a, size_t alen, 167 unsigned short qtype) 168 { 169 size_t i = *w; 170 171 /* add answer */ 172 bytes[i++] = 0xc0; 173 bytes[i++] = 0x0c; /* points to the query at this fixed packet index */ 174 175 /* QTYPE */ 176 bytes[i++] = (unsigned char)(qtype >> 8); 177 bytes[i++] = (unsigned char)(qtype & 0xff); 178 179 /* QCLASS IN */ 180 bytes[i++] = 0x00; 181 bytes[i++] = 0x01; 182 183 /* TTL, Time to live: 2580 (43 minutes) */ 184 bytes[i++] = 0x00; 185 bytes[i++] = 0x00; 186 bytes[i++] = 0x0a; 187 bytes[i++] = 0x14; 188 189 /* QTYPE size */ 190 bytes[i++] = (unsigned char)(alen >> 8); 191 bytes[i++] = (unsigned char)(alen & 0xff); 192 193 memcpy(&bytes[i], a, alen); 194 i += alen; 195 196 *w = i; 197 } 198 199 #ifdef _WIN32 200 #define SENDTO3 int 201 #else 202 #define SENDTO3 size_t 203 #endif 204 205 #define INSTRUCTIONS "dnsd.cmd" 206 207 #define MAX_ALPN 5 208 209 static unsigned char ipv4_pref[4]; 210 static unsigned char ipv6_pref[16]; 211 static unsigned char alpn_pref[MAX_ALPN]; 212 static int alpn_count; 213 static unsigned char ancount_a; 214 static unsigned char ancount_aaaa; 215 216 /* this is an answer to a question */ 217 static int send_response(curl_socket_t sock, 218 const struct sockaddr *addr, curl_socklen_t addrlen, 219 unsigned char *qbuf, size_t qlen, 220 unsigned short qtype, unsigned short id) 221 { 222 ssize_t rc; 223 size_t i; 224 int a; 225 char addrbuf[128]; /* IP address buffer */ 226 unsigned char bytes[256] = { 227 0x80, 0xea, /* ID, overwrite */ 228 0x81, 0x80, 229 /* 230 Flags: 0x8180 Standard query response, No error 231 1... .... .... .... = Response: Message is a response 232 .000 0... .... .... = Opcode: Standard query (0) 233 .... .0.. .... .... = Authoritative: Server is not an authority for 234 domain 235 .... ..0. .... .... = Truncated: Message is not truncated 236 .... ...1 .... .... = Recursion desired: Do query recursively 237 .... .... 1... .... = Recursion available: Server can do recursive 238 queries 239 .... .... .0.. .... = Z: reserved (0) 240 .... .... ..0. .... = Answer authenticated: Answer/authority portion 241 was not authenticated by the server 242 .... .... ...0 .... = Non-authenticated data: Unacceptable 243 .... .... .... 0000 = Reply code: No error (0) 244 */ 245 0x0, 0x1, /* QDCOUNT a single question */ 246 0x0, 0x0, /* ANCOUNT number of answers */ 247 0x0, 0x0, /* NSCOUNT */ 248 0x0, 0x0 /* ARCOUNT */ 249 }; 250 251 bytes[0] = (unsigned char)(id >> 8); 252 bytes[1] = (unsigned char)(id & 0xff); 253 254 if(qlen > (sizeof(bytes) - 12)) 255 return -1; 256 257 /* append query, includes QTYPE and QCLASS */ 258 memcpy(&bytes[12], qbuf, qlen); 259 260 i = 12 + qlen; 261 262 switch(qtype) { 263 case QTYPE_A: 264 bytes[7] = ancount_a; 265 for(a = 0; a < ancount_a; a++) { 266 const unsigned char *store = ipv4_pref; 267 add_answer(bytes, &i, store, sizeof(ipv4_pref), QTYPE_A); 268 logmsg("Sending back A (%x) '%s'", QTYPE_A, 269 curlx_inet_ntop(AF_INET, store, addrbuf, sizeof(addrbuf))); 270 } 271 break; 272 case QTYPE_AAAA: 273 bytes[7] = ancount_aaaa; 274 for(a = 0; a < ancount_aaaa; a++) { 275 const unsigned char *store = ipv6_pref; 276 add_answer(bytes, &i, store, sizeof(ipv6_pref), QTYPE_AAAA); 277 logmsg("Sending back AAAA (%x) '%s'", QTYPE_AAAA, 278 curlx_inet_ntop(AF_INET6, store, addrbuf, sizeof(addrbuf))); 279 } 280 break; 281 case QTYPE_HTTPS: 282 bytes[7] = 1; /* one answer */ 283 break; 284 } 285 286 #ifdef __AMIGA__ 287 /* Amiga breakage */ 288 (void)rc; 289 (void)sock; 290 (void)addr; 291 (void)addrlen; 292 fprintf(stderr, "Not working\n"); 293 return -1; 294 #else 295 rc = sendto(sock, (const void *)bytes, (SENDTO3) i, 0, addr, addrlen); 296 if(rc != (ssize_t)i) { 297 fprintf(stderr, "failed sending %d bytes\n", (int)i); 298 } 299 #endif 300 return 0; 301 } 302 303 304 static void read_instructions(void) 305 { 306 char file[256]; 307 FILE *f; 308 snprintf(file, sizeof(file), "%s/" INSTRUCTIONS, logdir); 309 f = fopen(file, FOPEN_READTEXT); 310 if(f) { 311 char buf[256]; 312 ancount_aaaa = ancount_a = 0; 313 alpn_count = 0; 314 while(fgets(buf, sizeof(buf), f)) { 315 char *p = strchr(buf, '\n'); 316 if(p) { 317 int rc; 318 *p = 0; 319 if(!strncmp("A: ", buf, 3)) { 320 rc = curlx_inet_pton(AF_INET, &buf[3], ipv4_pref); 321 ancount_a = (rc == 1); 322 } 323 else if(!strncmp("AAAA: ", buf, 6)) { 324 char *p6 = &buf[6]; 325 if(*p6 == '[') { 326 char *pt = strchr(p6, ']'); 327 if(pt) 328 *pt = 0; 329 p6++; 330 } 331 rc = curlx_inet_pton(AF_INET6, p6, ipv6_pref); 332 ancount_aaaa = (rc == 1); 333 } 334 else if(!strncmp("ALPN: ", buf, 6)) { 335 char *ap = &buf[6]; 336 rc = 0; 337 while(*ap) { 338 if('h' == *ap) { 339 ap++; 340 if(*ap >= '1' && *ap <= '3') { 341 if(alpn_count < MAX_ALPN) 342 alpn_pref[alpn_count++] = *ap; 343 } 344 else 345 break; 346 } 347 else 348 break; 349 } 350 } 351 else { 352 rc = 0; 353 } 354 if(rc != 1) { 355 logmsg("Bad line in %s: '%s'\n", file, buf); 356 } 357 } 358 } 359 fclose(f); 360 } 361 else 362 logmsg("Error opening file '%s'", file); 363 } 364 365 static int test_dnsd(int argc, char **argv) 366 { 367 srvr_sockaddr_union_t me; 368 ssize_t n = 0; 369 int arg = 1; 370 unsigned short port = 9123; /* UDP */ 371 curl_socket_t sock = CURL_SOCKET_BAD; 372 int flag; 373 int rc; 374 int error; 375 int result = 0; 376 377 pidname = ".dnsd.pid"; 378 serverlogfile = "log/dnsd.log"; 379 serverlogslocked = 0; 380 381 while(argc > arg) { 382 if(!strcmp("--verbose", argv[arg])) { 383 arg++; 384 /* nothing yet */ 385 } 386 else if(!strcmp("--version", argv[arg])) { 387 printf("dnsd IPv4%s\n", 388 #ifdef USE_IPV6 389 "/IPv6" 390 #else 391 "" 392 #endif 393 ); 394 return 0; 395 } 396 else if(!strcmp("--pidfile", argv[arg])) { 397 arg++; 398 if(argc > arg) 399 pidname = argv[arg++]; 400 } 401 else if(!strcmp("--portfile", argv[arg])) { 402 arg++; 403 if(argc > arg) 404 portname = argv[arg++]; 405 } 406 else if(!strcmp("--logfile", argv[arg])) { 407 arg++; 408 if(argc > arg) 409 serverlogfile = argv[arg++]; 410 } 411 else if(!strcmp("--logdir", argv[arg])) { 412 arg++; 413 if(argc > arg) 414 logdir = argv[arg++]; 415 } 416 else if(!strcmp("--ipv4", argv[arg])) { 417 #ifdef USE_IPV6 418 ipv_inuse = "IPv4"; 419 use_ipv6 = FALSE; 420 #endif 421 arg++; 422 } 423 else if(!strcmp("--ipv6", argv[arg])) { 424 #ifdef USE_IPV6 425 ipv_inuse = "IPv6"; 426 use_ipv6 = TRUE; 427 #endif 428 arg++; 429 } 430 else if(!strcmp("--port", argv[arg])) { 431 arg++; 432 if(argc > arg) { 433 char *endptr; 434 unsigned long ulnum = strtoul(argv[arg], &endptr, 10); 435 port = util_ultous(ulnum); 436 arg++; 437 } 438 } 439 else { 440 if(argv[arg]) 441 fprintf(stderr, "unknown option: %s\n", argv[arg]); 442 puts("Usage: dnsd [option]\n" 443 " --version\n" 444 " --logfile [file]\n" 445 " --logdir [directory]\n" 446 " --pidfile [file]\n" 447 " --portfile [file]\n" 448 " --ipv4\n" 449 " --ipv6\n" 450 " --port [port]\n"); 451 return 0; 452 } 453 } 454 455 snprintf(loglockfile, sizeof(loglockfile), "%s/%s/dnsd-%s.lock", 456 logdir, SERVERLOGS_LOCKDIR, ipv_inuse); 457 458 #ifdef _WIN32 459 if(win32_init()) 460 return 2; 461 #endif 462 463 #ifdef USE_IPV6 464 if(!use_ipv6) 465 #endif 466 sock = socket(AF_INET, SOCK_DGRAM, 0); 467 #ifdef USE_IPV6 468 else 469 sock = socket(AF_INET6, SOCK_DGRAM, 0); 470 #endif 471 472 if(CURL_SOCKET_BAD == sock) { 473 error = SOCKERRNO; 474 logmsg("Error creating socket (%d) %s", error, sstrerror(error)); 475 result = 1; 476 goto dnsd_cleanup; 477 } 478 479 flag = 1; 480 if(0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 481 (void *)&flag, sizeof(flag))) { 482 error = SOCKERRNO; 483 logmsg("setsockopt(SO_REUSEADDR) failed with error (%d) %s", 484 error, sstrerror(error)); 485 result = 1; 486 goto dnsd_cleanup; 487 } 488 489 #ifdef USE_IPV6 490 if(!use_ipv6) { 491 #endif 492 memset(&me.sa4, 0, sizeof(me.sa4)); 493 me.sa4.sin_family = AF_INET; 494 me.sa4.sin_addr.s_addr = INADDR_ANY; 495 me.sa4.sin_port = htons(port); 496 rc = bind(sock, &me.sa, sizeof(me.sa4)); 497 #ifdef USE_IPV6 498 } 499 else { 500 memset(&me.sa6, 0, sizeof(me.sa6)); 501 me.sa6.sin6_family = AF_INET6; 502 me.sa6.sin6_addr = in6addr_any; 503 me.sa6.sin6_port = htons(port); 504 rc = bind(sock, &me.sa, sizeof(me.sa6)); 505 } 506 #endif /* USE_IPV6 */ 507 if(rc) { 508 error = SOCKERRNO; 509 logmsg("Error binding socket on port %hu (%d) %s", port, error, 510 sstrerror(error)); 511 result = 1; 512 goto dnsd_cleanup; 513 } 514 515 if(!port) { 516 /* The system was supposed to choose a port number, figure out which 517 port we actually got and update the listener port value with it. */ 518 curl_socklen_t la_size; 519 srvr_sockaddr_union_t localaddr; 520 #ifdef USE_IPV6 521 if(!use_ipv6) 522 #endif 523 la_size = sizeof(localaddr.sa4); 524 #ifdef USE_IPV6 525 else 526 la_size = sizeof(localaddr.sa6); 527 #endif 528 memset(&localaddr.sa, 0, (size_t)la_size); 529 if(getsockname(sock, &localaddr.sa, &la_size) < 0) { 530 error = SOCKERRNO; 531 logmsg("getsockname() failed with error (%d) %s", 532 error, sstrerror(error)); 533 sclose(sock); 534 goto dnsd_cleanup; 535 } 536 switch(localaddr.sa.sa_family) { 537 case AF_INET: 538 port = ntohs(localaddr.sa4.sin_port); 539 break; 540 #ifdef USE_IPV6 541 case AF_INET6: 542 port = ntohs(localaddr.sa6.sin6_port); 543 break; 544 #endif 545 default: 546 break; 547 } 548 if(!port) { 549 /* Real failure, listener port shall not be zero beyond this point. */ 550 logmsg("Apparently getsockname() succeeded, with listener port zero."); 551 logmsg("A valid reason for this failure is a binary built without"); 552 logmsg("proper network library linkage. This might not be the only"); 553 logmsg("reason, but double check it before anything else."); 554 result = 2; 555 goto dnsd_cleanup; 556 } 557 } 558 559 dnsd_wrotepidfile = write_pidfile(pidname); 560 if(!dnsd_wrotepidfile) { 561 result = 1; 562 goto dnsd_cleanup; 563 } 564 565 if(portname) { 566 dnsd_wroteportfile = write_portfile(portname, port); 567 if(!dnsd_wroteportfile) { 568 result = 1; 569 goto dnsd_cleanup; 570 } 571 } 572 573 logmsg("Running %s version on port UDP/%d", ipv_inuse, (int)port); 574 575 for(;;) { 576 unsigned short id = 0; 577 unsigned char inbuffer[1500]; 578 srvr_sockaddr_union_t from; 579 curl_socklen_t fromlen; 580 unsigned char qbuf[256]; /* query storage */ 581 size_t qlen = 0; /* query size */ 582 unsigned short qtype = 0; 583 fromlen = sizeof(from); 584 #ifdef USE_IPV6 585 if(!use_ipv6) 586 #endif 587 fromlen = sizeof(from.sa4); 588 #ifdef USE_IPV6 589 else 590 fromlen = sizeof(from.sa6); 591 #endif 592 n = (ssize_t)recvfrom(sock, (char *)inbuffer, sizeof(inbuffer), 0, 593 &from.sa, &fromlen); 594 if(got_exit_signal) 595 break; 596 if(n < 0) { 597 logmsg("recvfrom"); 598 result = 3; 599 break; 600 } 601 602 /* read once per incoming query, which is probably more than one 603 per test case */ 604 read_instructions(); 605 606 store_incoming(inbuffer, n, qbuf, &qlen, &qtype, &id); 607 608 set_advisor_read_lock(loglockfile); 609 serverlogslocked = 1; 610 611 send_response(sock, &from.sa, fromlen, qbuf, qlen, qtype, id); 612 613 if(got_exit_signal) 614 break; 615 616 if(serverlogslocked) { 617 serverlogslocked = 0; 618 clear_advisor_read_lock(loglockfile); 619 } 620 621 logmsg("end of one transfer"); 622 623 } 624 625 dnsd_cleanup: 626 627 #if 0 628 if((peer != sock) && (peer != CURL_SOCKET_BAD)) 629 sclose(peer); 630 #endif 631 632 if(sock != CURL_SOCKET_BAD) 633 sclose(sock); 634 635 if(got_exit_signal) 636 logmsg("signalled to die"); 637 638 if(dnsd_wrotepidfile) 639 unlink(pidname); 640 if(dnsd_wroteportfile) 641 unlink(portname); 642 643 if(serverlogslocked) { 644 serverlogslocked = 0; 645 clear_advisor_read_lock(loglockfile); 646 } 647 648 restore_signal_handlers(true); 649 650 if(got_exit_signal) { 651 logmsg("========> %s dnsd (port: %d pid: %ld) exits with signal (%d)", 652 ipv_inuse, (int)port, (long)our_getpid(), exit_signal); 653 /* 654 * To properly set the return status of the process we 655 * must raise the same signal SIGINT or SIGTERM that we 656 * caught and let the old handler take care of it. 657 */ 658 raise(exit_signal); 659 } 660 661 logmsg("========> dnsd quits"); 662 return result; 663 }