diff options
Diffstat (limited to 'external/unbound/testcode/delayer.c')
-rw-r--r-- | external/unbound/testcode/delayer.c | 1185 |
1 files changed, 0 insertions, 1185 deletions
diff --git a/external/unbound/testcode/delayer.c b/external/unbound/testcode/delayer.c deleted file mode 100644 index 5489b591e..000000000 --- a/external/unbound/testcode/delayer.c +++ /dev/null @@ -1,1185 +0,0 @@ -/* - * testcode/delayer.c - debug program that delays queries to a server. - * - * Copyright (c) 2008, NLnet Labs. All rights reserved. - * - * This software is open source. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of the NLNET LABS nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * \file - * - * This program delays queries made. It performs as a proxy to another - * server and delays queries to it. - */ - -#include "config.h" -#ifdef HAVE_GETOPT_H -#include <getopt.h> -#endif -#ifdef HAVE_TIME_H -#include <time.h> -#endif -#include <sys/time.h> -#include "util/net_help.h" -#include "util/config_file.h" -#include "sldns/sbuffer.h" -#include <signal.h> - -/** number of reads per select for delayer */ -#define TRIES_PER_SELECT 100 - -/** - * The ring buffer - */ -struct ringbuf { - /** base of buffer */ - uint8_t* buf; - /** size of buffer */ - size_t size; - /** low mark, items start here */ - size_t low; - /** high mark, items end here */ - size_t high; -}; - -/** - * List of proxy fds that return replies from the server to our clients. - */ -struct proxy { - /** the fd to listen for replies from server */ - int s; - /** last time this was used */ - struct timeval lastuse; - /** remote address */ - struct sockaddr_storage addr; - /** length of addr */ - socklen_t addr_len; - /** number of queries waiting (in total) */ - size_t numwait; - /** number of queries sent to server (in total) */ - size_t numsent; - /** numberof answers returned to client (in total) */ - size_t numreturn; - /** how many times repurposed */ - size_t numreuse; - /** next in proxylist */ - struct proxy* next; -}; - -/** - * An item that has to be TCP relayed - */ -struct tcp_send_list { - /** the data item */ - uint8_t* item; - /** size of item */ - size_t len; - /** time when the item can be transmitted on */ - struct timeval wait; - /** how much of the item has already been transmitted */ - size_t done; - /** next in list */ - struct tcp_send_list* next; -}; - -/** - * List of TCP proxy fd pairs to TCP connect client to server - */ -struct tcp_proxy { - /** the fd to listen for client query */ - int client_s; - /** the fd to listen for server answer */ - int server_s; - - /** remote client address */ - struct sockaddr_storage addr; - /** length of address */ - socklen_t addr_len; - /** timeout on this entry */ - struct timeval timeout; - - /** list of query items to send to server */ - struct tcp_send_list* querylist; - /** last in query list */ - struct tcp_send_list* querylast; - /** list of answer items to send to client */ - struct tcp_send_list* answerlist; - /** last in answerlist */ - struct tcp_send_list* answerlast; - - /** next in list */ - struct tcp_proxy* next; -}; - -/** usage information for delayer */ -static void usage(char* argv[]) -{ - printf("usage: %s [options]\n", argv[0]); - printf(" -f addr : use addr, forward to that server, @port.\n"); - printf(" -b addr : bind to this address to listen.\n"); - printf(" -p port : bind to this port (use 0 for random).\n"); - printf(" -m mem : use this much memory for waiting queries.\n"); - printf(" -d delay: UDP queries are delayed n milliseconds.\n"); - printf(" TCP is delayed twice (on send, on recv).\n"); - printf(" -h : this help message\n"); - exit(1); -} - -/** timeval compare, t1 < t2 */ -static int -dl_tv_smaller(struct timeval* t1, const struct timeval* t2) -{ -#ifndef S_SPLINT_S - if(t1->tv_sec < t2->tv_sec) - return 1; - if(t1->tv_sec == t2->tv_sec && - t1->tv_usec < t2->tv_usec) - return 1; -#endif - return 0; -} - -/** timeval add, t1 += t2 */ -static void -dl_tv_add(struct timeval* t1, const struct timeval* t2) -{ -#ifndef S_SPLINT_S - t1->tv_sec += t2->tv_sec; - t1->tv_usec += t2->tv_usec; - while(t1->tv_usec > 1000000) { - t1->tv_usec -= 1000000; - t1->tv_sec++; - } -#endif -} - -/** timeval subtract, t1 -= t2 */ -static void -dl_tv_subtract(struct timeval* t1, const struct timeval* t2) -{ -#ifndef S_SPLINT_S - t1->tv_sec -= t2->tv_sec; - if(t1->tv_usec >= t2->tv_usec) { - t1->tv_usec -= t2->tv_usec; - } else { - t1->tv_sec--; - t1->tv_usec = 1000000-(t2->tv_usec-t1->tv_usec); - } -#endif -} - - -/** create new ring buffer */ -static struct ringbuf* -ring_create(size_t sz) -{ - struct ringbuf* r = (struct ringbuf*)calloc(1, sizeof(*r)); - if(!r) fatal_exit("out of memory"); - r->buf = (uint8_t*)malloc(sz); - if(!r->buf) fatal_exit("out of memory"); - r->size = sz; - r->low = 0; - r->high = 0; - return r; -} - -/** delete ring buffer */ -static void -ring_delete(struct ringbuf* r) -{ - if(!r) return; - free(r->buf); - free(r); -} - -/** add entry to ringbuffer */ -static void -ring_add(struct ringbuf* r, sldns_buffer* pkt, struct timeval* now, - struct timeval* delay, struct proxy* p) -{ - /* time -- proxy* -- 16bitlen -- message */ - uint16_t len = (uint16_t)sldns_buffer_limit(pkt); - struct timeval when; - size_t needed; - uint8_t* where = NULL; - log_assert(sldns_buffer_limit(pkt) <= 65535); - needed = sizeof(when) + sizeof(p) + sizeof(len) + len; - /* put item into ringbuffer */ - if(r->low < r->high) { - /* used part is in the middle */ - if(r->size - r->high >= needed) { - where = r->buf + r->high; - r->high += needed; - } else if(r->low > needed) { - /* wrap around ringbuffer */ - /* make sure r->low == r->high means empty */ - /* so r->low == r->high cannot be used to signify - * a completely full ringbuf */ - if(r->size - r->high > sizeof(when)+sizeof(p)) { - /* zero entry at end of buffer */ - memset(r->buf+r->high, 0, - sizeof(when)+sizeof(p)); - } - where = r->buf; - r->high = needed; - } else { - /* drop message */ - log_warn("warning: mem full, dropped message"); - return; - } - } else { - /* empty */ - if(r->high == r->low) { - where = r->buf; - r->low = 0; - r->high = needed; - /* unused part is in the middle */ - /* so ringbuffer has wrapped around */ - } else if(r->low - r->high > needed) { - where = r->buf + r->high; - r->high += needed; - } else { - log_warn("warning: mem full, dropped message"); - return; - } - } - when = *now; - dl_tv_add(&when, delay); - /* copy it at where part */ - log_assert(where != NULL); - memmove(where, &when, sizeof(when)); - memmove(where+sizeof(when), &p, sizeof(p)); - memmove(where+sizeof(when)+sizeof(p), &len, sizeof(len)); - memmove(where+sizeof(when)+sizeof(p)+sizeof(len), - sldns_buffer_begin(pkt), len); -} - -/** see if the ringbuffer is empty */ -static int -ring_empty(struct ringbuf* r) -{ - return (r->low == r->high); -} - -/** peek at timevalue for next item in ring */ -static struct timeval* -ring_peek_time(struct ringbuf* r) -{ - if(ring_empty(r)) - return NULL; - return (struct timeval*)&r->buf[r->low]; -} - -/** get entry from ringbuffer */ -static int -ring_pop(struct ringbuf* r, sldns_buffer* pkt, struct timeval* tv, - struct proxy** p) -{ - /* time -- proxy* -- 16bitlen -- message */ - uint16_t len; - uint8_t* where = NULL; - size_t done; - if(r->low == r->high) - return 0; - where = r->buf + r->low; - memmove(tv, where, sizeof(*tv)); - memmove(p, where+sizeof(*tv), sizeof(*p)); - memmove(&len, where+sizeof(*tv)+sizeof(*p), sizeof(len)); - memmove(sldns_buffer_begin(pkt), - where+sizeof(*tv)+sizeof(*p)+sizeof(len), len); - sldns_buffer_set_limit(pkt, (size_t)len); - done = sizeof(*tv)+sizeof(*p)+sizeof(len)+len; - /* move lowmark */ - if(r->low < r->high) { - /* used part in middle */ - log_assert(r->high - r->low >= done); - r->low += done; - } else { - /* unused part in middle */ - log_assert(r->size - r->low >= done); - r->low += done; - if(r->size - r->low > sizeof(*tv)+sizeof(*p)) { - /* see if it is zeroed; means end of buffer */ - struct proxy* pz; - memmove(&pz, r->buf+r->low+sizeof(*tv), sizeof(pz)); - if(pz == NULL) - r->low = 0; - } else r->low = 0; - } - if(r->low == r->high) { - r->low = 0; /* reset if empty */ - r->high = 0; - } - return 1; -} - -/** signal handler global info */ -static volatile int do_quit = 0; - -/** signal handler for user quit */ -static RETSIGTYPE delayer_sigh(int sig) -{ - printf("exit on signal %d\n", sig); - do_quit = 1; -} - -/** send out waiting packets */ -static void -service_send(struct ringbuf* ring, struct timeval* now, sldns_buffer* pkt, - struct sockaddr_storage* srv_addr, socklen_t srv_len) -{ - struct proxy* p; - struct timeval tv; - ssize_t sent; - while(!ring_empty(ring) && - dl_tv_smaller(ring_peek_time(ring), now)) { - /* this items needs to be sent out */ - if(!ring_pop(ring, pkt, &tv, &p)) - fatal_exit("ringbuf error: pop failed"); - verbose(1, "send out query %d.%6.6d", - (unsigned)tv.tv_sec, (unsigned)tv.tv_usec); - log_addr(1, "from client", &p->addr, p->addr_len); - /* send it */ - sent = sendto(p->s, (void*)sldns_buffer_begin(pkt), - sldns_buffer_limit(pkt), 0, - (struct sockaddr*)srv_addr, srv_len); - if(sent == -1) { -#ifndef USE_WINSOCK - log_err("sendto: %s", strerror(errno)); -#else - log_err("sendto: %s", wsa_strerror(WSAGetLastError())); -#endif - } else if(sent != (ssize_t)sldns_buffer_limit(pkt)) { - log_err("sendto: partial send"); - } - p->lastuse = *now; - p->numsent++; - } -} - -/** do proxy for one readable client */ -static void -do_proxy(struct proxy* p, int retsock, sldns_buffer* pkt) -{ - int i; - ssize_t r; - for(i=0; i<TRIES_PER_SELECT; i++) { - r = recv(p->s, (void*)sldns_buffer_begin(pkt), - sldns_buffer_capacity(pkt), 0); - if(r == -1) { -#ifndef USE_WINSOCK - if(errno == EAGAIN || errno == EINTR) - return; - log_err("recv: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEINPROGRESS || - WSAGetLastError() == WSAEWOULDBLOCK) - return; - log_err("recv: %s", wsa_strerror(WSAGetLastError())); -#endif - return; - } - sldns_buffer_set_limit(pkt, (size_t)r); - log_addr(1, "return reply to client", &p->addr, p->addr_len); - /* send reply back to the real client */ - p->numreturn++; - r = sendto(retsock, (void*)sldns_buffer_begin(pkt), (size_t)r, - 0, (struct sockaddr*)&p->addr, p->addr_len); - if(r == -1) { -#ifndef USE_WINSOCK - log_err("sendto: %s", strerror(errno)); -#else - log_err("sendto: %s", wsa_strerror(WSAGetLastError())); -#endif - } - } -} - -/** proxy return replies to clients */ -static void -service_proxy(fd_set* rset, int retsock, struct proxy* proxies, - sldns_buffer* pkt, struct timeval* now) -{ - struct proxy* p; - for(p = proxies; p; p = p->next) { - if(FD_ISSET(p->s, rset)) { - p->lastuse = *now; - do_proxy(p, retsock, pkt); - } - } -} - -/** find or else create proxy for this remote client */ -static struct proxy* -find_create_proxy(struct sockaddr_storage* from, socklen_t from_len, - fd_set* rorig, int* max, struct proxy** proxies, int serv_ip6, - struct timeval* now, struct timeval* reuse_timeout) -{ - struct proxy* p; - struct timeval t; - for(p = *proxies; p; p = p->next) { - if(sockaddr_cmp(from, from_len, &p->addr, p->addr_len)==0) - return p; - } - /* possibly: reuse lapsed entries */ - for(p = *proxies; p; p = p->next) { - if(p->numwait > p->numsent || p->numsent > p->numreturn) - continue; - t = *now; - dl_tv_subtract(&t, &p->lastuse); - if(dl_tv_smaller(&t, reuse_timeout)) - continue; - /* yes! */ - verbose(1, "reuse existing entry"); - memmove(&p->addr, from, from_len); - p->addr_len = from_len; - p->numreuse++; - return p; - } - /* create new */ - p = (struct proxy*)calloc(1, sizeof(*p)); - if(!p) fatal_exit("out of memory"); - p->s = socket(serv_ip6?AF_INET6:AF_INET, SOCK_DGRAM, 0); - if(p->s == -1) { -#ifndef USE_WINSOCK - fatal_exit("socket: %s", strerror(errno)); -#else - fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); -#endif - } - fd_set_nonblock(p->s); - memmove(&p->addr, from, from_len); - p->addr_len = from_len; - p->next = *proxies; - *proxies = p; - FD_SET(FD_SET_T p->s, rorig); - if(p->s+1 > *max) - *max = p->s+1; - return p; -} - -/** recv new waiting packets */ -static void -service_recv(int s, struct ringbuf* ring, sldns_buffer* pkt, - fd_set* rorig, int* max, struct proxy** proxies, - struct sockaddr_storage* srv_addr, socklen_t srv_len, - struct timeval* now, struct timeval* delay, struct timeval* reuse) -{ - int i; - struct sockaddr_storage from; - socklen_t from_len; - ssize_t len; - struct proxy* p; - for(i=0; i<TRIES_PER_SELECT; i++) { - from_len = (socklen_t)sizeof(from); - len = recvfrom(s, (void*)sldns_buffer_begin(pkt), - sldns_buffer_capacity(pkt), 0, - (struct sockaddr*)&from, &from_len); - if(len < 0) { -#ifndef USE_WINSOCK - if(errno == EAGAIN || errno == EINTR) - return; - fatal_exit("recvfrom: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEWOULDBLOCK || - WSAGetLastError() == WSAEINPROGRESS) - return; - fatal_exit("recvfrom: %s", - wsa_strerror(WSAGetLastError())); -#endif - } - sldns_buffer_set_limit(pkt, (size_t)len); - /* find its proxy element */ - p = find_create_proxy(&from, from_len, rorig, max, proxies, - addr_is_ip6(srv_addr, srv_len), now, reuse); - if(!p) fatal_exit("error: cannot find or create proxy"); - p->lastuse = *now; - ring_add(ring, pkt, now, delay, p); - p->numwait++; - log_addr(1, "recv from client", &p->addr, p->addr_len); - } -} - -/** delete tcp proxy */ -static void -tcp_proxy_delete(struct tcp_proxy* p) -{ - struct tcp_send_list* s, *sn; - if(!p) - return; - log_addr(1, "delete tcp proxy", &p->addr, p->addr_len); - s = p->querylist; - while(s) { - sn = s->next; - free(s->item); - free(s); - s = sn; - } - s = p->answerlist; - while(s) { - sn = s->next; - free(s->item); - free(s); - s = sn; - } -#ifndef USE_WINSOCK - close(p->client_s); - if(p->server_s != -1) - close(p->server_s); -#else - closesocket(p->client_s); - if(p->server_s != -1) - closesocket(p->server_s); -#endif - free(p); -} - -/** accept new TCP connections, and set them up */ -static void -service_tcp_listen(int s, fd_set* rorig, int* max, struct tcp_proxy** proxies, - struct sockaddr_storage* srv_addr, socklen_t srv_len, - struct timeval* now, struct timeval* tcp_timeout) -{ - int newfd; - struct sockaddr_storage addr; - struct tcp_proxy* p; - socklen_t addr_len; - newfd = accept(s, (struct sockaddr*)&addr, &addr_len); - if(newfd == -1) { -#ifndef USE_WINSOCK - if(errno == EAGAIN || errno == EINTR) - return; - fatal_exit("accept: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEWOULDBLOCK || - WSAGetLastError() == WSAEINPROGRESS || - WSAGetLastError() == WSAECONNRESET) - return; - fatal_exit("accept: %s", wsa_strerror(WSAGetLastError())); -#endif - } - p = (struct tcp_proxy*)calloc(1, sizeof(*p)); - if(!p) fatal_exit("out of memory"); - memmove(&p->addr, &addr, addr_len); - p->addr_len = addr_len; - log_addr(1, "new tcp proxy", &p->addr, p->addr_len); - p->client_s = newfd; - p->server_s = socket(addr_is_ip6(srv_addr, srv_len)?AF_INET6:AF_INET, - SOCK_STREAM, 0); - if(p->server_s == -1) { -#ifndef USE_WINSOCK - fatal_exit("tcp socket: %s", strerror(errno)); -#else - fatal_exit("tcp socket: %s", wsa_strerror(WSAGetLastError())); -#endif - } - fd_set_nonblock(p->client_s); - fd_set_nonblock(p->server_s); - if(connect(p->server_s, (struct sockaddr*)srv_addr, srv_len) == -1) { -#ifndef USE_WINSOCK - if(errno != EINPROGRESS) { - log_err("tcp connect: %s", strerror(errno)); - close(p->server_s); - close(p->client_s); -#else - if(WSAGetLastError() != WSAEWOULDBLOCK && - WSAGetLastError() != WSAEINPROGRESS) { - log_err("tcp connect: %s", - wsa_strerror(WSAGetLastError())); - closesocket(p->server_s); - closesocket(p->client_s); -#endif - free(p); - return; - } - } - p->timeout = *now; - dl_tv_add(&p->timeout, tcp_timeout); - - /* listen to client and server */ - FD_SET(FD_SET_T p->client_s, rorig); - FD_SET(FD_SET_T p->server_s, rorig); - if(p->client_s+1 > *max) - *max = p->client_s+1; - if(p->server_s+1 > *max) - *max = p->server_s+1; - - /* add into proxy list */ - p->next = *proxies; - *proxies = p; -} - -/** relay TCP, read a part */ -static int -tcp_relay_read(int s, struct tcp_send_list** first, - struct tcp_send_list** last, struct timeval* now, - struct timeval* delay, sldns_buffer* pkt) -{ - struct tcp_send_list* item; - ssize_t r = recv(s, (void*)sldns_buffer_begin(pkt), - sldns_buffer_capacity(pkt), 0); - if(r == -1) { -#ifndef USE_WINSOCK - if(errno == EINTR || errno == EAGAIN) - return 1; - log_err("tcp read: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEINPROGRESS || - WSAGetLastError() == WSAEWOULDBLOCK) - return 1; - log_err("tcp read: %s", wsa_strerror(WSAGetLastError())); -#endif - return 0; - } else if(r == 0) { - /* connection closed */ - return 0; - } - item = (struct tcp_send_list*)malloc(sizeof(*item)); - if(!item) { - log_err("out of memory"); - return 0; - } - verbose(1, "read item len %d", (int)r); - item->len = (size_t)r; - item->item = memdup(sldns_buffer_begin(pkt), item->len); - if(!item->item) { - free(item); - log_err("out of memory"); - return 0; - } - item->done = 0; - item->wait = *now; - dl_tv_add(&item->wait, delay); - item->next = NULL; - - /* link in */ - if(*first) { - (*last)->next = item; - } else { - *first = item; - } - *last = item; - return 1; -} - -/** relay TCP, write a part */ -static int -tcp_relay_write(int s, struct tcp_send_list** first, - struct tcp_send_list** last, struct timeval* now) -{ - ssize_t r; - struct tcp_send_list* p; - while(*first) { - p = *first; - /* is the item ready? */ - if(!dl_tv_smaller(&p->wait, now)) - return 1; - /* write it */ - r = send(s, (void*)(p->item + p->done), p->len - p->done, 0); - if(r == -1) { -#ifndef USE_WINSOCK - if(errno == EAGAIN || errno == EINTR) - return 1; - log_err("tcp write: %s", strerror(errno)); -#else - if(WSAGetLastError() == WSAEWOULDBLOCK || - WSAGetLastError() == WSAEINPROGRESS) - return 1; - log_err("tcp write: %s", - wsa_strerror(WSAGetLastError())); -#endif - return 0; - } else if(r == 0) { - /* closed */ - return 0; - } - /* account it */ - p->done += (size_t)r; - verbose(1, "write item %d of %d", (int)p->done, (int)p->len); - if(p->done >= p->len) { - free(p->item); - *first = p->next; - if(!*first) - *last = NULL; - free(p); - } else { - /* partial write */ - return 1; - } - } - return 1; -} - -/** perform TCP relaying */ -static void -service_tcp_relay(struct tcp_proxy** tcp_proxies, struct timeval* now, - struct timeval* delay, struct timeval* tcp_timeout, sldns_buffer* pkt, - fd_set* rset, fd_set* rorig, fd_set* worig) -{ - struct tcp_proxy* p, **prev; - struct timeval tout; - int delete_it; - p = *tcp_proxies; - prev = tcp_proxies; - tout = *now; - dl_tv_add(&tout, tcp_timeout); - - while(p) { - delete_it = 0; - /* can we receive further queries? */ - if(!delete_it && FD_ISSET(p->client_s, rset)) { - p->timeout = tout; - log_addr(1, "read tcp query", &p->addr, p->addr_len); - if(!tcp_relay_read(p->client_s, &p->querylist, - &p->querylast, now, delay, pkt)) - delete_it = 1; - } - /* can we receive further answers? */ - if(!delete_it && p->server_s != -1 && - FD_ISSET(p->server_s, rset)) { - p->timeout = tout; - log_addr(1, "read tcp answer", &p->addr, p->addr_len); - if(!tcp_relay_read(p->server_s, &p->answerlist, - &p->answerlast, now, delay, pkt)) { -#ifndef USE_WINSOCK - close(p->server_s); -#else - closesocket(p->server_s); -#endif - FD_CLR(FD_SET_T p->server_s, worig); - FD_CLR(FD_SET_T p->server_s, rorig); - p->server_s = -1; - } - } - /* can we send on further queries */ - if(!delete_it && p->querylist && p->server_s != -1) { - p->timeout = tout; - if(dl_tv_smaller(&p->querylist->wait, now)) - log_addr(1, "write tcp query", - &p->addr, p->addr_len); - if(!tcp_relay_write(p->server_s, &p->querylist, - &p->querylast, now)) - delete_it = 1; - if(p->querylist && p->server_s != -1 && - dl_tv_smaller(&p->querylist->wait, now)) - FD_SET(FD_SET_T p->server_s, worig); - else FD_CLR(FD_SET_T p->server_s, worig); - } - - /* can we send on further answers */ - if(!delete_it && p->answerlist) { - p->timeout = tout; - if(dl_tv_smaller(&p->answerlist->wait, now)) - log_addr(1, "write tcp answer", - &p->addr, p->addr_len); - if(!tcp_relay_write(p->client_s, &p->answerlist, - &p->answerlast, now)) - delete_it = 1; - if(p->answerlist && dl_tv_smaller(&p->answerlist->wait, - now)) - FD_SET(FD_SET_T p->client_s, worig); - else FD_CLR(FD_SET_T p->client_s, worig); - if(!p->answerlist && p->server_s == -1) - delete_it = 1; - } - - /* does this entry timeout? (unused too long) */ - if(dl_tv_smaller(&p->timeout, now)) { - delete_it = 1; - } - if(delete_it) { - struct tcp_proxy* np = p->next; - *prev = np; - FD_CLR(FD_SET_T p->client_s, rorig); - FD_CLR(FD_SET_T p->client_s, worig); - if(p->server_s != -1) { - FD_CLR(FD_SET_T p->server_s, rorig); - FD_CLR(FD_SET_T p->server_s, worig); - } - tcp_proxy_delete(p); - p = np; - continue; - } - - prev = &p->next; - p = p->next; - } -} - -/** find waiting time */ -static int -service_findwait(struct timeval* now, struct timeval* wait, - struct ringbuf* ring, struct tcp_proxy* tcplist) -{ - /* first item is the time to wait */ - struct timeval* peek = ring_peek_time(ring); - struct timeval tcv; - int have_tcpval = 0; - struct tcp_proxy* p; - - /* also for TCP list the first in sendlists is the time to wait */ - for(p=tcplist; p; p=p->next) { - if(!have_tcpval) - tcv = p->timeout; - have_tcpval = 1; - if(dl_tv_smaller(&p->timeout, &tcv)) - tcv = p->timeout; - if(p->querylist && dl_tv_smaller(&p->querylist->wait, &tcv)) - tcv = p->querylist->wait; - if(p->answerlist && dl_tv_smaller(&p->answerlist->wait, &tcv)) - tcv = p->answerlist->wait; - } - if(peek) { - /* peek can be unaligned */ - /* use wait as a temp variable */ - memmove(wait, peek, sizeof(*wait)); - if(!have_tcpval) - tcv = *wait; - else if(dl_tv_smaller(wait, &tcv)) - tcv = *wait; - have_tcpval = 1; - } - if(have_tcpval) { - *wait = tcv; - dl_tv_subtract(wait, now); - return 1; - } - /* nothing, block */ - return 0; -} - -/** clear proxy list */ -static void -proxy_list_clear(struct proxy* p) -{ - char from[109]; - struct proxy* np; - int i=0, port; - while(p) { - np = p->next; - port = (int)ntohs(((struct sockaddr_in*)&p->addr)->sin_port); - if(addr_is_ip6(&p->addr, p->addr_len)) { - if(inet_ntop(AF_INET6, - &((struct sockaddr_in6*)&p->addr)->sin6_addr, - from, (socklen_t)sizeof(from)) == 0) - (void)strlcpy(from, "err", sizeof(from)); - } else { - if(inet_ntop(AF_INET, - &((struct sockaddr_in*)&p->addr)->sin_addr, - from, (socklen_t)sizeof(from)) == 0) - (void)strlcpy(from, "err", sizeof(from)); - } - printf("client[%d]: last %s@%d of %d : %u in, %u out, " - "%u returned\n", i++, from, port, (int)p->numreuse+1, - (unsigned)p->numwait, (unsigned)p->numsent, - (unsigned)p->numreturn); -#ifndef USE_WINSOCK - close(p->s); -#else - closesocket(p->s); -#endif - free(p); - p = np; - } -} - -/** clear TCP proxy list */ -static void -tcp_proxy_list_clear(struct tcp_proxy* p) -{ - struct tcp_proxy* np; - while(p) { - np = p->next; - tcp_proxy_delete(p); - p = np; - } -} - -/** delayer service loop */ -static void -service_loop(int udp_s, int listen_s, struct ringbuf* ring, - struct timeval* delay, struct timeval* reuse, - struct sockaddr_storage* srv_addr, socklen_t srv_len, - sldns_buffer* pkt) -{ - fd_set rset, rorig; - fd_set wset, worig; - struct timeval now, wait; - int max, have_wait = 0; - struct proxy* proxies = NULL; - struct tcp_proxy* tcp_proxies = NULL; - struct timeval tcp_timeout; - tcp_timeout.tv_sec = 120; - tcp_timeout.tv_usec = 0; -#ifndef S_SPLINT_S - FD_ZERO(&rorig); - FD_ZERO(&worig); - FD_SET(FD_SET_T udp_s, &rorig); - FD_SET(FD_SET_T listen_s, &rorig); -#endif - max = udp_s + 1; - if(listen_s + 1 > max) max = listen_s + 1; - while(!do_quit) { - /* wait for events */ - rset = rorig; - wset = worig; - if(have_wait) - verbose(1, "wait for %d.%6.6d", - (unsigned)wait.tv_sec, (unsigned)wait.tv_usec); - else verbose(1, "wait"); - if(select(max, &rset, &wset, NULL, have_wait?&wait:NULL) < 0) { - if(errno == EAGAIN || errno == EINTR) - continue; - fatal_exit("select: %s", strerror(errno)); - } - /* get current time */ - if(gettimeofday(&now, NULL) < 0) { - if(errno == EAGAIN || errno == EINTR) - continue; - fatal_exit("gettimeofday: %s", strerror(errno)); - } - verbose(1, "process at %u.%6.6u\n", - (unsigned)now.tv_sec, (unsigned)now.tv_usec); - /* sendout delayed queries to master server (frees up buffer)*/ - service_send(ring, &now, pkt, srv_addr, srv_len); - /* proxy return replies */ - service_proxy(&rset, udp_s, proxies, pkt, &now); - /* see what can be received to start waiting */ - service_recv(udp_s, ring, pkt, &rorig, &max, &proxies, - srv_addr, srv_len, &now, delay, reuse); - /* see if there are new tcp connections */ - service_tcp_listen(listen_s, &rorig, &max, &tcp_proxies, - srv_addr, srv_len, &now, &tcp_timeout); - /* service tcp connections */ - service_tcp_relay(&tcp_proxies, &now, delay, &tcp_timeout, - pkt, &rset, &rorig, &worig); - /* see what next timeout is (if any) */ - have_wait = service_findwait(&now, &wait, ring, tcp_proxies); - } - proxy_list_clear(proxies); - tcp_proxy_list_clear(tcp_proxies); -} - -/** delayer main service routine */ -static void -service(const char* bind_str, int bindport, const char* serv_str, - size_t memsize, int delay_msec) -{ - struct sockaddr_storage bind_addr, srv_addr; - socklen_t bind_len, srv_len; - struct ringbuf* ring = ring_create(memsize); - struct timeval delay, reuse; - sldns_buffer* pkt; - int i, s, listen_s; -#ifndef S_SPLINT_S - delay.tv_sec = delay_msec / 1000; - delay.tv_usec = (delay_msec % 1000)*1000; -#endif - reuse = delay; /* reuse is max(4*delay, 1 second) */ - dl_tv_add(&reuse, &delay); - dl_tv_add(&reuse, &delay); - dl_tv_add(&reuse, &delay); - if(reuse.tv_sec == 0) - reuse.tv_sec = 1; - if(!extstrtoaddr(serv_str, &srv_addr, &srv_len)) { - printf("cannot parse forward address: %s\n", serv_str); - exit(1); - } - pkt = sldns_buffer_new(65535); - if(!pkt) - fatal_exit("out of memory"); - if( signal(SIGINT, delayer_sigh) == SIG_ERR || -#ifdef SIGHUP - signal(SIGHUP, delayer_sigh) == SIG_ERR || -#endif -#ifdef SIGQUIT - signal(SIGQUIT, delayer_sigh) == SIG_ERR || -#endif -#ifdef SIGBREAK - signal(SIGBREAK, delayer_sigh) == SIG_ERR || -#endif -#ifdef SIGALRM - signal(SIGALRM, delayer_sigh) == SIG_ERR || -#endif - signal(SIGTERM, delayer_sigh) == SIG_ERR) - fatal_exit("could not bind to signal"); - /* bind UDP port */ - if((s = socket(str_is_ip6(bind_str)?AF_INET6:AF_INET, - SOCK_DGRAM, 0)) == -1) { -#ifndef USE_WINSOCK - fatal_exit("socket: %s", strerror(errno)); -#else - fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); -#endif - } - i=0; - if(bindport == 0) { - bindport = 1024 + arc4random()%64000; - i = 100; - } - while(1) { - if(!ipstrtoaddr(bind_str, bindport, &bind_addr, &bind_len)) { - printf("cannot parse listen address: %s\n", bind_str); - exit(1); - } - if(bind(s, (struct sockaddr*)&bind_addr, bind_len) == -1) { -#ifndef USE_WINSOCK - log_err("bind: %s", strerror(errno)); -#else - log_err("bind: %s", wsa_strerror(WSAGetLastError())); -#endif - if(i--==0) - fatal_exit("cannot bind any port"); - bindport = 1024 + arc4random()%64000; - } else break; - } - fd_set_nonblock(s); - /* and TCP port */ - if((listen_s = socket(str_is_ip6(bind_str)?AF_INET6:AF_INET, - SOCK_STREAM, 0)) == -1) { -#ifndef USE_WINSOCK - fatal_exit("tcp socket: %s", strerror(errno)); -#else - fatal_exit("tcp socket: %s", wsa_strerror(WSAGetLastError())); -#endif - } -#ifdef SO_REUSEADDR - if(1) { - int on = 1; - if(setsockopt(listen_s, SOL_SOCKET, SO_REUSEADDR, (void*)&on, - (socklen_t)sizeof(on)) < 0) -#ifndef USE_WINSOCK - fatal_exit("setsockopt(.. SO_REUSEADDR ..) failed: %s", - strerror(errno)); -#else - fatal_exit("setsockopt(.. SO_REUSEADDR ..) failed: %s", - wsa_strerror(WSAGetLastError())); -#endif - } -#endif - if(bind(listen_s, (struct sockaddr*)&bind_addr, bind_len) == -1) { -#ifndef USE_WINSOCK - fatal_exit("tcp bind: %s", strerror(errno)); -#else - fatal_exit("tcp bind: %s", wsa_strerror(WSAGetLastError())); -#endif - } - if(listen(listen_s, 5) == -1) { -#ifndef USE_WINSOCK - fatal_exit("tcp listen: %s", strerror(errno)); -#else - fatal_exit("tcp listen: %s", wsa_strerror(WSAGetLastError())); -#endif - } - fd_set_nonblock(listen_s); - printf("listening on port: %d\n", bindport); - - /* process loop */ - do_quit = 0; - service_loop(s, listen_s, ring, &delay, &reuse, &srv_addr, srv_len, - pkt); - - /* cleanup */ - verbose(1, "cleanup"); -#ifndef USE_WINSOCK - close(s); - close(listen_s); -#else - closesocket(s); - closesocket(listen_s); -#endif - sldns_buffer_free(pkt); - ring_delete(ring); -} - -/** getopt global, in case header files fail to declare it. */ -extern int optind; -/** getopt global, in case header files fail to declare it. */ -extern char* optarg; - -/** main program for delayer */ -int main(int argc, char** argv) -{ - int c; /* defaults */ - const char* server = "127.0.0.1@53"; - const char* bindto = "0.0.0.0"; - int bindport = 0; - size_t memsize = 10*1024*1024; - int delay = 100; - - verbosity = 0; - log_init(0, 0, 0); - log_ident_set("delayer"); - if(argc == 1) usage(argv); - while( (c=getopt(argc, argv, "b:d:f:hm:p:")) != -1) { - switch(c) { - case 'b': - bindto = optarg; - break; - case 'd': - if(atoi(optarg)==0 && strcmp(optarg,"0")!=0) { - printf("bad delay: %s\n", optarg); - return 1; - } - delay = atoi(optarg); - break; - case 'f': - server = optarg; - break; - case 'm': - if(!cfg_parse_memsize(optarg, &memsize)) { - printf("bad memsize: %s\n", optarg); - return 1; - } - break; - case 'p': - if(atoi(optarg)==0 && strcmp(optarg,"0")!=0) { - printf("bad port nr: %s\n", optarg); - return 1; - } - bindport = atoi(optarg); - break; - case 'h': - case '?': - default: - usage(argv); - } - } - argc -= optind; - argv += optind; - if(argc != 0) - usage(argv); - - printf("bind to %s @ %d and forward to %s after %d msec\n", - bindto, bindport, server, delay); - service(bindto, bindport, server, memsize, delay); - return 0; -} |