From: Wouter Wijngaards Date: Wed, 31 Jan 2007 15:38:44 +0000 (+0000) Subject: outside_network start. X-Git-Tag: release-0.0~77 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57aef52d4822e5419dcfdd9790c96399d13d54c3;p=thirdparty%2Funbound.git outside_network start. git-svn-id: file:///svn/unbound/trunk@48 be551aaa-1e26-0410-a405-d3ace91eadb9 --- diff --git a/configure.ac b/configure.ac index 7117bebb2..57defe0c0 100644 --- a/configure.ac +++ b/configure.ac @@ -300,6 +300,8 @@ AH_BOTTOM([ #include "ldns/ldns.h" +/** default port to listen for queries, passed to getaddrinfo */ +#define UNBOUND_DNS_PORT "53" ]) AC_CONFIG_FILES([Makefile]) diff --git a/daemon/unbound.c b/daemon/unbound.c index 91e930beb..373d1280b 100644 --- a/daemon/unbound.c +++ b/daemon/unbound.c @@ -44,8 +44,6 @@ #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 @@ -77,8 +75,12 @@ main(int argc, char* argv[]) { 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 */ @@ -88,6 +90,7 @@ main(int argc, char* argv[]) 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': @@ -109,10 +112,14 @@ main(int argc, char* argv[]) } /* 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); diff --git a/daemon/worker.c b/daemon/worker.c index ca2b5f43a..6f7525457 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -44,12 +44,23 @@ #include "daemon/worker.h" #include "util/netevent.h" #include "services/listen_dnsport.h" +#include "services/outside_network.h" + +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#include +#include + +/** 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. @@ -124,7 +135,8 @@ static int worker_handle_request(struct comm_point* c, void* arg, int 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) { struct worker* worker = (struct worker*)calloc(1, sizeof(struct worker)); @@ -143,10 +155,22 @@ struct worker* worker_init(const char* port, int do_ip4, int do_ip6, 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); @@ -157,6 +181,26 @@ void worker_delete(struct worker* worker) 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; +} diff --git a/daemon/worker.h b/daemon/worker.h index b8ae64012..30f171672 100644 --- a/daemon/worker.h +++ b/daemon/worker.h @@ -46,6 +46,10 @@ #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. @@ -58,11 +62,23 @@ struct worker { /** 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; }; /** @@ -74,10 +90,13 @@ struct worker { * @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. @@ -89,4 +108,13 @@ void worker_work(struct worker* worker); */ 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 */ diff --git a/doc/Changelog b/doc/Changelog index 31a420d35..8d5d2afb5 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,7 @@ - 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. diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index ec5781189..d28012326 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -79,12 +79,7 @@ verbose_print_addr(struct addrinfo *addr) } } -/** - * 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; diff --git a/services/listen_dnsport.h b/services/listen_dnsport.h index 70818db27..385fea964 100644 --- a/services/listen_dnsport.h +++ b/services/listen_dnsport.h @@ -45,6 +45,7 @@ #include "config.h" #include "util/netevent.h" struct listen_list; +struct addrinfo; /** * Listening for queries structure. @@ -101,4 +102,13 @@ struct listen_dnsport* listen_create(struct comm_base* base, */ 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 */ diff --git a/services/outside_network.c b/services/outside_network.c new file mode 100644 index 000000000..e81dc872c --- /dev/null +++ b/services/outside_network.c @@ -0,0 +1,267 @@ +/* + * 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 +#endif +#include +#include +#include + +/** 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; iudp_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; kpending) { + 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; inum_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; +} diff --git a/services/outside_network.h b/services/outside_network.h new file mode 100644 index 000000000..dee539488 --- /dev/null +++ b/services/outside_network.h @@ -0,0 +1,151 @@ +/* + * 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 */ diff --git a/util/rbtree.h b/util/rbtree.h index e2353e8c2..1507eb302 100644 --- a/util/rbtree.h +++ b/util/rbtree.h @@ -37,6 +37,7 @@ */ /** + * \file * Red black tree. Implementation taken from NSD 3.0.5, adjusted for use * in unbound (memory allocation, logging and so on). */