aboutsummaryrefslogtreecommitdiff
path: root/external/unbound/testcode/delayer.c
diff options
context:
space:
mode:
Diffstat (limited to 'external/unbound/testcode/delayer.c')
-rw-r--r--external/unbound/testcode/delayer.c1185
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;
-}