/* * Copyright (C) 2000, 2001 Nominum, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (C) 2004 - 2006 Nominum, Inc. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose with or without fee is hereby granted, * provided that the above copyright notice and this permission notice * appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Common code for dnsperf and resperf */ #define DEFINE_GLOBALS #include "common.h" const char *rcode_strings[] = { "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED", "YXDOMAIN", "YXRRSET", "NXRRSET", "NOTAUTH", "NOTZONE", "rcode11", "rcode12", "rcode13", "rcode14", "rcode15" }; /* * show_usage_common: * Print out usage/syntax information common to both programs */ void show_usage_common() { fprintf(stderr, "\nUsage: %s [-d datafile] [-s server_addr] [-p port]\n" " [-b bufsize] [-f family] [-e] [-D]\n" " [-y name:secret] [-v] [-A] [-h]\n", progname); } void show_options_common() { fprintf(stderr, " -d specifies the input data file (default: stdin)\n" " -s sets the server to query (default: %s)\n" " -p sets the port on which to query the server (default: %d)\n" " -b set socket send/receive buffer size in kilobytes (default: %d k)\n" " -f specify address family of DNS transport, inet or inet6 (default: any)\n" " -e enable EDNS 0\n" " -D set the DNSSEC OK bit (implies EDNS)\n" " -y specifies the TSIG name and secret (no default)\n" " -A report command-line arguments\n" " -h print this usage\n", DEF_SERVER_TO_QUERY, DEF_SERVER_PORT, DEF_BUFFER_SIZE); } void show_usage(void) { show_usage_common(); show_usage_prog(); show_options_common(); show_options_prog(); fprintf(stderr, "\n"); } /* * parse_udouble: * Converts a string to an unsigned double */ isc_result_t parse_udouble(double *dp, const char *string) { char c; int index = 0; isc_boolean_t seen_dot = ISC_FALSE; while (string[index] != 0) { c = string[index]; if (c == '.') { if (seen_dot) { return (ISC_R_BADNUMBER); } else { seen_dot = ISC_TRUE; } } else if (c < '0' || c > '9') { return (ISC_R_BADNUMBER); } index++; } *dp = atof(string); return (ISC_R_SUCCESS); } /* * get_input_line: * Get the next non-comment line from the input file * * Put text in line, up to max of n chars, removing trailing newlines. * Skip comment lines and empty lines. */ isc_result_t get_input_line(char *line, int n) { char *str; unsigned int len; while (ISC_TRUE) { str = fgets(line, n, datafile); if (str == NULL) return (ISC_R_EOF); if (str[0] != COMMENT_CHAR && str[0] != '\n') break; } /* * We should really test if we saw the whole line, and either * concatenate or flush and warn until the next complete line. */ len = strlen(str); if (str[len - 1] == '\n') str[len - 1] = '\0'; return (ISC_R_SUCCESS); } static void buffer_fromstring(isc_buffer_t *buffer, char *s) { int len; len = strlen(s); isc_buffer_init(buffer, s, len); isc_buffer_add(buffer, len); } /* * Appends an OPT record to the packet. */ static isc_result_t add_edns(isc_buffer_t *packet) { unsigned char *base; if (isc_buffer_availablelength(packet) < EDNSLEN) { fprintf(stderr, "Failed to add OPT to query packet\n"); return (ISC_R_NOSPACE); } base = isc_buffer_base(packet); isc_buffer_putuint8(packet, 0); /* root name */ isc_buffer_putuint16(packet, dns_rdatatype_opt);/* type */ isc_buffer_putuint16(packet, MAX_EDNS_PACKET); /* class */ isc_buffer_putuint8(packet, 0); /* xrcode */ isc_buffer_putuint8(packet, 0); /* version */ if (dnssec) /* flags */ isc_buffer_putuint16(packet, 0x8000); else isc_buffer_putuint16(packet, 0); isc_buffer_putuint16(packet, 0); /* rdlen */ base[11]++; /* increment record count */ return (ISC_R_SUCCESS); } /* * Appends a TSIG record to the packet. */ static isc_result_t add_tsig(isc_buffer_t *packet) { unsigned char *base; isc_hmacmd5_t hmac; isc_region_t name_r; unsigned int rdlen, totallen; unsigned char tmpdata[512]; isc_buffer_t tmp; isc_uint32_t now; unsigned char digest[ISC_MD5_DIGESTLENGTH]; unsigned int digest_len; isc_hmacmd5_init(&hmac, isc_buffer_base(&tsigsecret), isc_buffer_usedlength(&tsigsecret)); now = time(NULL); dns_name_toregion(tsigname, &name_r); digest_len = ISC_MD5_DIGESTLENGTH; /* Make sure everything will fit */ rdlen = tsigalg.length + 16 + digest_len; totallen = name_r.length + 10 + rdlen; if (totallen > isc_buffer_availablelength(packet)) { fprintf(stderr, "adding TSIG: out of space\n"); return (ISC_R_NOSPACE); } base = isc_buffer_base(packet); /* Digest the message */ isc_hmacmd5_update(&hmac, isc_buffer_base(packet), isc_buffer_usedlength(packet)); /* Digest the TSIG record */ isc_buffer_init(&tmp, tmpdata, sizeof tmpdata); isc_buffer_copyregion(&tmp, &name_r); /* name */ isc_buffer_putuint16(&tmp, dns_rdataclass_any); /* class */ isc_buffer_putuint32(&tmp, 0); /* ttl */ isc_buffer_copyregion(&tmp, &tsigalg); /* alg */ isc_buffer_putuint16(&tmp, 0); /* time high */ isc_buffer_putuint32(&tmp, now); /* time low */ isc_buffer_putuint16(&tmp, 300); /* fudge */ isc_buffer_putuint16(&tmp, 0); /* error */ isc_buffer_putuint16(&tmp, 0); /* other length */ isc_hmacmd5_update(&hmac, isc_buffer_base(&tmp), isc_buffer_usedlength(&tmp)); isc_hmacmd5_sign(&hmac, digest); /* Add the TSIG record. */ isc_buffer_copyregion(packet, &name_r); /* name */ isc_buffer_putuint16(packet, dns_rdatatype_tsig); /* type */ isc_buffer_putuint16(packet, dns_rdataclass_any); /* class */ isc_buffer_putuint32(packet, 0); /* ttl */ isc_buffer_putuint16(packet, rdlen); /* rdlen */ isc_buffer_copyregion(packet, &tsigalg); /* alg */ isc_buffer_putuint16(packet, 0); /* time high */ isc_buffer_putuint32(packet, now); /* time low */ isc_buffer_putuint16(packet, 300); /* fudge */ isc_buffer_putuint16(packet, digest_len); /* digest len */ isc_buffer_putmem(packet, digest, digest_len); /* digest */ isc_buffer_putmem(packet, base, 2); /* orig ID */ isc_buffer_putuint16(packet, 0); /* error */ isc_buffer_putuint16(packet, 0); /* other len */ base[11]++; /* increment record count */ return (ISC_R_SUCCESS); } static isc_result_t send_datagram(isc_buffer_t *msg) { unsigned char *base; int len; int bytes_sent; base = isc_buffer_base(msg); len = isc_buffer_usedlength(msg); bytes_sent = sendto(query_socket, base, len, 0, &server_addr.type.sa, server_addr.length); if (bytes_sent == -1) { fprintf(stderr, "Failed to send packet to %s\n", server_addr_str); return (ISC_R_FAILURE); } if (bytes_sent != len) fprintf(stderr, "Warning: incomplete packet sent\n"); total_request_size += len; return (ISC_R_SUCCESS); } /* * open_datafile: * Open the data file ready for reading */ static void open_datafile(void) { if (datafile_name == NULL) datafile = stdin; else { datafile = fopen(datafile_name, "r"); if (datafile == NULL) { fprintf(stderr, "Error: unable to open datafile: %s\n", datafile_name); exit(1); } } } static void set_server_sa(void) { isc_sockaddr_t addrs[8]; int count, i; isc_netaddr_t na; isc_result_t result; count = 0; result = bind9_getaddresses(server_name, server_port, addrs, 8, &count); if (result == ISC_R_SUCCESS) { for (i = 0; i < count; i++) { if (isc_sockaddr_pf(&addrs[i]) == family || family == AF_UNSPEC) { server_addr = addrs[i]; isc_netaddr_fromsockaddr(&na, &server_addr); isc_netaddr_format(&na, server_addr_str, sizeof(server_addr_str)); return; } } } fprintf(stderr, "Error: unknown server name: %s\n", server_name); exit(1); } /* * open_socket: * Open a socket for the queries. */ static void open_socket(void) { isc_sockaddr_t sockaddr; int sock; int ret; int bufsize; int flags; isc_sockaddr_anyofpf(&sockaddr, isc_sockaddr_pf(&server_addr)); sock = socket(isc_sockaddr_pf(&sockaddr), SOCK_DGRAM, 0); if (sock == -1) { fprintf(stderr, "Error: socket: %s", strerror(errno)); exit(1); } #if defined(AF_INET6) && defined(IPV6_V6ONLY) if (isc_sockaddr_pf(&sockaddr) == AF_INET6) { int on = 1; if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) { fprintf(stderr, "Warning: setsockopt(IPV6_V6ONLY) failed\n"); } } #endif if (bind(sock, &sockaddr.type.sa, sockaddr.length) == -1) fprintf(stderr, "Error: bind call failed"); bufsize = 1024 * socket_bufsize; ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &bufsize, sizeof(bufsize)); if (ret < 0) fprintf(stderr, "Warning: setsockbuf(SO_RCVBUF) failed\n"); ret = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &bufsize, sizeof(bufsize)); if (ret < 0) fprintf(stderr, "Warning: setsockbuf(SO_SNDBUF) failed\n"); flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { fprintf(stderr, "Error: fcntl(F_GETFL)"); exit(1); } ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK); if (ret < 0) { fprintf(stderr, "Error: fcntl(F_SETFL)"); exit(1); } query_socket = sock; } /* * setup: * Set configuration options from command line arguments * Open datafile for reading */ isc_result_t setup(int argc, char **argv, const char *progopts) { int i; isc_result_t result; progname = isc_file_basename(argv[0]); dns_result_register(); ISC_LIST_INIT(outstanding_list); ISC_LIST_INIT(instanding_list); for (i = 0; i < NQIDS; i++) { ISC_LINK_INIT(&status[i], link); ISC_LIST_APPEND(instanding_list, &status[i], link); status[i].list = &instanding_list; } result = parse_args(argc, argv, progopts); if (result != ISC_R_SUCCESS) { show_usage(); return (result); } result = isc_mem_create(0, 0, &mctx); if (result != ISC_R_SUCCESS) { fprintf(stderr, "creating memory context: %s\n", isc_result_totext(result)); return (result); } if (update_mode) { result = isc_lex_create(mctx, 1024, &lexer); if (result != ISC_R_SUCCESS) { fprintf(stderr, "creating lexer: %s\n", isc_result_totext(result)); return (result); } } if (tsigkeyarg != NULL) { char *colon, *secret; isc_buffer_t source; DE_CONST(TSIG_HMACMD5_NAME, tsigalg.base); tsigalg.length = strlen(TSIG_HMACMD5_NAME) + 1; colon = strchr(tsigkeyarg, ':'); if (colon == NULL) fprintf(stderr, "Invalid TSIG name:secret\n"); secret = colon + 1; isc_buffer_init(&source, tsigkeyarg, colon - tsigkeyarg); isc_buffer_add(&source, colon - tsigkeyarg); dns_fixedname_init(&tsigfname); tsigname = dns_fixedname_name(&tsigfname); result = dns_name_fromtext(tsigname, &source, dns_rootname, DNS_NAME_DOWNCASE, NULL); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid TSIG key name '%.*s'\n", (int)(colon - tsigkeyarg), tsigkeyarg); return (result); } isc_buffer_init(&tsigsecret, tsigdata, sizeof(tsigdata)); result = isc_base64_decodestring(colon + 1, &tsigsecret); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid TSIG secret '%s'\n", secret); return (result); } } result = dns_compress_init(&compress, 0, mctx); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Error creating compression context: %s\n", isc_result_totext(result)); return (result); } dns_compress_setmethods(&compress, DNS_COMPRESS_GLOBAL14); open_datafile(); set_server_sa(); open_socket(); return (ISC_R_SUCCESS); } void cleanup(void) { dns_compress_invalidate(&compress); if (lexer != NULL) isc_lex_destroy(&lexer); if (mctx != NULL) isc_mem_destroy(&mctx); (void) close(query_socket); if (datafile != stdin && datafile != NULL) (void) fclose(datafile); } /* * Update the time cache. */ void update_current_time(void) { if (gettimeofday(&time_now, NULL) == -1) { fprintf(stderr, "gettimeofday(): %s\n", strerror(errno)); exit(1); } } /* * difftv: * Find the difference in seconds between two timeval structs. * * Return the difference between tv1 and tv2 in seconds as a double. */ double difftv(const struct timeval *tv1, const struct timeval *tv2) { long diff_sec, diff_usec; diff_sec = tv1->tv_sec - tv2->tv_sec; diff_usec = tv1->tv_usec - tv2->tv_usec; return (double)diff_sec + ((double)diff_usec / 1000000.0); } /* * Reads a line of input containing a query and sends it. */ static isc_result_t do_query(isc_buffer_t *msg, char *input, int qid) { isc_buffer_t buffer; char *domain_str, *type_str; int domain_len; dns_fixedname_t fname; dns_name_t *name; isc_region_t name_r; isc_consttextregion_t qtype_r; dns_rdatatype_t qtype; isc_result_t result; domain_str = input; domain_len = strcspn(input, WHITESPACE); type_str = input + domain_len; while (isspace(*type_str & 0xff)) type_str++; if (*type_str == 0) { fprintf(stderr, "Invalid query input format: %s\n", input); return (ISC_R_FAILURE); } dns_fixedname_init(&fname); name = dns_fixedname_name(&fname); isc_buffer_init(&buffer, domain_str, domain_len); isc_buffer_add(&buffer, domain_len); result = dns_name_fromtext(name, &buffer, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid domain name: %.*s\n", domain_len, domain_str); return (result); } qtype_r.base = type_str; qtype_r.length = strlen(type_str); result = dns_rdatatype_fromtext(&qtype, (isc_textregion_t *) &qtype_r); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid query type: %s\n", type_str); return (ISC_R_FAILURE); } /* Create the DNS packet header */ isc_buffer_putuint16(msg, qid); isc_buffer_putuint16(msg, DNS_MESSAGEFLAG_RD); /* flags */ isc_buffer_putuint16(msg, 1); /* qdcount */ isc_buffer_putuint16(msg, 0); /* ancount */ isc_buffer_putuint16(msg, 0); /* aucount */ isc_buffer_putuint16(msg, 0); /* arcount */ /* Create the question section */ dns_name_toregion(name, &name_r); isc_buffer_copyregion(msg, &name_r); isc_buffer_putuint16(msg, qtype); isc_buffer_putuint16(msg, dns_rdataclass_in); if (edns) { result = add_edns(msg); if (result != ISC_R_SUCCESS) return (result); } if (tsigname != NULL) { result = add_tsig(msg); if (result != ISC_R_SUCCESS) return (result); } return send_datagram(msg); } /* * Reads one line containing an individual update for a dynamic update message. */ static isc_result_t read_update_line(char *line, char *str, dns_name_t *zname, int want_ttl, int need_type, int want_rdata, int need_rdata, dns_name_t *name, isc_uint32_t *ttlp, dns_rdatatype_t *typep, dns_rdata_t *rdata, isc_buffer_t *rdatabuf) { char *token; isc_buffer_t buffer; isc_textregion_t src; dns_rdatacallbacks_t callbacks; isc_result_t result; /* Read the owner name */ token = strsep(&str, WHITESPACE); if (token == NULL || *token == 0) { fprintf(stderr, "Invalid update command: %s\n", line); return (ISC_R_FAILURE); } buffer_fromstring(&buffer, token); result = dns_name_fromtext(name, &buffer, zname, 0, NULL); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid owner name: %s\n", token); return (result); } /* Read the ttl */ if (want_ttl) { token = strsep(&str, WHITESPACE); if (token == NULL || *token == 0) { fprintf(stderr, "Invalid update command: %s\n", line); return (ISC_R_FAILURE); } src.base = token; src.length = strlen(token); result = dns_ttl_fromtext(&src, ttlp); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid ttl: %s\n", token); return (result); } } /* Read the type */ token = strsep(&str, WHITESPACE); if (token == NULL || *token == 0) { if (!need_type) return (ISC_R_SUCCESS); fprintf(stderr, "Invalid update command: %s\n", line); return (ISC_R_SUCCESS); } src.base = token; src.length = strlen(token); result = dns_rdatatype_fromtext(typep, &src); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid type: %s\n", token); return (result); } /* Read the rdata */ if (!want_rdata) return (ISC_R_SUCCESS); if (str != NULL) { while (isspace(*str & 0xff)) str++; } if (str == NULL || *str == 0) { if (!need_rdata) return (ISC_R_SUCCESS); fprintf(stderr, "Invalid update command: %s\n", line); return (ISC_R_FAILURE); } buffer_fromstring(&buffer, str); result = isc_lex_openbuffer(lexer, &buffer); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Setting up lexer: %s\n", isc_result_totext(result)); return (result); } dns_rdatacallbacks_init_stdio(&callbacks); result = dns_rdata_fromtext(rdata, dns_rdataclass_in, *typep, lexer, zname, 0, mctx, rdatabuf, &callbacks); (void)isc_lex_close(lexer); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Parsing rdata: %s\n", str); return (result); } return (ISC_R_SUCCESS); } /* * Skips all lines until a line containing "send" is seen or there are no lines * left. */ static void skip_update(char *line, int len) { while (get_input_line(line, len) == ISC_R_SUCCESS) { if (strcmp(line, "send") == 0) break; } } /* * Reads a complete dynamic update message and sends it. */ static isc_result_t do_update(isc_buffer_t *msg, char *input, int qid) { char *msgbase; isc_buffer_t buffer, rdlenbuf, rdatabuf; char line[MAX_INPUT_LEN + 1]; char copy[MAX_INPUT_LEN + 1]; unsigned char rdataarray[MAX_INPUT_LEN + 1]; char *str, *token; isc_boolean_t is_update; int updates = 0; int prereqs = 0; dns_fixedname_t fzname, foname; dns_name_t *zname, *oname; isc_uint32_t ttl; dns_rdatatype_t rdtype; dns_rdataclass_t rdclass; dns_rdata_t rdata; isc_uint16_t rdlen; isc_result_t result; /* Reset compression context */ dns_compress_rollback(&compress, 0); msgbase = isc_buffer_base(msg); /* Create the DNS packet header */ isc_buffer_putuint16(msg, qid); isc_buffer_putuint16(msg, dns_opcode_update << 11); /* flags */ isc_buffer_putuint16(msg, 1); /* qdcount */ isc_buffer_putuint16(msg, 0); /* ancount */ isc_buffer_putuint16(msg, 0); /* aucount */ isc_buffer_putuint16(msg, 0); /* arcount */ /* Initialize */ dns_fixedname_init(&fzname); zname = dns_fixedname_name(&fzname); dns_fixedname_init(&foname); oname = dns_fixedname_name(&foname); /* Parse zone name */ buffer_fromstring(&buffer, input); result = dns_name_fromtext(zname, &buffer, dns_rootname, 0, NULL); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid zone name: %s\n", input); skip_update(line, sizeof(line)); return (result); } /* Render zone section */ result = dns_name_towire(zname, &compress, msg); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Error rendering zone name: %s\n", isc_result_totext(result)); skip_update(line, sizeof(line)); return (result); } isc_buffer_putuint16(msg, dns_rdatatype_soa); isc_buffer_putuint16(msg, dns_rdataclass_in); while (ISC_TRUE) { if (get_input_line(line, sizeof(line)) != ISC_R_SUCCESS) { fprintf(stderr, "Warning: incomplete update\n"); return (ISC_R_EOF); } strcpy(copy, line); str = copy; ttl = 0; rdtype = dns_rdatatype_any; isc_buffer_init(&rdatabuf, rdataarray, sizeof(rdataarray)); dns_rdata_init(&rdata); rdlen = 0; rdclass = dns_rdataclass_in; is_update = ISC_FALSE; token = strsep(&str, WHITESPACE); if (strcasecmp(token, "send") == 0) { break; } else if (strcasecmp(token, "add") == 0) { result = read_update_line(line, str, zname, ISC_TRUE, ISC_TRUE, ISC_TRUE, ISC_TRUE, oname, &ttl, &rdtype, &rdata, &rdatabuf); rdclass = dns_rdataclass_in; is_update = ISC_TRUE; } else if (strcasecmp(token, "delete") == 0) { result = read_update_line(line, str, zname, ISC_FALSE, ISC_FALSE, ISC_TRUE, ISC_FALSE, oname, &ttl, &rdtype, &rdata, &rdatabuf); if (isc_buffer_usedlength(&rdatabuf) > 0) rdclass = dns_rdataclass_none; else rdclass = dns_rdataclass_any; is_update = ISC_TRUE; } else if (strcasecmp(token, "require") == 0) { result = read_update_line(line, str, zname, ISC_FALSE, ISC_FALSE, ISC_TRUE, ISC_FALSE, oname, &ttl, &rdtype, &rdata, &rdatabuf); if (isc_buffer_usedlength(&rdatabuf) > 0) rdclass = dns_rdataclass_in; else rdclass = dns_rdataclass_any; is_update = ISC_FALSE; } else if (strcasecmp(token, "prohibit") == 0) { result = read_update_line(line, str, zname, ISC_FALSE, ISC_FALSE, ISC_FALSE, ISC_FALSE, oname, &ttl, &rdtype, &rdata, &rdatabuf); rdclass = dns_rdataclass_none; is_update = ISC_FALSE; } else { fprintf(stderr, "Invalid update command: %s\n", line); result = ISC_R_FAILURE; } if (result != ISC_R_SUCCESS) { skip_update(line, sizeof(line)); return (result); } if (!is_update && updates > 0) { fprintf(stderr, "Error: prereqs must precede updates\n"); skip_update(line, sizeof(line)); return (ISC_R_FAILURE); } /* Render record */ result = dns_name_towire(oname, &compress, msg); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Error rendering record name: %s\n", isc_result_totext(result)); skip_update(line, sizeof(line)); return (result); } if (isc_buffer_availablelength(msg) < 10) { fprintf(stderr, "Error: out of space in message buffer\n"); skip_update(line, sizeof(line)); return (ISC_R_NOSPACE); } isc_buffer_putuint16(msg, rdtype); isc_buffer_putuint16(msg, rdclass); isc_buffer_putuint32(msg, ttl); rdlenbuf = *msg; isc_buffer_putuint16(msg, 0); /* rdlen */ rdlen = isc_buffer_usedlength(&rdatabuf); if (rdlen > 0) { result = dns_rdata_towire(&rdata, &compress, msg); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Error rendering rdata: %s\n", isc_result_totext(result)); skip_update(line, sizeof(line)); return (result); } rdlen = msg->used - rdlenbuf.used - 2; isc_buffer_putuint16(&rdlenbuf, rdlen); } if (is_update) updates++; else prereqs++; } msgbase[7] = prereqs; /* ANCOUNT = number of prereqs */ msgbase[9] = updates; /* AUCOUNT = number of updates */ if (edns) { result = add_edns(msg); if (result != ISC_R_SUCCESS) return (result); } if (tsigname != NULL) { result = add_tsig(msg); if (result != ISC_R_SUCCESS) return (result); } return send_datagram(msg); } /* * process_line: * Send a query or update based on a line of input * Return ISC_R_NOMORE if we ran out of query IDs. */ isc_result_t process_line(char *query_desc) { struct query_status *s; isc_buffer_t msg; isc_result_t result; int qid; /* Find an unused query ID. */ s = ISC_LIST_HEAD(instanding_list); if (! s) return ISC_R_NOMORE; qid = s - status; /* Create message buffer */ if (edns) isc_buffer_init(&msg, outpacket_buffer, MAX_EDNS_PACKET); else isc_buffer_init(&msg, outpacket_buffer, MAX_UDP_PACKET); if (update_mode) result = do_update(&msg, query_desc, qid); else result = do_query(&msg, query_desc, qid); if (result != ISC_R_SUCCESS) return result; /* Register the query in status[] */ if (verbose) { s->desc = strdup(query_desc); if (s->desc == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } } s->sent_timestamp = time_now; /* Link it into the queue of outstanding queries */ ISC_LIST_UNLINK(instanding_list, s, link); ISC_LIST_PREPEND(outstanding_list, s, link); s->list = &outstanding_list; num_queries_sent++; num_queries_outstanding++; return ISC_R_SUCCESS; } /* * try_process_response: * * Receive from the given socket & process an individual response packet. * Remove it from the list of open queries (status[]) and decrement the * number of outstanding queries if it matches an open query. * * Returns whether a response was available. */ isc_boolean_t try_process_response(int sockfd) { int numbytes, resp_id, flags; isc_buffer_t buffer; numbytes = recvfrom(sockfd, inpacket_buffer, sizeof(inpacket_buffer), 0, NULL, NULL); if (numbytes == -1) { if (errno == EAGAIN || errno == EINTR) { return (ISC_FALSE); } else { fprintf(stderr, "Error receiving datagram: %s\n", strerror(errno)); exit(1); } } else if (numbytes < 4) { fprintf(stderr, "Warning: received short response\n"); return (ISC_TRUE); } isc_buffer_init(&buffer, inpacket_buffer, numbytes); isc_buffer_add(&buffer, numbytes); resp_id = isc_buffer_getuint16(&buffer); flags = isc_buffer_getuint16(&buffer); if (register_response(resp_id, flags & 0xF)) total_response_size += numbytes; return (ISC_TRUE); } /* * parse_args: * Parse program arguments and set configuration options */ isc_result_t parse_args(int argc, char **argv, const char *progopts) { int c; isc_result_t result; char opts[100]; snprintf(opts, sizeof opts, "f:Ad:s:p:b:eDhy:%s", progopts); while ((c = getopt(argc, argv, opts)) != -1) { switch (c) { case 'f': if (strcmp(optarg, "inet") == 0) family = AF_INET; #ifdef AF_INET6 else if (strcmp(optarg, "inet6") == 0) family = AF_INET6; #endif else if (strcmp(optarg, "any") == 0) family = AF_UNSPEC; else { fprintf(stderr, "Invalid address family: %s\n", optarg); return (ISC_R_FAILURE); } break; case 'A': print_arguments = ISC_TRUE; break; case 'd': datafile_name = optarg; break; case 's': server_name = optarg; break; case 'p': result = isc_parse_uint16(&server_port, optarg, 10); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid port: %s\n", optarg); return (ISC_R_FAILURE); } break; case 'b': result = isc_parse_uint32(&socket_bufsize, optarg, 10); if (result != ISC_R_SUCCESS) { fprintf(stderr, "Invalid socket buffer size: " "%s\n", optarg); return (ISC_R_FAILURE); } break; case 'e': edns = ISC_TRUE; break; case 'D': dnssec = ISC_TRUE; edns = ISC_TRUE; break; case 'h': show_usage(); exit(0); case 'y': tsigkeyarg = optarg; break; default: result = handle_prog_opt(c); if (result == ISC_R_NOTFOUND) return result; break; } } if (optind != argc) { fprintf(stderr, "unexpected argument %s\n", argv[optind]); return ISC_R_FAILURE; } return (ISC_R_SUCCESS); } void maybe_print_args(int argc, char **argv) { if (print_arguments) { int i; printf("[Status] Arguments: "); for (i = 1; i < argc; i++) { printf("%s ", argv[i]); } printf("\n"); } }