Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15:Update
dnsmasq
dnsmasq-CVE-2021-3448.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File dnsmasq-CVE-2021-3448.patch of Package dnsmasq
From d88dc5e696f1b8b95e416890ac831eb0c26250ff Mon Sep 17 00:00:00 2001 From: Simon Kelley <simon@thekelleys.org.uk> Date: Mon, 15 Mar 2021 21:59:51 +0000 Subject: [PATCH] Use random source ports where possible if source addresses/interfaces in use. CVE-2021-3448 applies. It's possible to specify the source address or interface to be used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4 or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of these have, until now, used a single socket, bound to a fixed port. This was originally done to allow an error (non-existent interface, or non-local address) to be detected at start-up. This means that any upstream servers specified in such a way don't use random source ports, and are more susceptible to cache-poisoning attacks. We now use random ports where possible, even when the source is specified, so server=8.8.8.8@1.2.3.4 or server=8.8.8.8@eth0 will use random source ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will use the explicitly configured port, and should only be done with understanding of the security implications. Note that this change changes non-existing interface, or non-local source address errors from fatal to run-time. The error will be logged and communiction with the server not possible. --- man/dnsmasq.8 | 4 +- src/dnsmasq.c | 31 +++-- src/dnsmasq.h | 28 ++-- src/forward.c | 373 +++++++++++++++++++++++++++++++------------------- src/loop.c | 20 +-- src/network.c | 100 ++++---------- src/option.c | 3 +- src/tftp.c | 6 +- src/util.c | 2 +- 9 files changed, 310 insertions(+), 257 deletions(-) --- man/dnsmasq.8.orig +++ man/dnsmasq.8 @@ -418,7 +418,7 @@ Tells dnsmasq to never forward A or AAAA or domain parts, to upstream nameservers. If the name is not known from /etc/hosts or DHCP then a "not found" answer is returned. .TP -.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]] +.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]] Specify IP address of upstream servers directly. Setting this flag does not suppress reading of /etc/resolv.conf, use -R to do that. If one or more @@ -480,7 +480,7 @@ source address specified but the port ma part of the source address. Forcing queries to an interface is not implemented on all platforms supported by dnsmasq. .TP -.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]] +.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|@<interface>[#<port>]] This is functionally the same as .B --server, but provides some syntactic sugar to make specifying address-to-name queries easier. For example --- src/dnsmasq.c.orig +++ src/dnsmasq.c @@ -1496,6 +1496,7 @@ static int set_dns_listeners(time_t now) { struct serverfd *serverfdp; struct listener *listener; + struct randfd_list *rfl; int wait = 0, i; #ifdef HAVE_TFTP @@ -1515,11 +1516,14 @@ static int set_dns_listeners(time_t now) for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) poll_listen(serverfdp->fd, POLLIN); - if (daemon->port != 0 && !daemon->osport) - for (i = 0; i < RANDOM_SOCKS; i++) - if (daemon->randomsocks[i].refcount != 0) - poll_listen(daemon->randomsocks[i].fd, POLLIN); - + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount != 0) + poll_listen(daemon->randomsocks[i].fd, POLLIN); + + /* Check overflow random sockets too. */ + for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) + poll_listen(rfl->rfd->fd, POLLIN); + for (listener = daemon->listeners; listener; listener = listener->next) { /* only listen for queries if we have resources */ @@ -1550,17 +1554,22 @@ static void check_dns_listeners(time_t n { struct serverfd *serverfdp; struct listener *listener; + struct randfd_list *rfl; int i; for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) if (poll_check(serverfdp->fd, POLLIN)) - reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); + reply_query(serverfdp->fd, now); - if (daemon->port != 0 && !daemon->osport) - for (i = 0; i < RANDOM_SOCKS; i++) - if (daemon->randomsocks[i].refcount != 0 && - poll_check(daemon->randomsocks[i].fd, POLLIN)) - reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount != 0 && + poll_check(daemon->randomsocks[i].fd, POLLIN)) + reply_query(daemon->randomsocks[i].fd, now); + + /* Check overflow random sockets too. */ + for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) + if (poll_check(rfl->rfd->fd, POLLIN)) + reply_query(rfl->rfd->fd, now); for (listener = daemon->listeners; listener; listener = listener->next) { --- src/dnsmasq.h.orig +++ src/dnsmasq.h @@ -509,13 +509,20 @@ struct serverfd { }; struct randfd { + struct server *serv; int fd; - unsigned short refcount, family; + unsigned short refcount; /* refcount == 0xffff means overflow record. */ }; - + +struct randfd_list { + struct randfd *rfd; + struct randfd_list *next; +}; + struct server { union mysockaddr addr, source_addr; char interface[IF_NAMESIZE+1]; + unsigned int ifindex; /* corresponding to interface, above */ struct serverfd *sfd; char *domain; /* set if this server only handles a domain. */ int flags, tcpfd, edns_pktsz; @@ -625,10 +632,7 @@ struct frec { struct frec_src *next; } frec_src; struct server *sentto; /* NULL means free */ - struct randfd *rfd4; -#ifdef HAVE_IPV6 - struct randfd *rfd6; -#endif + struct randfd_list *rfds; unsigned short new_id; int fd, forwardall, flags; time_t time; @@ -1044,9 +1048,10 @@ extern struct daemon { int forwardcount; struct server *srv_save; /* Used for resend on DoD */ size_t packet_len; /* " " */ - struct randfd *rfd_save; /* " " */ + int fd_save; /* " " */ pid_t tcp_pids[MAX_PROCS]; struct randfd randomsocks[RANDOM_SOCKS]; + struct randfd_list *rfl_spare, *rfl_poll; int v6pktinfo; struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ int log_id, log_display_id; /* ids of transactions for logging */ @@ -1199,7 +1204,7 @@ void *safe_malloc(size_t size); void safe_pipe(int *fd, int read_noblock); void *whine_malloc(size_t size); int sa_len(union mysockaddr *addr); -int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); +int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); int hostname_isequal(const char *a, const char *b); time_t dnsmasq_time(void); int netmask_length(struct in_addr mask); @@ -1248,7 +1253,7 @@ char *parse_server(char *arg, union myso int option_read_dynfile(char *file, int flags); /* forward.c */ -void reply_query(int fd, int family, time_t now); +void reply_query(int fd, time_t now); void receive_query(struct listener *listen, time_t now); unsigned char *tcp_request(int confd, time_t now, union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); @@ -1258,13 +1263,13 @@ int send_from(int fd, int nowild, char * union mysockaddr *to, struct all_addr *source, unsigned int iface); void resend_query(void); -struct randfd *allocate_rfd(int family); -void free_rfd(struct randfd *rfd); +int allocate_rfd(struct randfd_list **fdlp, struct server *serv); +void free_rfds(struct randfd_list **fdlp); + /* network.c */ int indextoname(int fd, int index, char *name); 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 mark_servers(int flag); --- src/forward.c.orig +++ src/forward.c @@ -16,7 +16,7 @@ #include "dnsmasq.h" -static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); +static struct frec *lookup_frec(unsigned short id, int fd, void *hash); static struct frec *lookup_frec_by_sender(unsigned short id, union mysockaddr *addr, void *hash); @@ -291,29 +291,19 @@ static int forward_query(int udpfd, unio if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) PUTSHORT(SAFE_PKTSZ, pheader); - if (forward->sentto->addr.sa.sa_family == AF_INET) - log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); -#ifdef HAVE_IPV6 - else - log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); -#endif - - if (forward->sentto->sfd) - fd = forward->sentto->sfd->fd; - else + if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1) { + if (forward->sentto->addr.sa.sa_family == AF_INET) + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (struct all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); #ifdef HAVE_IPV6 - if (forward->sentto->addr.sa.sa_family == AF_INET6) - fd = forward->rfd6->fd; else + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (struct all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); #endif - fd = forward->rfd4->fd; + while (retry_send(sendto(fd, (char *)header, plen, 0, + &forward->sentto->addr.sa, + sa_len(&forward->sentto->addr)))); } - - while (retry_send( sendto(fd, (char *)header, plen, 0, - &forward->sentto->addr.sa, - sa_len(&forward->sentto->addr)))); - + return 1; } #endif @@ -481,51 +471,26 @@ static int forward_query(int udpfd, unio while (1) { + int fd; + /* only send to servers dealing with our domain. domain may be NULL, in which case server->domain must be NULL also. */ if (type == (start->flags & SERV_TYPE) && (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && - !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) + !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && + ((fd = allocate_rfd(&forward->rfds, start)) != -1)) { - 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; - daemon->rfd_save = forward->rfd6; - fd = forward->rfd6->fd; - } - else -#endif - { - if (!forward->rfd4 && - !(forward->rfd4 = allocate_rfd(AF_INET))) - break; - daemon->rfd_save = forward->rfd4; - fd = forward->rfd4->fd; - } - #ifdef HAVE_CONNTRACK - /* Copy connection mark of incoming query to outgoing connection. */ - if (option_bool(OPT_CONNTRACK)) - { - unsigned int mark; - if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) - setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); - } + /* Copy connection mark of incoming query to outgoing connection. */ + if (option_bool(OPT_CONNTRACK)) + { + unsigned int mark; + if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) + setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); + } #endif - } - #ifdef HAVE_DNSSEC if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER)) { @@ -552,6 +517,7 @@ static int forward_query(int udpfd, unio /* Keep info in case we want to re-send this packet */ daemon->srv_save = start; daemon->packet_len = plen; + daemon->fd_save = fd; if (!gotname) strcpy(daemon->namebuff, "query"); @@ -570,7 +536,7 @@ static int forward_query(int udpfd, unio break; forward->forwardall++; } - } + } if (!(start = start->next)) start = daemon->servers; @@ -770,7 +736,7 @@ static size_t process_reply(struct dns_h } /* sets new last_server */ -void reply_query(int fd, int family, time_t now) +void reply_query(int fd, time_t now) { /* packet from peer server, extract data for cache, and send to original requester */ @@ -785,9 +751,8 @@ void reply_query(int fd, int family, tim /* 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 = family; #ifdef HAVE_IPV6 if (serveraddr.sa.sa_family == AF_INET6) serveraddr.in6.sin6_flowinfo = 0; @@ -809,7 +774,7 @@ void reply_query(int fd, int family, tim hash = hash_questions(header, n, daemon->namebuff); - if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) + if (!(forward = lookup_frec(ntohs(header->id), fd, hash))) return; /* log_query gets called indirectly all over the place, so @@ -997,9 +962,8 @@ void reply_query(int fd, int family, tim } new->sentto = server; - new->rfd4 = NULL; + new->rfds = NULL; #ifdef HAVE_IPV6 - new->rfd6 = NULL; #endif new->frec_src.next = NULL; new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY); @@ -1028,26 +992,7 @@ void reply_query(int fd, int family, tim /* Don't resend this. */ daemon->srv_save = NULL; - if (server->sfd) - fd = server->sfd->fd; - else - { - fd = -1; -#ifdef HAVE_IPV6 - if (server->addr.sa.sa_family == AF_INET6) - { - if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) - fd = new->rfd6->fd; - } - else -#endif - { - if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) - fd = new->rfd4->fd; - } - } - - if (fd != -1) + if ((fd = allocate_rfd(&new->rfds, server)) != -1) { #ifdef HAVE_CONNTRACK /* Copy connection mark of incoming query to outgoing connection. */ @@ -1204,7 +1149,7 @@ void receive_query(struct listener *list /* packet buffer overwritten */ daemon->srv_save = NULL; - + dst_addr_4.s_addr = dst_addr.addr.addr4.s_addr = 0; netmask.s_addr = 0; @@ -2036,10 +1981,9 @@ static struct frec *allocate_frec(time_t f->next = daemon->frec_list; f->time = now; f->sentto = NULL; - f->rfd4 = NULL; + f->rfds = NULL; f->flags = 0; #ifdef HAVE_IPV6 - f->rfd6 = NULL; #endif #ifdef HAVE_DNSSEC f->dependent = NULL; @@ -2052,46 +1996,192 @@ static struct frec *allocate_frec(time_t return f; } -struct randfd *allocate_rfd(int family) +/* return a UDP socket bound to a random port, have to cope with straying into + occupied port nos and reserved ones. */ +static int random_sock(struct server *s) { - static int finger = 0; - int i; + int fd; + + if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1) + { + if (local_bind(fd, &s->source_addr, s->interface, 0)) + return fd; + + if (s->interface[0] == 0) + (void)prettyprint_addr(&s->source_addr, daemon->namebuff); + else + strcpy(daemon->namebuff, s->interface); + my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), + daemon->namebuff, strerror(errno)); + close(fd); + } + + return -1; +} + +/* compare source addresses and interface, serv2 can be null. */ +static int server_isequal(const struct server *serv1, + const struct server *serv2) +{ + return (serv2 && + serv2->ifindex == serv1->ifindex && + sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) && + strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0); +} + +/* fdlp points to chain of randomfds already in use by transaction. + If there's already a suitable one, return it, else allocate a + new one and add it to the list. + + Not leaking any resources in the face of allocation failures + is rather convoluted here. + + Note that rfd->serv may be NULL, when a server goes away. +*/ +int allocate_rfd(struct randfd_list **fdlp, struct server *serv) +{ + static int finger = 0; + int i, j = 0; + struct randfd_list *rfl; + struct randfd *rfd = NULL; + int fd = 0; + + /* If server has a pre-allocated fd, use that. */ + if (serv->sfd) + return serv->sfd->fd; + + /* existing suitable random port socket linked to this transaction? */ + for (rfl = *fdlp; rfl; rfl = rfl->next) + if (server_isequal(serv, rfl->rfd->serv)) + return rfl->rfd->fd; + + /* No. need new link. */ + if ((rfl = daemon->rfl_spare)) + daemon->rfl_spare = rfl->next; + else if (!(rfl = whine_malloc(sizeof(struct randfd_list)))) + return -1; + /* 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) { - if ((daemon->randomsocks[i].fd = random_sock(family)) == -1) - break; - - daemon->randomsocks[i].refcount = 1; - daemon->randomsocks[i].family = family; - return &daemon->randomsocks[i]; + if ((fd = random_sock(serv)) != -1) + { + rfd = &daemon->randomsocks[i]; + rfd->serv = serv; + rfd->fd = fd; + rfd->refcount = 1; + } + break; } - + /* No free ones or cannot get new socket, grab an existing one */ - for (i = 0; i < RANDOM_SOCKS; i++) + if (!rfd) + for (j = 0; j < RANDOM_SOCKS; j++) + { + i = (j + finger) % RANDOM_SOCKS; + if (daemon->randomsocks[i].refcount != 0 && + server_isequal(serv, daemon->randomsocks[i].serv) && + daemon->randomsocks[i].refcount != 0xfffe) + { + finger = i + 1; + rfd = &daemon->randomsocks[i]; + rfd->refcount++; + break; + } + } + + if (j == RANDOM_SOCKS) { - int j = (i+finger) % RANDOM_SOCKS; - if (daemon->randomsocks[j].refcount != 0 && - daemon->randomsocks[j].family == family && - daemon->randomsocks[j].refcount != 0xffff) + struct randfd_list *rfl_poll; + + /* there are no free slots, and non with the same parameters we can piggy-back on. + We're going to have to allocate a new temporary record, distinguished by + refcount == 0xffff. This will exist in the frec randfd list, never be shared, + and be freed when no longer in use. It will also be held on + the daemon->rfl_poll list so the poll system can find it. */ + + if ((rfl_poll = daemon->rfl_spare)) + daemon->rfl_spare = rfl_poll->next; + else + rfl_poll = whine_malloc(sizeof(struct randfd_list)); + + if (!rfl_poll || + !(rfd = whine_malloc(sizeof(struct randfd))) || + (fd = random_sock(serv)) == -1) { - finger = j; - daemon->randomsocks[j].refcount++; - return &daemon->randomsocks[j]; + + /* Don't leak anything we may already have */ + rfl->next = daemon->rfl_spare; + daemon->rfl_spare = rfl; + + if (rfl_poll) + { + rfl_poll->next = daemon->rfl_spare; + daemon->rfl_spare = rfl_poll; + } + + if (rfd) + free(rfd); + + return -1; /* doom */ } - } - return NULL; /* doom */ + /* Note rfd->serv not set here, since it's not reused */ + rfd->fd = fd; + rfd->refcount = 0xffff; /* marker for temp record */ + + rfl_poll->rfd = rfd; + rfl_poll->next = daemon->rfl_poll; + daemon->rfl_poll = rfl_poll; + } + + rfl->rfd = rfd; + rfl->next = *fdlp; + *fdlp = rfl; + + return rfl->rfd->fd; } -void free_rfd(struct randfd *rfd) +void free_rfds(struct randfd_list **fdlp) { - if (rfd && --(rfd->refcount) == 0) - close(rfd->fd); + struct randfd_list *tmp, *rfl, *poll, *next, **up; + + for (rfl = *fdlp; rfl; rfl = tmp) + { + if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0) + close(rfl->rfd->fd); + + /* temporary overflow record */ + if (rfl->rfd->refcount == 0xffff) + { + free(rfl->rfd); + + /* go through the link of all these by steam to delete. + This list is expected to be almost always empty. */ + for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next) + { + next = poll->next; + + if (poll->rfd == rfl->rfd) + { + *up = poll->next; + poll->next = daemon->rfl_spare; + daemon->rfl_spare = poll; + } + else + up = &poll->next; + } + } + + tmp = rfl->next; + rfl->next = daemon->rfl_spare; + daemon->rfl_spare = rfl; + } + + *fdlp = NULL; } static void free_frec(struct frec *f) @@ -2107,14 +2197,11 @@ static void free_frec(struct frec *f) } f->frec_src.next = NULL; - free_rfd(f->rfd4); - f->rfd4 = NULL; + free_rfds(&f->rfds); f->sentto = NULL; f->flags = 0; #ifdef HAVE_IPV6 - free_rfd(f->rfd6); - f->rfd6 = NULL; #endif #ifdef HAVE_DNSSEC @@ -2222,26 +2309,39 @@ struct frec *get_new_frec(time_t now, in } /* crc is all-ones if not known. */ -static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) +static struct frec *lookup_frec(unsigned short id, int fd, void *hash) { struct frec *f; - + struct server *s; + int type; + struct randfd_list *fdl; + for(f = daemon->frec_list; f; f = f->next) if (f->sentto && f->new_id == id && (memcmp(hash, f->hash, HASH_SIZE) == 0)) { /* sent from random port */ - if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) - return f; - - if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) - return f; - - /* sent to upstream from bound socket. */ - if (f->sentto->sfd && f->sentto->sfd->fd == fd) + for (fdl = f->rfds; fdl; fdl = fdl->next) + if (fdl->rfd->fd == fd) return f; + + /* Sent to upstream from socket associated with a server. + Note we have to iterate over all the possible servers, since they may + have different bound sockets. */ + type = f->sentto->flags & SERV_TYPE; + s = f->sentto; + do { + if ((type == (s->flags & SERV_TYPE)) && + (type != SERV_HAS_DOMAIN || + (s->domain && hostname_isequal(f->sentto->domain, s->domain))) && + !(s->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && + s->sfd && s->sfd->fd == fd) + return f; + + s = s->next ? s->next : daemon->servers; + } while (s != f->sentto); } - + return NULL; } @@ -2287,31 +2387,27 @@ static struct frec *lookup_frec_by_query void resend_query() { if (daemon->srv_save) - { - int fd; - - if (daemon->srv_save->sfd) - fd = daemon->srv_save->sfd->fd; - else if (daemon->rfd_save && daemon->rfd_save->refcount != 0) - fd = daemon->rfd_save->fd; - else - return; - - while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0, - &daemon->srv_save->addr.sa, - sa_len(&daemon->srv_save->addr)))); - } + while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0, + &daemon->srv_save->addr.sa, + sa_len(&daemon->srv_save->addr)))); } /* A server record is going away, remove references to it */ void server_gone(struct server *server) { struct frec *f; + int i; for (f = daemon->frec_list; f; f = f->next) if (f->sentto && f->sentto == server) free_frec(f); - + + /* If any random socket refers to this server, NULL the reference. + No more references to the socket will be created in the future. */ + for (i = 0; i < RANDOM_SOCKS; i++) + if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server) + daemon->randomsocks[i].serv = NULL; + if (daemon->last_server == server) daemon->last_server = NULL; --- src/loop.c.orig +++ src/loop.c @@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid); void loop_send_probes() { struct server *serv; + struct randfd_list *rfds = NULL; if (!option_bool(OPT_LOOP_DETECT)) return; @@ -34,29 +35,22 @@ void loop_send_probes() { ssize_t len = loop_make_probe(serv->uid); int fd; - struct randfd *rfd = NULL; - if (serv->sfd) - fd = serv->sfd->fd; - else - { - if (!(rfd = allocate_rfd(serv->addr.sa.sa_family))) - continue; - fd = rfd->fd; - } - + if ((fd = allocate_rfd(&rfds, serv)) == -1) + continue; + while (retry_send(sendto(fd, daemon->packet, len, 0, &serv->addr.sa, sa_len(&serv->addr)))); - - free_rfd(rfd); } + + free_rfds(&rfds); } static ssize_t loop_make_probe(u32 uid) { struct dns_header *header = (struct dns_header *)daemon->packet; unsigned char *p = (unsigned char *)(header+1); - + /* packet buffer overwritten */ daemon->srv_save = NULL; --- src/network.c.orig +++ src/network.c @@ -565,7 +565,8 @@ int enumerate_interfaces(int reset) #ifdef HAVE_AUTH struct auth_zone *zone; #endif - + struct server *serv; + /* Do this max once per select cycle - also inhibits netlink socket use in TCP child processes. */ @@ -582,7 +583,14 @@ int enumerate_interfaces(int reset) if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return 0; - + + /* iface indexes can change when interfaces are created/destroyed. + We use them in the main forwarding control path, when the path + to a server is specified by an interface, so cache them. + Update the cache here. */ + for (serv = daemon->servers; serv; serv = serv->next) + serv->ifindex = if_nametoindex(serv->interface); + /* Mark interfaces for garbage collection */ for (iface = daemon->interfaces; iface; iface = iface->next) iface->found = 0; @@ -680,7 +688,7 @@ int enumerate_interfaces(int reset) errno = errsave; spare = param.spare; - + return ret; } @@ -819,10 +827,10 @@ int tcp_interface(int fd, int af) /* use mshdr so that the CMSDG_* macros are available */ msg.msg_control = daemon->packet; msg.msg_controllen = len = daemon->packet_buff_sz; - + /* we overwrote the buffer... */ - daemon->srv_save = NULL; - + daemon->srv_save = NULL; + if (af == AF_INET) { if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 && @@ -1129,64 +1137,6 @@ void join_multicast(int dienow) } #endif -/* return a UDP socket bound to a random port, have to cope 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; - unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; - int tries = ports_avail < 30 ? 3 * ports_avail : 100; - - memset(&addr, 0, sizeof(addr)); - addr.sa.sa_family = family; - - /* don't loop forever if all ports in use. */ - - if (fix_fd(fd)) - while(tries--) - { - unsigned short port = rand16(); - - if (daemon->min_port != 0 || daemon->max_port != MAX_PORT) - port = htons(daemon->min_port + (port % ((unsigned short)ports_avail))); - - if (family == AF_INET) - { - addr.in.sin_addr.s_addr = INADDR_ANY; - addr.in.sin_port = port; -#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 = port; -#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; @@ -1214,39 +1164,34 @@ int local_bind(int fd, union mysockaddr return 1; } -static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) +static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex) { struct serverfd *sfd; - unsigned int ifindex = 0; int errsave; /* when using random ports, servers which would otherwise use - the INADDR_ANY/port0 socket have sfd set to NULL */ - if (!daemon->osport && intname[0] == 0) + the INADDR_ANY/port0 socket have sfd set to NULL, this is + anything without an explictly set source port. */ + 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 } - if (intname && strlen(intname) != 0) - ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */ - /* may have a suitable one already */ for (sfd = daemon->sfds; sfd; sfd = sfd->next ) - if (sockaddr_isequal(&sfd->source_addr, addr) && - strcmp(intname, sfd->interface) == 0 && - ifindex == sfd->ifindex) + if (ifindex == sfd->ifindex && + sockaddr_isequal(&sfd->source_addr, addr) && + strcmp(intname, sfd->interface) == 0) return sfd; /* need to make a new one. */ @@ -1294,7 +1239,7 @@ void pre_allocate_sfds(void) #ifdef HAVE_SOCKADDR_SA_LEN addr.in.sin_len = sizeof(struct sockaddr_in); #endif - allocate_sfd(&addr, ""); + allocate_sfd(&addr, "", 0); #ifdef HAVE_IPV6 memset(&addr, 0, sizeof(addr)); addr.in6.sin6_family = AF_INET6; @@ -1303,13 +1248,13 @@ void pre_allocate_sfds(void) #ifdef HAVE_SOCKADDR_SA_LEN addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif - allocate_sfd(&addr, ""); + allocate_sfd(&addr, "", 0); #endif } for (srv = daemon->servers; srv; srv = srv->next) if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) && - !allocate_sfd(&srv->source_addr, srv->interface) && + !allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) && errno != 0 && option_bool(OPT_NOWILD)) { @@ -1521,7 +1466,7 @@ void check_servers(void) /* Do we need a socket set? */ if (!serv->sfd && - !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && + !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && errno != 0) { my_syslog(LOG_WARNING, --- src/option.c.orig +++ src/option.c @@ -793,6 +793,7 @@ char *parse_server(char *arg, union myso { #if defined(SO_BINDTODEVICE) strncpy(interface, interface_opt, IF_NAMESIZE - 1); + source = interface_opt; #else return _("interface binding not supported"); #endif --- src/tftp.c.orig +++ src/tftp.c @@ -96,7 +96,7 @@ void tftp_request(struct listener *liste if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; - + /* Can always get recvd interface for IPv6 */ if (!check_dest) { @@ -566,7 +566,7 @@ void check_tftp_listeners(time_t now) { /* we overwrote the buffer... */ daemon->srv_save = NULL; - + if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack)) { if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) @@ -609,7 +609,7 @@ void check_tftp_listeners(time_t now) /* we overwrote the buffer... */ daemon->srv_save = NULL; - + if ((len = get_block(daemon->packet, transfer)) == -1) { len = tftp_err_oops(daemon->packet, transfer->file->filename); --- src/util.c.orig +++ src/util.c @@ -297,7 +297,7 @@ void *whine_malloc(size_t size) return ret; } -int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2) +int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2) { if (s1->sa.sa_family == s2->sa.sa_family) {
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor