/*
* 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");
}
}
syntax highlighted by Code2HTML, v. 0.9.1