/* * ldns-notify.c - ldns-notify(8) * * Copyright (c) 2001-2008, NLnet Labs, All right reserved * * See LICENSE for the license * * send a notify packet to a server */ #include "config.h" /* ldns */ #include #ifdef HAVE_NETDB_H #include #endif #include static int verbose = 1; static int max_num_retry = 15; /* times to try */ static void usage(void) { fprintf(stderr, "usage: ldns-notify [other options] -z zone \n"); fprintf(stderr, "Ldns notify utility\n\n"); fprintf(stderr, " Supported options:\n"); fprintf(stderr, "\t-z zone\t\tThe zone\n"); fprintf(stderr, "\t-I
\tsource address to query from\n"); fprintf(stderr, "\t-s version\tSOA version number to include\n"); fprintf(stderr, "\t-y \tspecify named base64 tsig key" ", and optional an\n\t\t\t" "algorithm (defaults to hmac-md5.sig-alg.reg.int)\n"); fprintf(stderr, "\t-p port\t\tport to use to send to\n"); fprintf(stderr, "\t-v\t\tPrint version information\n"); fprintf(stderr, "\t-d\t\tPrint verbose debug information\n"); fprintf(stderr, "\t-r num\t\tmax number of retries (%d)\n", max_num_retry); fprintf(stderr, "\t-h\t\tPrint this help information\n\n"); fprintf(stderr, "Report bugs to \n"); exit(1); } static void version(void) { fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); fprintf(stderr, "Written by NLnet Labs.\n\n"); fprintf(stderr, "Copyright (C) 2001-2008 NLnet Labs. This is free software.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS\n" "FOR A PARTICULAR PURPOSE.\n"); exit(0); } static void notify_host(int s, struct addrinfo* res, uint8_t* wire, size_t wiresize, const char* addrstr) { int timeout_retry = 5; /* seconds */ int num_retry = max_num_retry; #ifndef S_SPLINT_S fd_set rfds; #endif struct timeval tv; int retval = 0; ssize_t received = 0; int got_ack = 0; socklen_t addrlen = 0; uint8_t replybuf[2048]; ldns_status status; ldns_pkt* pkt = NULL; while(!got_ack) { /* send it */ if(sendto(s, (void*)wire, wiresize, 0, res->ai_addr, res->ai_addrlen) == -1) { printf("warning: send to %s failed: %s\n", addrstr, strerror(errno)); #ifndef USE_WINSOCK close(s); #else closesocket(s); #endif return; } /* wait for ACK packet */ #ifndef S_SPLINT_S FD_ZERO(&rfds); FD_SET(s, &rfds); tv.tv_sec = timeout_retry; /* seconds */ #endif tv.tv_usec = 0; /* microseconds */ retval = select(s + 1, &rfds, NULL, NULL, &tv); if (retval == -1) { printf("error waiting for reply from %s: %s\n", addrstr, strerror(errno)); #ifndef USE_WINSOCK close(s); #else closesocket(s); #endif return; } if(retval == 0) { num_retry--; if(num_retry == 0) { printf("error: failed to send notify to %s.\n", addrstr); exit(1); } printf("timeout (%d s) expired, retry notify to %s.\n", timeout_retry, addrstr); } if (retval == 1) { got_ack = 1; } } /* got reply */ addrlen = res->ai_addrlen; received = recvfrom(s, (void*)replybuf, sizeof(replybuf), 0, res->ai_addr, &addrlen); res->ai_addrlen = addrlen; #ifndef USE_WINSOCK close(s); #else closesocket(s); #endif if (received == -1) { printf("recv %s failed: %s\n", addrstr, strerror(errno)); return; } /* check reply */ status = ldns_wire2pkt(&pkt, replybuf, (size_t)received); if(status != LDNS_STATUS_OK) { ssize_t i; printf("Could not parse reply packet: %s\n", ldns_get_errorstr_by_id(status)); if (verbose > 1) { printf("hexdump of reply: "); for(i=0; i 1) { printf("hexdump of reply: "); for(i=0; i %s\n", optarg); exit(1); } tsig_name = optarg; *tsig_sep++ = '\0'; tsig_data = tsig_sep; if ((tsig_sep = strchr(tsig_sep, ':'))) { *tsig_sep++ = '\0'; tsig_algo = tsig_sep; } else { tsig_algo = "hmac-md5.sig-alg.reg.int."; } /* With dig TSIG keys are also specified with -y, * but format with drill is: -y * and with dig: -y [hmac:]name:key * * When we detect an unknown tsig algorithm in algo, * but a known algorithm in name, we cane assume dig * order was used. * * Following if statement is to anticipate and correct * dig order */ if (strcasecmp(tsig_algo, "hmac-md5.sig-alg.reg.int")&& strcasecmp(tsig_algo, "hmac-md5") && strcasecmp(tsig_algo, "hmac-sha1") && strcasecmp(tsig_algo, "hmac-sha256") && strcasecmp(tsig_algo, "hmac-sha384") && strcasecmp(tsig_algo, "hmac-sha512") && ! (strcasecmp(tsig_name, "hmac-md5.sig-alg.reg.int") && strcasecmp(tsig_name, "hmac-md5") && strcasecmp(tsig_name, "hmac-sha1") && strcasecmp(tsig_name, "hmac-sha256") && strcasecmp(tsig_name, "hmac-sha384") && strcasecmp(tsig_name, "hmac-sha512"))) { /* Roll options */ const char *tmp_tsig_algo = tsig_name; tsig_name = tsig_data; tsig_data = tsig_algo; tsig_algo = tmp_tsig_algo; } printf("Sign with name: %s, data: %s, algorithm: %s\n" , tsig_name, tsig_data, tsig_algo); break; case 'z': zone_name = optarg; ldns_zone_name = ldns_dname_new_frm_str(zone_name); if(!ldns_zone_name) { printf("cannot parse zone name: %s\n", zone_name); exit(1); } break; case 'I': memset(&from_hints, 0, sizeof(from_hints)); from_hints.ai_family = AF_UNSPEC; from_hints.ai_socktype = SOCK_DGRAM; from_hints.ai_protocol = IPPROTO_UDP; from_hints.ai_flags = AI_NUMERICHOST; error = getaddrinfo(optarg, 0, &from_hints, &from0); if (error) { printf("bad address: %s: %s\n", optarg, gai_strerror(error)); exit(EXIT_FAILURE); } break; case 'v': version(); /* fallthrough */ case 'h': case '?': default: usage(); } } argc -= optind; argv += optind; if (argc == 0 || zone_name == NULL) { usage(); } notify = ldns_pkt_new(); question = ldns_rr_new(); if (!notify || !question) { /* bail out */ printf("error: cannot create ldns types\n"); exit(1); } /* create the rr for inside the pkt */ ldns_rr_set_class(question, LDNS_RR_CLASS_IN); ldns_rr_set_owner(question, ldns_zone_name); ldns_rr_set_type(question, LDNS_RR_TYPE_SOA); ldns_rr_set_question(question, true); ldns_pkt_set_opcode(notify, LDNS_PACKET_NOTIFY); ldns_pkt_push_rr(notify, LDNS_SECTION_QUESTION, question); ldns_pkt_set_aa(notify, true); ldns_pkt_set_random_id(notify); if(include_soa) { char buf[10240]; ldns_rr *soa_rr=NULL; ldns_rdf *prev=NULL; snprintf(buf, sizeof(buf), "%s 3600 IN SOA . . %u 0 0 0 0", zone_name, (unsigned)soa_version); /*printf("Adding soa %s\n", buf);*/ status = ldns_rr_new_frm_str(&soa_rr, buf, 3600, NULL, &prev); if(status != LDNS_STATUS_OK) { printf("Error adding SOA version: %s\n", ldns_get_errorstr_by_id(status)); } ldns_pkt_push_rr(notify, LDNS_SECTION_ANSWER, soa_rr); } if(tsig_name && tsig_data) { #ifdef HAVE_SSL status = ldns_pkt_tsig_sign(notify, tsig_name, tsig_data, 300, tsig_algo, NULL); if(status != LDNS_STATUS_OK) { printf("Error TSIG sign query: %s\n", ldns_get_errorstr_by_id(status)); } #else fprintf(stderr, "Warning: TSIG needs OpenSSL support, which has not been compiled in, TSIG skipped\n"); #endif } if(verbose) { printf("# Sending packet:\n"); ldns_pkt_print(stdout, notify); } status = ldns_pkt2wire(&wire, notify, &wiresize); if (status) { printf("Error converting notify packet to hex: %s\n", ldns_get_errorstr_by_id(status)); } else if(wiresize == 0) { printf("Error converting notify packet to hex.\n"); exit(1); } if(do_hexdump && verbose > 1) { printf("Hexdump of notify packet:\n"); for(i=0; i<(int)wiresize; i++) printf("%02x", (unsigned)wire[i]); printf("\n"); } for(i=0; iai_next) { int s; if (from0 && ai_res->ai_family != from0->ai_family) continue; s = socket(ai_res->ai_family, ai_res->ai_socktype, ai_res->ai_protocol); if(s == -1) continue; if (from0 && bind(s, from0->ai_addr, from0->ai_addrlen)) { perror("Could not bind to source IP"); exit(EXIT_FAILURE); } /* send the notify */ notify_host(s, ai_res, wire, wiresize, argv[i]); } freeaddrinfo(res0); } ldns_pkt_free(notify); free(wire); return 0; }