/* Based on getdns/src/test/getdns_query.c (commit 60be402). For license information, see end of this file. Example usage: $ dns-net2wire -q +dnssec_return_validation_chain +dnssec_return_status +dnssec_return_only_secure dfri.se */ #define _POSIX_SOURCE //#include "config.h" //#include "debug.h" #include #include #include #include #include #include #include #include #include #include #include "common.h" #define DEBUG_SCHED(...) #define MAX_TIMEOUTS FD_SETSIZE #define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" /* Eventloop based on select */ typedef struct my_eventloop { getdns_eventloop base; getdns_eventloop_event *fd_events[FD_SETSIZE]; uint64_t fd_timeout_times[FD_SETSIZE]; getdns_eventloop_event *timeout_events[MAX_TIMEOUTS]; uint64_t timeout_times[MAX_TIMEOUTS]; } my_eventloop; static uint64_t get_now_plus(uint64_t amount) { struct timeval tv; uint64_t now; if (gettimeofday(&tv, NULL)) { perror("gettimeofday() failed"); exit(EXIT_FAILURE); } now = tv.tv_sec * 1000000 + tv.tv_usec; return (now + amount * 1000) >= now ? now + amount * 1000 : -1; } getdns_return_t my_eventloop_schedule(getdns_eventloop *loop, int fd, uint64_t timeout, getdns_eventloop_event *event) { my_eventloop *my_loop = (my_eventloop *)loop; size_t i; assert(loop); assert(event); assert(fd < FD_SETSIZE); DEBUG_SCHED( "%s(loop: %p, fd: %d, timeout: %"PRIu64", event: %p)\n" , __FUNCTION__, loop, fd, timeout, event); if (fd >= 0 && (event->read_cb || event->write_cb)) { assert(my_loop->fd_events[fd] == NULL); my_loop->fd_events[fd] = event; my_loop->fd_timeout_times[fd] = get_now_plus(timeout); event->ev = (void *) (intptr_t) fd + 1; DEBUG_SCHED( "scheduled read/write at %d\n", fd); return GETDNS_RETURN_GOOD; } assert(event->timeout_cb && !event->read_cb && !event->write_cb); for (i = 0; i < MAX_TIMEOUTS; i++) { if (my_loop->timeout_events[i] == NULL) { my_loop->timeout_events[i] = event; my_loop->timeout_times[i] = get_now_plus(timeout); event->ev = (void *) (intptr_t) i + 1; DEBUG_SCHED( "scheduled timeout at %d\n", (int)i); return GETDNS_RETURN_GOOD; } } return GETDNS_RETURN_GENERIC_ERROR; } getdns_return_t my_eventloop_clear(getdns_eventloop *loop, getdns_eventloop_event *event) { my_eventloop *my_loop = (my_eventloop *)loop; size_t i; assert(loop); assert(event); DEBUG_SCHED( "%s(loop: %p, event: %p)\n", __FUNCTION__, loop, event); i = (intptr_t)event->ev - 1; assert(i >= 0 && i < FD_SETSIZE); if (event->timeout_cb && !event->read_cb && !event->write_cb) { assert(my_loop->timeout_events[i] == event); my_loop->timeout_events[i] = NULL; } else { assert(my_loop->fd_events[i] == event); my_loop->fd_events[i] = NULL; } event->ev = NULL; return GETDNS_RETURN_GOOD; } void my_eventloop_cleanup(getdns_eventloop *loop) { } void my_read_cb(int fd, getdns_eventloop_event *event) { DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); event->read_cb(event->userarg); } void my_write_cb(int fd, getdns_eventloop_event *event) { DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); event->write_cb(event->userarg); } void my_timeout_cb(int fd, getdns_eventloop_event *event) { DEBUG_SCHED( "%s(fd: %d, event: %p)\n", __FUNCTION__, fd, event); event->timeout_cb(event->userarg); } void my_eventloop_run_once(getdns_eventloop *loop, int blocking) { my_eventloop *my_loop = (my_eventloop *)loop; fd_set readfds, writefds; int fd, max_fd = -1; uint64_t now, timeout = (uint64_t)-1; size_t i; struct timeval tv; assert(loop); FD_ZERO(&readfds); FD_ZERO(&writefds); now = get_now_plus(0); for (i = 0; i < MAX_TIMEOUTS; i++) { if (!my_loop->timeout_events[i]) continue; if (now > my_loop->timeout_times[i]) my_timeout_cb(-1, my_loop->timeout_events[i]); else if (my_loop->timeout_times[i] < timeout) timeout = my_loop->timeout_times[i]; } for (fd = 0; fd < FD_SETSIZE; fd++) { if (!my_loop->fd_events[fd]) continue; if (my_loop->fd_events[fd]->read_cb) FD_SET(fd, &readfds); if (my_loop->fd_events[fd]->write_cb) FD_SET(fd, &writefds); if (fd > max_fd) max_fd = fd; if (my_loop->fd_timeout_times[fd] < timeout) timeout = my_loop->fd_timeout_times[fd]; } if (max_fd == -1 && timeout == (uint64_t)-1) return; if (! blocking || now > timeout) { tv.tv_sec = 0; tv.tv_usec = 0; } else { tv.tv_sec = (timeout - now) / 1000000; tv.tv_usec = (timeout - now) % 1000000; } if (select(max_fd + 1, &readfds, &writefds, NULL, &tv) < 0) { perror("select() failed"); exit(EXIT_FAILURE); } now = get_now_plus(0); for (fd = 0; fd < FD_SETSIZE; fd++) { if (my_loop->fd_events[fd] && my_loop->fd_events[fd]->read_cb && FD_ISSET(fd, &readfds)) my_read_cb(fd, my_loop->fd_events[fd]); if (my_loop->fd_events[fd] && my_loop->fd_events[fd]->write_cb && FD_ISSET(fd, &writefds)) my_write_cb(fd, my_loop->fd_events[fd]); if (my_loop->fd_events[fd] && my_loop->fd_events[fd]->timeout_cb && now > my_loop->fd_timeout_times[fd]) my_timeout_cb(fd, my_loop->fd_events[fd]); i = fd; if (my_loop->timeout_events[i] && my_loop->timeout_events[i]->timeout_cb && now > my_loop->timeout_times[i]) my_timeout_cb(-1, my_loop->timeout_events[i]); } } void my_eventloop_run(getdns_eventloop *loop) { my_eventloop *my_loop = (my_eventloop *)loop; size_t i; assert(loop); i = 0; while (i < MAX_TIMEOUTS) { if (my_loop->fd_events[i] || my_loop->timeout_events[i]) { my_eventloop_run_once(loop, 1); i = 0; } else { i++; } } } void my_eventloop_init(my_eventloop *loop) { static getdns_eventloop_vmt my_eventloop_vmt = { my_eventloop_cleanup, my_eventloop_schedule, my_eventloop_clear, my_eventloop_run, my_eventloop_run_once }; (void) memset(loop, 0, sizeof(my_eventloop)); loop->base.vmt = &my_eventloop_vmt; } static int quiet = 0; static int batch_mode = 0; static char *query_file = NULL; static int json = 0; static char *the_root = "."; static char *name; static getdns_context *context; static getdns_dict *extensions; static getdns_list *pubkey_pinset = NULL; static size_t pincount = 0; static uint16_t request_type = GETDNS_RRTYPE_NS; static int timeout, edns0_size, padding_blocksize; static int async = 0, interactive = 0; static enum { GENERAL, ADDRESS, HOSTNAME, SERVICE } calltype = GENERAL; int get_rrtype(const char *t); int gqldns_b64_pton(char const *src, uint8_t *target, size_t targsize) { const uint8_t pad64 = 64; /* is 64th in the b64 array */ const char* s = src; uint8_t in[4]; size_t o = 0, incount = 0; while(*s) { /* skip any character that is not base64 */ /* conceptually we do: const char* b64 = pad'=' is appended to array "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; const char* d = strchr(b64, *s++); and use d-b64; */ char d = *s++; if(d <= 'Z' && d >= 'A') d -= 'A'; else if(d <= 'z' && d >= 'a') d = d - 'a' + 26; else if(d <= '9' && d >= '0') d = d - '0' + 52; else if(d == '+') d = 62; else if(d == '/') d = 63; else if(d == '=') d = 64; else continue; in[incount++] = (uint8_t)d; if(incount != 4) continue; /* process whole block of 4 characters into 3 output bytes */ if(in[3] == pad64 && in[2] == pad64) { /* A B = = */ if(o+1 > targsize) return -1; target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); o += 1; break; /* we are done */ } else if(in[3] == pad64) { /* A B C = */ if(o+2 > targsize) return -1; target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); o += 2; break; /* we are done */ } else { if(o+3 > targsize) return -1; /* write xxxxxxyy yyyyzzzz zzwwwwww */ target[o] = (in[0]<<2) | ((in[1]&0x30)>>4); target[o+1]= ((in[1]&0x0f)<<4) | ((in[2]&0x3c)>>2); target[o+2]= ((in[2]&0x03)<<6) | in[3]; o += 3; } incount = 0; } return (int)o; } getdns_dict * ipaddr_dict(getdns_context *context, char *ipstr) { getdns_dict *r = getdns_dict_create_with_context(context); char *s = strchr(ipstr, '%'), *scope_id_str = ""; char *p = strchr(ipstr, '@'), *portstr = ""; char *t = strchr(ipstr, '#'), *tls_portstr = ""; char *n = strchr(ipstr, '~'), *tls_namestr = ""; /* ^[alg:]name:key */ char *T = strchr(ipstr, '^'), *tsig_name_str = "" , *tsig_secret_str = "" , *tsig_algorithm_str = ""; int tsig_secret_size; uint8_t tsig_secret_buf[256]; /* 4 times SHA512 */ getdns_bindata tsig_secret; uint8_t buf[sizeof(struct in6_addr)]; getdns_bindata addr; addr.data = buf; if (!r) return NULL; if (s) { *s = 0; scope_id_str = s + 1; } if (p) { *p = 0; portstr = p + 1; } if (t) { *t = 0; tls_portstr = t + 1; } if (n) { *n = 0; tls_namestr = n + 1; } if (T) { *T = 0; tsig_name_str = T + 1; if ((T = strchr(tsig_name_str, ':'))) { *T = 0; tsig_secret_str = T + 1; if ((T = strchr(tsig_secret_str, ':'))) { *T = 0; tsig_algorithm_str = tsig_name_str; tsig_name_str = tsig_secret_str; tsig_secret_str = T + 1; } } else { tsig_name_str = ""; } } if (strchr(ipstr, ':')) { getdns_dict_util_set_string(r, "address_type", "IPv6"); addr.size = 16; if (inet_pton(AF_INET6, ipstr, buf) <= 0) { getdns_dict_destroy(r); return NULL; } } else { getdns_dict_util_set_string(r, "address_type", "IPv4"); addr.size = 4; if (inet_pton(AF_INET, ipstr, buf) <= 0) { getdns_dict_destroy(r); return NULL; } } getdns_dict_set_bindata(r, "address_data", &addr); if (*portstr) getdns_dict_set_int(r, "port", (int32_t)atoi(portstr)); if (*tls_portstr) getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr)); if (*tls_namestr) { getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr); } if (*scope_id_str) getdns_dict_util_set_string(r, "scope_id", scope_id_str); if (*tsig_name_str) getdns_dict_util_set_string(r, "tsig_name", tsig_name_str); if (*tsig_algorithm_str) getdns_dict_util_set_string(r, "tsig_algorithm", tsig_algorithm_str); if (*tsig_secret_str) { tsig_secret_size = gqldns_b64_pton( tsig_secret_str, tsig_secret_buf, sizeof(tsig_secret_buf)); if (tsig_secret_size > 0) { tsig_secret.size = tsig_secret_size; tsig_secret.data = tsig_secret_buf; getdns_dict_set_bindata(r, "tsig_secret", &tsig_secret); } } return r; } static getdns_return_t fill_transport_list(getdns_context *context, char *transport_list_str, getdns_transport_list_t *transports, size_t *transport_count) { size_t max_transports = *transport_count; *transport_count = 0; for ( size_t i = 0 ; i < max_transports && i < strlen(transport_list_str) ; i++, (*transport_count)++) { switch(*(transport_list_str + i)) { case 'U': transports[i] = GETDNS_TRANSPORT_UDP; break; case 'T': transports[i] = GETDNS_TRANSPORT_TCP; break; case 'L': transports[i] = GETDNS_TRANSPORT_TLS; break; default: fprintf(stderr, "Unrecognised transport '%c' in string %s\n", *(transport_list_str + i), transport_list_str); return GETDNS_RETURN_GENERIC_ERROR; } } return GETDNS_RETURN_GOOD; } void print_usage(FILE *out, const char *progname) { fprintf(out, "usage: %s [