diff -ur dnsmasq-2.43rc2/src/config.h dnsmasq-2.43/src/config.h --- dnsmasq-2.43rc2/src/config.h 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/config.h 2008-07-10 13:23:13.000000000 +0100 @@ -14,13 +14,14 @@ along with this program. If not, see . */ -#define VERSION "2.43rc2" +#define VERSION "2.43rc5" #define FTABSIZ 150 /* max number of outstanding requests (default) */ #define MAX_PROCS 20 /* max no children for TCP requests */ #define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */ #define EDNS_PKTSZ 1280 /* default max EDNS.0 UDP packet from RFC2671 */ #define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */ +#define RANDOM_SOCKS 64 /* max simultaneous random ports */ #define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */ #define CACHESIZ 150 /* default cache size */ #define MAXLEASES 150 /* maximum number of DHCP leases */ @@ -59,6 +60,7 @@ #define TFTP_PORT 69 #define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */ #define LOG_MAX 5 /* log-queue length */ +#define RANDFILE "/dev/urandom" /* DBUS interface specifics */ #define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" @@ -127,19 +129,6 @@ define this if you have arc4random() to get better security from DNS spoofs by using really random ids (OpenBSD) -HAVE_RANDOM - define this if you have the 4.2BSD random() function (and its - associated srandom() function), which is at least as good as (if not - better than) the rand() function. - -HAVE_DEV_RANDOM - define this if you have the /dev/random device, which gives truly - random numbers but may run out of random numbers. - -HAVE_DEV_URANDOM - define this if you have the /dev/urandom device, which gives - semi-random numbers when it runs out of truly random numbers. - HAVE_SOCKADDR_SA_LEN define this if struct sockaddr has sa_len field (*BSD) @@ -160,9 +149,6 @@ For Linux you should define HAVE_LINUX_NETWORK HAVE_GETOPT_LONG - HAVE_RANDOM - HAVE_DEV_RANDOM - HAVE_DEV_URANDOM you should NOT define HAVE_ARC4RANDOM HAVE_SOCKADDR_SA_LEN @@ -170,12 +156,8 @@ For *BSD systems you should define HAVE_BSD_NETWORK HAVE_SOCKADDR_SA_LEN - HAVE_RANDOM and you MAY define HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later - HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD - HAVE_DEV_RANDOM - FreeBSD and NetBSD - (OpenBSD with hardware random number generator) HAVE_GETOPT_LONG - NetBSD, later FreeBSD (FreeBSD and OpenBSD only if you link GNU getopt) @@ -203,18 +185,12 @@ #define HAVE_LINUX_NETWORK #define HAVE_GETOPT_LONG #undef HAVE_ARC4RANDOM -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM -#define HAVE_DEV_RANDOM #undef HAVE_SOCKADDR_SA_LEN /* Never use fork() on uClinux. Note that this is subtly different from the --keep-in-foreground option, since it also suppresses forking new processes for TCP connections and disables the call-a-script on leasechange system. It's intended for use on MMU-less kernels. */ #define NO_FORK #elif defined(__UCLIBC__) #define HAVE_LINUX_NETWORK @@ -223,16 +199,10 @@ # define HAVE_GETOPT_LONG #endif #undef HAVE_ARC4RANDOM -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM -#define HAVE_DEV_RANDOM #undef HAVE_SOCKADDR_SA_LEN #if !defined(__ARCH_HAS_MMU__) && !defined(__UCLIBC_HAS_MMU__) # define NO_FORK #endif #if defined(__UCLIBC_HAS_IPV6__) # ifndef IPV6_V6ONLY # define IPV6_V6ONLY 26 @@ -244,9 +214,6 @@ #define HAVE_LINUX_NETWORK #define HAVE_GETOPT_LONG #undef HAVE_ARC4RANDOM -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM -#define HAVE_DEV_RANDOM #undef HAVE_SOCKADDR_SA_LEN /* glibc < 2.2 has broken Sockaddr_in6 so we have to use our own. */ /* glibc < 2.2 doesn't define in_addr_t */ @@ -255,7 +222,6 @@ typedef unsigned long in_addr_t; # define HAVE_BROKEN_SOCKADDR_IN6 #endif #elif defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ @@ -269,35 +235,25 @@ #if !defined (__FreeBSD_kernel__) # define HAVE_ARC4RANDOM #endif -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM #define HAVE_SOCKADDR_SA_LEN #define HAVE_BSD_BRIDGE #elif defined(__APPLE__) #define HAVE_BSD_NETWORK #undef HAVE_GETOPT_LONG #define HAVE_ARC4RANDOM -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM #define HAVE_SOCKADDR_SA_LEN /* Define before sys/socket.h is included so we get socklen_t */ #define _BSD_SOCKLEN_T_ /* This is not defined in Mac OS X arpa/nameserv.h */ #define IN6ADDRSZ 16 #elif defined(__NetBSD__) #define HAVE_BSD_NETWORK #define HAVE_GETOPT_LONG #undef HAVE_ARC4RANDOM -#define HAVE_RANDOM -#define HAVE_DEV_URANDOM -#define HAVE_DEV_RANDOM #define HAVE_SOCKADDR_SA_LEN #define HAVE_BSD_BRIDGE -#define HAVE_LARGEFILE #elif defined(__sun) || defined(__sun__) #define HAVE_SOLARIS_NETWORK @@ -320,11 +276,8 @@ # define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) #endif #undef HAVE_ARC4RANDOM -#define HAVE_RANDOM -#undef HAVE_DEV_URANDOM -#undef HAVE_DEV_RANDOM #undef HAVE_SOCKADDR_SA_LEN + #define _XPG4_2 #define __EXTENSIONS__ #define ETHER_ADDR_LEN 6 diff -ur dnsmasq-2.43rc2/src/dnsmasq.c dnsmasq-2.43/src/dnsmasq.c --- dnsmasq-2.43rc2/src/dnsmasq.c 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/dnsmasq.c 2008-07-10 13:18:49.000000000 +0100 @@ -111,7 +111,7 @@ daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? daemon->edns_pktsz : DNSMASQ_PACKETSZ; daemon->packet = safe_malloc(daemon->packet_buff_sz); - + if (!daemon->lease_file) { if (daemon->dhcp) @@ -149,6 +149,8 @@ die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF); #endif + rand_init(); + now = dnsmasq_time(); if (daemon->dhcp) @@ -193,7 +195,7 @@ if (daemon->port != 0) cache_init(); - + if (daemon->options & OPT_DBUS) #ifdef HAVE_DBUS { @@ -963,7 +970,15 @@ FD_SET(serverfdp->fd, set); bump_maxfd(serverfdp->fd, maxfdp); } - + + if (daemon->port != 0 && !daemon->osport) + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount != 0) + { + FD_SET(daemon->randomsocks[i].fd, set); + bump_maxfd(daemon->randomsocks[i].fd, maxfdp); + } + for (listener = daemon->listeners; listener; listener = listener->next) { /* only listen for queries if we have resources */ @@ -1000,17 +1015,24 @@ static void check_dns_listeners(fd_set *set, time_t now) { struct serverfd *serverfdp; - struct listener *listener; - + struct listener *listener; + int i; + for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) if (FD_ISSET(serverfdp->fd, set)) - reply_query(serverfdp, now); + reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); + + if (daemon->port != 0 && !daemon->osport) + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount != 0 && + FD_ISSET(daemon->randomsocks[i].fd, set)) + reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); for (listener = daemon->listeners; listener; listener = listener->next) { if (listener->fd != -1 && FD_ISSET(listener->fd, set)) receive_query(listener, now); - + #ifdef HAVE_TFTP if (listener->tftpfd != -1 && FD_ISSET(listener->tftpfd, set)) tftp_request(listener, now); diff -ur dnsmasq-2.43rc2/src/dnsmasq.h dnsmasq-2.43/src/dnsmasq.h --- dnsmasq-2.43rc2/src/dnsmasq.h 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/dnsmasq.h 2008-07-09 20:47:31.000000000 +0100 @@ -317,6 +317,11 @@ struct serverfd *next; }; +struct randfd { + int fd; + unsigned short refcount, family; +}; + struct server { union mysockaddr addr, source_addr; char interface[IF_NAMESIZE+1]; @@ -367,6 +372,10 @@ union mysockaddr source; struct all_addr dest; struct server *sentto; /* NULL means free */ + struct randfd *rfd4; +#ifdef HAVE_IPV6 + struct randfd *rfd6; +#endif unsigned int iface; unsigned short orig_id, new_id; int fd, forwardall; @@ -563,7 +572,7 @@ char *mxtarget; char *lease_file; char *username, *groupname, *scriptuser; - int group_set; + int group_set, osport; char *domain_suffix; char *runfile; char *lease_change_command; @@ -598,6 +607,7 @@ int packet_buff_sz; /* size of above */ char *namebuff; /* MAXDNAME size buffer */ unsigned int local_answer, queries_forwarded; + struct frec *frec_list; struct serverfd *sfds; struct irec *interfaces; struct listener *listeners; @@ -605,7 +615,8 @@ struct server *srv_save; /* Used for resend on DoD */ size_t packet_len; /* " " */ pid_t tcp_pids[MAX_PROCS]; - + struct randfd randomsocks[RANDOM_SOCKS]; + /* DHCP state */ int dhcpfd, helperfd; #ifdef HAVE_LINUX_NETWORK @@ -672,6 +683,7 @@ unsigned char *pheader, size_t hlen); /* util.c */ +void rand_init(void); unsigned short rand16(void); int legal_char(char c); int canonicalise(char *s); @@ -711,7 +723,7 @@ void reread_dhcp(void); /* forward.c */ -void reply_query(struct serverfd *sfd, time_t now); +void reply_query(int fd, int family, time_t now); void receive_query(struct listener *listen, time_t now); unsigned char *tcp_request(int confd, time_t now, struct in_addr local_addr, struct in_addr netmask); @@ -720,6 +732,7 @@ /* network.c */ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp); +int random_sock(int family); void pre_allocate_sfds(void); int reload_servers(char *fname); void check_servers(void); diff -ur dnsmasq-2.43rc2/src/forward.c dnsmasq-2.43/src/forward.c --- dnsmasq-2.43rc2/src/forward.c 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/forward.c 2008-07-09 21:32:16.000000000 +0100 @@ -16,14 +16,13 @@ #include "dnsmasq.h" -static struct frec *frec_list = NULL; - static struct frec *lookup_frec(unsigned short id, unsigned int crc); static struct frec *lookup_frec_by_sender(unsigned short id, union mysockaddr *addr, unsigned int crc); static unsigned short get_id(int force, unsigned short force_id, unsigned int crc); - +static void free_frec(struct frec *f); +static struct randfd *allocate_rfd(int family); /* Send a UDP packet with its source address set as "source" unless nowild is true, when we just send it with the kernel default */ @@ -290,7 +289,32 @@ (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && !(start->flags & SERV_LITERAL_ADDRESS)) { - if (sendto(start->sfd->fd, (char *)header, plen, 0, + int fd; + + /* find server socket to use, may need to get random one. */ + if (start->sfd) + fd = start->sfd->fd; + else + { +#ifdef HAVE_IPV6 + if (start->addr.sa.sa_family == AF_INET6) + { + if (!forward->rfd6 && + !(forward->rfd6 = allocate_rfd(AF_INET6))) + break; + fd = forward->rfd6->fd; + } + else +#endif + { + if (!forward->rfd4 && + !(forward->rfd4 = allocate_rfd(AF_INET))) + break; + fd = forward->rfd4->fd; + } + } + + if (sendto(fd, (char *)header, plen, 0, &start->addr.sa, sa_len(&start->addr)) == -1) { @@ -334,7 +358,7 @@ /* could not send on, prepare to return */ header->id = htons(forward->orig_id); - forward->sentto = NULL; /* cancel */ + free_frec(forward); /* cancel */ } /* could not send on, return empty answer or address if known for whole domain */ @@ -426,7 +450,7 @@ } /* sets new last_server */ -void reply_query(struct serverfd *sfd, time_t now) +void reply_query(int fd, int family, time_t now) { /* packet from peer server, extract data for cache, and send to original requester */ @@ -434,91 +458,101 @@ union mysockaddr serveraddr; struct frec *forward; socklen_t addrlen = sizeof(serveraddr); - ssize_t n = recvfrom(sfd->fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen); + ssize_t n = recvfrom(fd, daemon->packet, daemon->edns_pktsz, 0, &serveraddr.sa, &addrlen); size_t nn; - + struct server *server; + /* packet buffer overwritten */ daemon->srv_save = NULL; /* Determine the address of the server replying so that we can mark that as good */ - serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family; + serveraddr.sa.sa_family = family; #ifdef HAVE_IPV6 if (serveraddr.sa.sa_family == AF_INET6) serveraddr.in6.sin6_flowinfo = 0; #endif + /* spoof check: answer must come from known server, */ + for (server = daemon->servers; server; server = server->next) + if (!(server->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) && + sockaddr_isequal(&server->addr, &serveraddr)) + break; + header = (HEADER *)daemon->packet; - if (n >= (int)sizeof(HEADER) && header->qr && - (forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff)))) - { - struct server *server = forward->sentto; - - if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && - !(daemon->options & OPT_ORDER) && - forward->forwardall == 0) - /* for broken servers, attempt to send to another one. */ + if (!server || + n < (int)sizeof(HEADER) || !header->qr || + !(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff)))) + return; + + server = forward->sentto; + + if ((header->rcode == SERVFAIL || header->rcode == REFUSED) && + !(daemon->options & OPT_ORDER) && + forward->forwardall == 0) + /* for broken servers, attempt to send to another one. */ + { + unsigned char *pheader; + size_t plen; + int is_sign; + + /* recreate query from reply */ + pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign); + if (!is_sign) { - unsigned char *pheader; - size_t plen; - int is_sign; - - /* recreate query from reply */ - pheader = find_pseudoheader(header, (size_t)n, &plen, NULL, &is_sign); - if (!is_sign) + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); + if ((nn = resize_packet(header, (size_t)n, pheader, plen))) { - header->ancount = htons(0); - header->nscount = htons(0); - header->arcount = htons(0); - if ((nn = resize_packet(header, (size_t)n, pheader, plen))) - { - header->qr = 0; - header->tc = 0; - forward_query(-1, NULL, NULL, 0, header, nn, now, forward); - return; - } + header->qr = 0; + header->tc = 0; + forward_query(-1, NULL, NULL, 0, header, nn, now, forward); + return; } - } - - if ((forward->sentto->flags & SERV_TYPE) == 0) - { - if (header->rcode == SERVFAIL || header->rcode == REFUSED) - server = NULL; - else - { - struct server *last_server; - /* find good server by address if possible, otherwise assume the last one we sent to */ - for (last_server = daemon->servers; last_server; last_server = last_server->next) - if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) && - sockaddr_isequal(&last_server->addr, &serveraddr)) - { - server = last_server; - break; - } - } - if (!(daemon->options & OPT_ALL_SERVERS)) - daemon->last_server = server; } - - /* If the answer is an error, keep the forward record in place in case - we get a good reply from another server. Kill it when we've - had replies from all to avoid filling the forwarding table when - everything is broken */ - if (forward->forwardall == 0 || --forward->forwardall == 1 || - (header->rcode != REFUSED && header->rcode != SERVFAIL)) + } + + if ((forward->sentto->flags & SERV_TYPE) == 0) + { + if (header->rcode == SERVFAIL || header->rcode == REFUSED) + server = NULL; + else { - if ((nn = process_reply(header, now, server, (size_t)n))) - { - header->id = htons(forward->orig_id); - header->ra = 1; /* recursion if available */ - send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn, - &forward->source, &forward->dest, forward->iface); - } - forward->sentto = NULL; /* cancel */ + struct server *last_server; + + /* find good server by address if possible, otherwise assume the last one we sent to */ + for (last_server = daemon->servers; last_server; last_server = last_server->next) + if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) && + sockaddr_isequal(&last_server->addr, &serveraddr)) + { + server = last_server; + break; + } + } + if (!(daemon->options & OPT_ALL_SERVERS)) + daemon->last_server = server; + } + + /* If the answer is an error, keep the forward record in place in case + we get a good reply from another server. Kill it when we've + had replies from all to avoid filling the forwarding table when + everything is broken */ + if (forward->forwardall == 0 || --forward->forwardall == 1 || + (header->rcode != REFUSED && header->rcode != SERVFAIL)) + { + if ((nn = process_reply(header, now, server, (size_t)n))) + { + header->id = htons(forward->orig_id); + header->ra = 1; /* recursion if available */ + send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn, + &forward->source, &forward->dest, forward->iface); } + free_frec(forward); /* cancel */ } } + void receive_query(struct listener *listen, time_t now) { HEADER *header = (HEADER *)daemon->packet; @@ -860,34 +894,99 @@ if ((f = (struct frec *)whine_malloc(sizeof(struct frec)))) { - f->next = frec_list; + f->next = daemon->frec_list; f->time = now; f->sentto = NULL; - frec_list = f; + f->rfd4 = NULL; +#ifdef HAVE_IPV6 + f->rfd6 = NULL; +#endif + daemon->frec_list = f; } return f; } +static struct randfd *allocate_rfd(int family) +{ + static int finger = 0; + int i; + + /* limit the number of sockets we have open to avoid starvation of + (eg) TFTP. Once we have a reasonable number, randomness should be OK */ + + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount == 0 && + (daemon->randomsocks[i].fd = random_sock(family)) != -1) + { + daemon->randomsocks[i].refcount = 1; + daemon->randomsocks[i].family = family; + return &daemon->randomsocks[i]; + } + + /* No free ones, grab an existing one */ + for (i = 0; i < RANDOM_SOCKS; i++) + { + int j = (i+finger) % RANDOM_SOCKS; + if (daemon->randomsocks[j].family == family && daemon->randomsocks[j].refcount != 0xffff) + { + finger = j; + daemon->randomsocks[j].refcount++; + return &daemon->randomsocks[j]; + } + } + + return NULL; /* doom */ +} + +static void free_frec(struct frec *f) +{ + if (f->rfd4 && --(f->rfd4->refcount) == 0) + close(f->rfd4->fd); + + f->rfd4 = NULL; + f->sentto = NULL; + +#ifdef HAVE_IPV6 + if (f->rfd6 && --(f->rfd6->refcount) == 0) + close(f->rfd6->fd); + + f->rfd6 = NULL; +#endif +} + /* if wait==NULL return a free or older than TIMEOUT record. else return *wait zero if one available, or *wait is delay to - when the oldest in-use record will expire. */ + when the oldest in-use record will expire. Impose an absolute + limit of 4*TIMEOUT before we wipe things (for random sockets) */ struct frec *get_new_frec(time_t now, int *wait) { - struct frec *f, *oldest; + struct frec *f, *oldest, *target; int count; if (wait) *wait = 0; - for (f = frec_list, oldest = NULL, count = 0; f; f = f->next, count++) + for (f = daemon->frec_list, oldest = NULL, target = NULL, count = 0; f; f = f->next, count++) if (!f->sentto) + target = f; + else { - f->time = now; - return f; + if (difftime(now, f->time) >= 4*TIMEOUT) + { + free_frec(f); + target = f; + } + + if (!oldest || difftime(f->time, oldest->time) <= 0) + oldest = f; } - else if (!oldest || difftime(f->time, oldest->time) <= 0) - oldest = f; + + if (target) + { + target->time = now; + return target; + } /* can't find empty one, use oldest if there is one and it's older than timeout */ @@ -902,7 +1001,7 @@ if (!wait) { - oldest->sentto = 0; + free_frec(oldest); oldest->time = now; } return oldest; @@ -928,7 +1027,7 @@ { struct frec *f; - for(f = frec_list; f; f = f->next) + for(f = daemon->frec_list; f; f = f->next) if (f->sentto && f->new_id == id && (f->crc == crc || crc == 0xffffffff)) return f; @@ -942,7 +1041,7 @@ { struct frec *f; - for(f = frec_list; f; f = f->next) + for(f = daemon->frec_list; f; f = f->next) if (f->sentto && f->orig_id == id && f->crc == crc && @@ -957,9 +1056,9 @@ { struct frec *f; - for (f = frec_list; f; f = f->next) + for (f = daemon->frec_list; f; f = f->next) if (f->sentto && f->sentto == server) - f->sentto = NULL; + free_frec(f); if (daemon->last_server == server) daemon->last_server = NULL; @@ -980,7 +1079,7 @@ { struct frec *f = lookup_frec(force_id, crc); if (f) - f->sentto = NULL; /* free */ + free_frec(f); /* free */ ret = force_id; } else do diff -ur dnsmasq-2.43rc2/src/network.c dnsmasq-2.43/src/network.c --- dnsmasq-2.43rc2/src/network.c 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/network.c 2008-07-09 15:59:39.000000000 +0100 @@ -441,6 +441,55 @@ return listeners; } + +/* return a UDP socket bound to a random port, have to coper with straying into + occupied port nos and reserved ones. */ +int random_sock(int family) +{ + int fd; + + if ((fd = socket(family, SOCK_DGRAM, 0)) != -1) + { + union mysockaddr addr; + + memset(&addr, 0, sizeof(addr)); + addr.in.sin_family = family; + + while (1) + { + if (family == AF_INET) + { + addr.in.sin_addr.s_addr = INADDR_ANY; + addr.in.sin_port = rand16(); +#ifdef HAVE_SOCKADDR_SA_LEN + addr.in.sin_len = sizeof(struct sockaddr_in); +#endif + } +#ifdef HAVE_IPV6 + else + { + addr.in6.sin6_addr = in6addr_any; + addr.in6.sin6_port = rand16(); +#ifdef HAVE_SOCKADDR_SA_LEN + addr.in6.sin6_len = sizeof(struct sockaddr_in6); +#endif + } +#endif + + if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0) + return fd; + + if (errno != EADDRINUSE && errno != EACCES) + break; + } + + close(fd); + } + + return -1; +} + + int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp) { union mysockaddr addr_copy = *addr; @@ -473,6 +522,25 @@ struct serverfd *sfd; int errsave; + /* when using random ports, servers which would otherwise use + the INADDR_ANY/port0 socket have sfd set to NULL */ + if (!daemon->osport) + { + errno = 0; + + if (addr->sa.sa_family == AF_INET && + addr->in.sin_addr.s_addr == INADDR_ANY && + addr->in.sin_port == htons(0)) + return NULL; + +#ifdef HAVE_IPV6 + if (addr->sa.sa_family == AF_INET6 && + memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 && + addr->in6.sin6_port == htons(0)) + return NULL; +#endif + } + /* may have a suitable one already */ for (sfd = daemon->sfds; sfd; sfd = sfd->next ) if (sockaddr_isequal(&sfd->source_addr, addr) && @@ -538,6 +606,7 @@ for (srv = daemon->servers; srv; srv = srv->next) if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) && !allocate_sfd(&srv->source_addr, srv->interface) && + errno != 0 && (daemon->options & OPT_NOWILD)) { prettyprint_addr(&srv->addr, daemon->namebuff); @@ -585,7 +654,9 @@ } /* Do we need a socket set? */ - if (!new->sfd && !(new->sfd = allocate_sfd(&new->source_addr, new->interface))) + if (!new->sfd && + !(new->sfd = allocate_sfd(&new->source_addr, new->interface)) && + errno != 0) { my_syslog(LOG_WARNING, _("ignoring nameserver %s - cannot make/bind socket: %s"), diff -ur dnsmasq-2.43rc2/src/option.c dnsmasq-2.43/src/option.c --- dnsmasq-2.43rc2/src/option.c 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/option.c 2008-07-10 13:32:40.000000000 +0100 @@ -1416,6 +1416,10 @@ case 'Q': /* --query-port */ if (!atoi_check(arg, &daemon->query_port)) option = '?'; + /* if explicitly set to zero, use single OS ephemeral port + and disable random ports */ + if (daemon->query_port == 0) + daemon->osport = 1; break; case 'T': /* --local-ttl */ diff -ur dnsmasq-2.43rc2/src/util.c dnsmasq-2.43/src/util.c --- dnsmasq-2.43rc2/src/util.c 2008-07-08 11:31:05.000000000 +0100 +++ dnsmasq-2.43/src/util.c 2008-07-09 20:24:25.000000000 +0100 @@ -16,82 +16,88 @@ /* Some code in this file contributed by Rob Funk. */ +/* The SURF random number generator was taken from djbdns-1.05, by + Daniel J Berstein, which is public domain. */ + + #include "dnsmasq.h" #ifdef HAVE_BROKEN_RTC #include #endif -/* Prefer arc4random(3) over random(3) over rand(3) */ -/* Also prefer /dev/urandom over /dev/random, to preserve the entropy pool */ + #ifdef HAVE_ARC4RANDOM -# define rand() arc4random() -# define srand(s) (void)0 -# define RANDFILE (NULL) -#else -# ifdef HAVE_RANDOM -# define rand() random() -# define srand(s) srandom(s) -# endif -# ifdef HAVE_DEV_URANDOM -# define RANDFILE "/dev/urandom" -# else -# ifdef HAVE_DEV_RANDOM -# define RANDFILE "/dev/random" -# else -# define RANDFILE (NULL) -# endif -# endif -#endif +void rand_init(void) +{ + return; +} unsigned short rand16(void) { - static int been_seeded = 0; - const char *randfile = RANDFILE; + return (unsigned short) (arc4random() >> 15); +} + +#else + +/* SURF random number generator */ + +typedef unsigned int uint32; + +static uint32 seed[32]; +static uint32 in[12]; +static uint32 out[8]; + +void rand_init() +{ + int fd = open(RANDFILE, O_RDONLY); - if (! been_seeded) - { - int fd, n = 0; - unsigned int c = 0, seed = 0, badseed; - char sbuf[sizeof(seed)]; - char *s; - struct timeval now; - - /* get the bad seed as a backup */ - /* (but we'd rather have something more random) */ - gettimeofday(&now, NULL); - badseed = now.tv_sec ^ now.tv_usec ^ (getpid() << 16); - - fd = open(randfile, O_RDONLY); - if (fd < 0) - seed = badseed; - else - { - s = (char *) &seed; - while ((c < sizeof(seed)) && - ((n = read(fd, sbuf, sizeof(seed)) > 0))) - { - memcpy(s, sbuf, n); - s += n; - c += n; - } - if (n < 0) - seed = badseed; - close(fd); - } + if (fd == -1 || + !read_write(fd, (unsigned char *)&seed, sizeof(seed), 1) || + !read_write(fd, (unsigned char *)&in, sizeof(in), 1)) + die(_("failed to seed the random number generator: %s"), NULL, EC_MISC); + + close(fd); +} - srand(seed); - been_seeded = 1; +#define ROTATE(x,b) (((x) << (b)) | ((x) >> (32 - (b)))) +#define MUSH(i,b) x = t[i] += (((x ^ seed[i]) + sum) ^ ROTATE(x,b)); + +static void surf(void) +{ + uint32 t[12]; uint32 x; uint32 sum = 0; + int r; int i; int loop; + + for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i]; + for (i = 0;i < 8;++i) out[i] = seed[24 + i]; + x = t[11]; + for (loop = 0;loop < 2;++loop) { + for (r = 0;r < 16;++r) { + sum += 0x9e3779b9; + MUSH(0,5) MUSH(1,7) MUSH(2,9) MUSH(3,13) + MUSH(4,5) MUSH(5,7) MUSH(6,9) MUSH(7,13) + MUSH(8,5) MUSH(9,7) MUSH(10,9) MUSH(11,13) } - - /* Some rand() implementations have less randomness in low bits - * than in high bits, so we only pay attention to the high ones. - * But most implementations don't touch the high bit, so we - * ignore that one. - */ - return( (unsigned short) (rand() >> 15) ); + for (i = 0;i < 8;++i) out[i] ^= t[i + 4]; + } } +unsigned short rand16(void) +{ + static int outleft = 0; + + if (!outleft) { + if (!++in[0]) if (!++in[1]) if (!++in[2]) ++in[3]; + surf(); + outleft = 8; + } + + return (unsigned short) out[--outleft]; +} + +#endif + + int legal_char(char c) { /* check for legal char a-z A-Z 0-9 -