#include "ldns/ldns.h"
+/** default port to listen for queries, passed to getaddrinfo */
+#define UNBOUND_DNS_PORT "53"
])
AC_CONFIG_FILES([Makefile])
#include "util/log.h"
#include "daemon/worker.h"
-/** default port to listen for queries, passed to getaddrinfo */
-#define UNBOUND_DNS_PORT "53"
/** buffer size for network connections */
#define BUFSZ 65552
{
struct worker* worker = NULL;
int do_ip4=1, do_ip6=1, do_udp=1, do_tcp=1;
+ size_t numports=3;
+ int baseport=10000;
const char* port = UNBOUND_DNS_PORT;
int c;
+ const char* fwd = "127.0.0.1";
+ const char* fwdport = UNBOUND_DNS_PORT;
log_init();
/* parse the options */
if(!atoi(optarg))
fatal_exit("invalid port '%s'", optarg);
port = optarg;
+ baseport = atoi(optarg)+2000;
verbose(VERB_ALGO, "using port: %s", port);
break;
case 'v':
}
/* setup */
- worker = worker_init(port, do_ip4, do_ip6, do_udp, do_tcp, BUFSZ);
+ worker = worker_init(port, do_ip4, do_ip6, do_udp, do_tcp, BUFSZ,
+ numports, baseport);
if(!worker) {
fatal_exit("could not initialize");
}
+ if(!worker_set_fwd(worker, fwd, fwdport)) {
+ fatal_exit("could set forwarder address");
+ }
/* drop user priviliges and chroot if needed */
log_info("Start of %s.", PACKAGE_STRING);
#include "daemon/worker.h"
#include "util/netevent.h"
#include "services/listen_dnsport.h"
+#include "services/outside_network.h"
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netdb.h>
+#include <errno.h>
+
+/** timeout in seconds for UDP queries to auth servers. TODO: proper rtt */
+#define UDP_QUERY_TIMEOUT 5
/** process incoming request */
static void worker_process_query(struct worker* worker)
{
/* query the forwarding address */
-
+ pending_udp_query(worker->back, worker->query_reply.c->buffer,
+ &worker->fwd_addr, worker->fwd_addrlen, UDP_QUERY_TIMEOUT);
}
/** check request sanity. Returns error code, 0 OK, or -1 discard.
}
struct worker* worker_init(const char* port, int do_ip4, int do_ip6,
- int do_udp, int do_tcp, size_t buffer_size)
+ int do_udp, int do_tcp, size_t buffer_size, size_t numports,
+ int base_port)
{
struct worker* worker = (struct worker*)calloc(1,
sizeof(struct worker));
log_err("could not create listening sockets");
return NULL;
}
+ worker->back = outside_network_create(worker->base,
+ buffer_size, numports, NULL, 0, do_ip4, do_ip6, base_port);
+ if(!worker->back) {
+ comm_base_delete(worker->base);
+ log_err("could not create outgoing sockets");
+ return NULL;
+ }
+ /* init random(), large table size. */
+ if(!initstate(time(NULL)^getpid(), worker->rndstate, RND_STATE_SIZE)) {
+ log_err("could not init random numbers.");
+ comm_base_delete(worker->base);
+ return NULL;
+ }
return worker;
}
-
void worker_work(struct worker* worker)
{
comm_base_dispatch(worker->base);
if(!worker)
return;
listen_delete(worker->front);
+ outside_network_delete(worker->back);
comm_base_delete(worker->base);
free(worker);
}
+
+int worker_set_fwd(struct worker* worker, const char* ip, const char* port)
+{
+ struct addrinfo *res = NULL;
+ int r;
+ log_assert(worker && ip);
+ if(!port)
+ port = UNBOUND_DNS_PORT;
+ if((r=getaddrinfo(ip, port, NULL, &res)) != 0 || !res) {
+ log_err("failed %s:%s getaddrinfo: %s %s",
+ ip, port,
+ gai_strerror(r), r==EAI_SYSTEM?strerror(errno):"");
+ return 0;
+ }
+ worker->fwd_addrlen = res->ai_addrlen;
+ memcpy(&worker->fwd_addr, &res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ return 1;
+}
#include "config.h"
#include "util/netevent.h"
struct listen_dnsport;
+struct outside_network;
+
+/** size of table used for random numbers. large to be more secure. */
+#define RND_STATE_SIZE 256
/**
* Structure holding working information for unbound.
/** the frontside listening interface where request events come in */
struct listen_dnsport* front;
+ /** the backside outside network interface to the auth servers */
+ struct outside_network* back;
+
/** our one and only query, packet buffer and where to send. */
struct comm_reply query_reply;
/** number of requests currently active */
int num_requests;
+
+ /** random() table for this worker. */
+ char rndstate[RND_STATE_SIZE];
+
+ /** address to forward to */
+ struct sockaddr_storage fwd_addr;
+
+ /** length of fwd_addr */
+ socklen_t fwd_addrlen;
};
/**
* @param do_udp: listen to udp queries.
* @param do_tcp: listen to tcp queries.
* @param buffer_size: size of datagram buffer.
+ * @param numports: number of outgoing ports.
+ * @param base_port: -1 or specify base of outgoing port range.
* @return: The worker, or NULL on error.
*/
struct worker* worker_init(const char* port, int do_ip4, int do_ip6,
- int do_udp, int do_tcp, size_t buffer_size);
+ int do_udp, int do_tcp, size_t buffer_size, size_t numports,
+ int base_port);
/**
* Make worker work.
*/
void worker_delete(struct worker* worker);
+/**
+ * Set forwarder
+ * @param worker: the worker to modify.
+ * @param ip: the server name.
+ * @param port: port on server or NULL for default 53.
+ * @return: false on error.
+ */
+int worker_set_fwd(struct worker* worker, const char* ip, const char* port);
+
#endif /* DAEMON_WORKER_H */
- netevent communication reply storage structure.
- minimal query header sanity checking for worker.
- copied over rbtree implementation from NSD (BSD licensed too).
+ - outgoing network query service work.
30 January 2007: Wouter
- links in example/ldns-testpkts.c and .h for premade packet support.
}
}
-/**
- * Create and bind UDP socket
- * @param addr: address info ready to make socket.
- * @return: the socket. -1 on error.
- */
-static int
+int
create_udp_sock(struct addrinfo *addr)
{
int s, flag;
#include "config.h"
#include "util/netevent.h"
struct listen_list;
+struct addrinfo;
/**
* Listening for queries structure.
*/
void listen_delete(struct listen_dnsport* listen);
+
+/**
+ * Create and bind nonblocking UDP socket
+ * @param addr: address info ready to make socket.
+ * @return: the socket. -1 on error.
+ */
+int create_udp_sock(struct addrinfo *addr);
+
+
#endif /* LISTEN_DNSPORT_H */
--- /dev/null
+/*
+ * services/outside_network.c - implement sending of queries and wait answer.
+ *
+ * Copyright (c) 2007, 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 REGENTS 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 file has functions to send queries to authoritative servers and
+ * wait for the pending answer events.
+ */
+
+#include "services/outside_network.h"
+#include "services/listen_dnsport.h"
+#include "util/netevent.h"
+#include "util/log.h"
+
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#include <netdb.h>
+#include <errno.h>
+#include <fcntl.h>
+
+/** compare function of pending rbtree */
+static int pending_cmp(const void* key1, const void* key2)
+{
+ struct pending *p1 = (struct pending*)key1;
+ struct pending *p2 = (struct pending*)key2;
+ if(p1->id < p2->id)
+ return -1;
+ if(p1->id > p2->id)
+ return 1;
+ log_assert(p1->id == p2->id);
+ if(p1->addrlen < p2->addrlen)
+ return -1;
+ if(p1->addrlen > p2->addrlen)
+ return 1;
+ log_assert(p1->addrlen == p2->addrlen);
+ return memcmp(&p1->addr, &p2->addr, p1->addrlen);
+}
+
+/** compare function of pending_timeout rbtree */
+static int pending_timeout_cmp(const void* key1, const void* key2)
+{
+ struct pending_timeout *p1 = (struct pending_timeout*)key1;
+ struct pending_timeout *p2 = (struct pending_timeout*)key2;
+ if(p1->timeout.tv_sec < p2->timeout.tv_sec)
+ return -1;
+ if(p1->timeout.tv_sec > p2->timeout.tv_sec)
+ return 1;
+ log_assert(p1->timeout.tv_sec == p2->timeout.tv_sec);
+ if(p1->timeout.tv_usec < p2->timeout.tv_usec)
+ return -1;
+ if(p1->timeout.tv_usec > p2->timeout.tv_usec)
+ return 1;
+ log_assert(p1->timeout.tv_usec == p2->timeout.tv_usec);
+ if(p1 < p2)
+ return -1;
+ if(p1 > p2)
+ return 1;
+ return 0;
+}
+
+/** callback for incoming udp answers from the network. */
+static int outnet_udp_cb(struct comm_point* c, void* my_arg, int error,
+ struct comm_reply *reply_info)
+{
+ log_info("answer cb");
+ return 0;
+}
+
+/** open another udp port to listen to, every thread has its own range
+ * of open ports.
+ * @param ifname: on which interface to open the port.
+ * @param hints: hints on family and passiveness preset.
+ * @param porthint: if not -1, it gives the port to base range on.
+ * @return: file descriptor
+ */
+static int open_udp_port_range(const char* ifname, struct addrinfo* hints,
+ int porthint)
+{
+ struct addrinfo *res = NULL;
+ int r, s;
+ char portstr[20];
+ if(porthint != -1)
+ snprintf(portstr, sizeof(portstr), "%d", porthint);
+
+ if((r=getaddrinfo(ifname, ((porthint==-1)?NULL:portstr), hints,
+ &res)) != 0 || !res) {
+ log_err("node %s:%s getaddrinfo: %s %s",
+ ifname?ifname:"default", (porthint!=-1)?portstr:"eph",
+ gai_strerror(r), r==EAI_SYSTEM?strerror(errno):"");
+ return -1;
+ }
+ s = create_udp_sock(res);
+ freeaddrinfo(res);
+ return s;
+}
+
+/**
+ * Create range of UDP ports on the given interface.
+ */
+static int
+make_udp_range(struct outside_network* outnet, const char* ifname,
+ size_t num_ports, size_t off, int do_ip4, int do_ip6, int porthint)
+{
+ size_t i;
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ if(ifname)
+ hints.ai_flags |= AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
+ if(do_ip4 && do_ip6)
+ hints.ai_family = AF_UNSPEC;
+ else if(do_ip4)
+ hints.ai_family = AF_INET;
+ else if(do_ip6)
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ for(i=0; i<num_ports; i++) {
+ int fd = open_udp_port_range(ifname, &hints, porthint);
+ if(fd == -1) {
+ return 0;
+ }
+ if(porthint != -1)
+ porthint++;
+ outnet->udp_ports[i+off] = comm_point_create_udp(
+ outnet->base, fd, outnet->udp_buff, outnet_udp_cb,
+ outnet);
+ if(!outnet->udp_ports[i+off]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+struct outside_network*
+outside_network_create(struct comm_base *base, size_t bufsize,
+ size_t num_ports, const char** ifs, int num_ifs, int do_ip4,
+ int do_ip6, int port_base)
+{
+ struct outside_network* outnet = (struct outside_network*)
+ calloc(1, sizeof(struct outside_network));
+ int k;
+ if(!outnet) {
+ log_err("malloc failed");
+ return NULL;
+ }
+ outnet->base = base;
+ outnet->num_udp = (num_ifs?num_ports * num_ifs:num_ports);
+ if( !(outnet->udp_buff = ldns_buffer_new(bufsize)) ||
+ !(outnet->udp_ports = (struct comm_point **)calloc(
+ outnet->num_udp, sizeof(struct comm_point*))) ||
+ !(outnet->pending = rbtree_create(pending_cmp)) ||
+ !(outnet->pending_timeout = rbtree_create(
+ pending_timeout_cmp))) {
+ log_err("malloc failed");
+ outside_network_delete(outnet);
+ return NULL;
+ }
+ if(num_ifs == 0) {
+ if(!make_udp_range(outnet, NULL, num_ports, 0, do_ip4,
+ do_ip6, port_base)) {
+ outside_network_delete(outnet);
+ return NULL;
+ }
+ }
+ else for(k=0; k<num_ifs; k++) {
+ if(!make_udp_range(outnet, ifs[k], num_ports, k*num_ports,
+ do_ip4, do_ip6, port_base)) {
+ outside_network_delete(outnet);
+ return NULL;
+ }
+ }
+
+ return outnet;
+}
+
+void outside_network_delete(struct outside_network* outnet)
+{
+ if(!outnet)
+ return;
+ /* check every element, since we can be called on malloc error */
+ if(outnet->pending) {
+ struct pending *p, *np;
+ p = (struct pending*)rbtree_first(outnet->pending);
+ while(p) {
+ np = (struct pending*)rbtree_next((rbnode_t*)p);
+ pending_delete(NULL, p);
+ p = np;
+ }
+ free(outnet->pending);
+ }
+ if(outnet->pending_timeout) {
+ log_assert(outnet->pending_timeout->count == 0);
+ free(outnet->pending_timeout);
+ }
+ if(outnet->udp_buff)
+ ldns_buffer_free(outnet->udp_buff);
+ if(outnet->udp_ports) {
+ size_t i;
+ for(i=0; i<outnet->num_udp; i++) {
+ if(outnet->udp_ports[i])
+ comm_point_delete(outnet->udp_ports[i]);
+ }
+ free(outnet->udp_ports);
+ }
+ free(outnet);
+}
+
+void pending_delete(struct outside_network* outnet, struct pending* p)
+{
+ if(!p)
+ return;
+ if(outnet) {
+ (void)rbtree_delete(outnet->pending, p->node.key);
+ (void)rbtree_delete(outnet->pending_timeout,
+ p->timeout->node.key);
+ }
+ free(p->timeout);
+ free(p);
+}
+
+void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
+ struct sockaddr_storage* addr, socklen_t addrlen, int timeout)
+{
+ /* choose a random outgoing port and interface */
+ /* uses lousy random() function. TODO: entropy source. */
+ double precho = (double)random() * (double)outnet->num_udp /
+ ((double)RAND_MAX + 1.0);
+ int chosen = (int)precho;
+ /* don't trust in perfect double rounding */
+ if(chosen < 0) chosen = 0;
+ if(chosen >= (int)outnet->num_udp) chosen = (int)outnet->num_udp-1;
+}
--- /dev/null
+/*
+ * services/outside_network.h - listen to answers from the network
+ *
+ * Copyright (c) 2007, 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 REGENTS 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 file has functions to send queries to authoritative servers,
+ * and wait for the pending answer, with timeouts.
+ */
+
+#ifndef OUTSIDE_NETWORK_H
+#define OUTSIDE_NETWORK_H
+
+#include "config.h"
+#include "util/rbtree.h"
+struct pending;
+struct pending_timeout;
+struct comm_point;
+struct comm_base;
+
+/**
+ * Send queries to outside servers and wait for answers from servers.
+ * Contains answer-listen sockets.
+ */
+struct outside_network {
+ /** Base for select calls */
+ struct comm_base* base;
+
+ /** buffer shared by UDP connections, since there is only one
+ datagram at any time. */
+ ldns_buffer* udp_buff;
+
+ /**
+ * Array of udp comm point* that are used to listen to pending events.
+ * Each is on a different port.
+ */
+ struct comm_point **udp_ports;
+ /** number of udp ports */
+ size_t num_udp;
+
+ /** pending answers. sorted by id, addr */
+ rbtree_t *pending;
+ /** Each pending answer has a timeout, sorted by timeout. */
+ rbtree_t *pending_timeout;
+};
+
+/**
+ * A query that has an answer pending for it.
+ */
+struct pending {
+ /** redblacktree entry, key is the pending struct(id, addr). */
+ rbnode_t node;
+ /** the ID for the query */
+ uint16_t id;
+ /** remote address. */
+ struct sockaddr_storage addr;
+ /** length of addr field in use. */
+ socklen_t addrlen;
+ /** comm point it was sent on (and reply must come back on). */
+ struct comm_point* c;
+ /** the timeout of the query */
+ struct pending_timeout *timeout;
+};
+
+/**
+ * Timeout structure for pending queries
+ */
+struct pending_timeout {
+ /** entry in rbtree. key is timeout value, then pending ptr(uniq). */
+ rbnode_t node;
+ /** timeout, an absolute time value. */
+ struct timeval timeout;
+ /** pending query */
+ struct pending* pending;
+};
+
+/**
+ * Create outside_network structure with N udp ports.
+ * @param base: the communication base to use for event handling.
+ * @param bufsize: size for network buffers.
+ * @param num_ports: number of udp ports to open per interface.
+ * @param ifs: interface names (or NULL for default interface).
+ * @param num_ifs: number of names in array ifs.
+ * @param do_ip4: service IP4.
+ * @param do_ip6: service IP6.
+ * @param port_base: if -1 system assigns ports, otherwise try to get
+ * the ports numbered from this starting number.
+ * @return: the new empty structure or NULL on error.
+ */
+struct outside_network* outside_network_create(struct comm_base *base,
+ size_t bufsize, size_t num_ports, const char** ifs, int num_ifs,
+ int do_ip4, int do_ip6, int port_base);
+
+/**
+ * Delete outside_network structure.
+ * @param outnet: object to delete.
+ */
+void outside_network_delete(struct outside_network* outnet);
+
+/**
+ * Send UDP query, create pending answer.
+ * @param outnet: provides the event handling
+ * @param packet: wireformat query to send to destination.
+ * @param addr: address to send to.
+ * @param addrlen: length of addr.
+ * @param timeout: in seconds from now.
+ */
+void pending_udp_query(struct outside_network* outnet, ldns_buffer* packet,
+ struct sockaddr_storage* addr, socklen_t addrlen, int timeout);
+
+/**
+ * Delete pending answer.
+ * @param outnet: outside network the pending query is part of.
+ * Used internal, if NULL, p is not unlinked from rbtree.
+ * @param p: deleted
+ */
+void pending_delete(struct outside_network* outnet, struct pending* p);
+
+#endif /* OUTSIDE_NETWORK_H */
*/
/**
+ * \file
* Red black tree. Implementation taken from NSD 3.0.5, adjusted for use
* in unbound (memory allocation, logging and so on).
*/