diff options
| author | Armin Kuster <akuster@mvista.com> | 2021-09-10 15:16:48 -0700 |
|---|---|---|
| committer | Armin Kuster <akuster@mvista.com> | 2021-09-10 15:16:48 -0700 |
| commit | 2e7e98cd0cb82db214b13224c71134b9335a719b (patch) | |
| tree | 758e6fa5bef92a11521ea0a07b06ea40ab01887d | |
| parent | 06d80777f47891ec876b55212790deb5fef9116e (diff) | |
| download | meta-openembedded-2e7e98cd0cb82db214b13224c71134b9335a719b.tar.gz | |
dnsmasq: Security fix CVE-2021-3448
Source: https://thekelleys.org.uk/dnsmasq.git
MR: 110238
Type: Security Fix
Disposition: Backport from https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=74d4fcd756a85bc1823232ea74334f7ccfb9d5d2
ChangeID: 3365bcc47b0467b487f14fc6bfad89bc560cd818
Description:
A flaw was found in dnsmasq in versions before 2.85. When configured to use a specific server for a given network interface, dnsmasq uses a fixed port while forwarding queries. An attacker on the network, able to find the outgoing port used by dnsmasq, only needs to guess the random transmission ID to forge a reply and get it accepted by dnsmasq. This flaw makes a DNS Cache Poisoning attack much easier. The highest threat from this vulnerability is to data integrity.
Signed-off-by: Armin Kuster <akuster@mvista.com>
| -rw-r--r-- | meta-networking/recipes-support/dnsmasq/dnsmasq/CVE-2021-3448.patch | 1040 | ||||
| -rw-r--r-- | meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb | 1 |
2 files changed, 1041 insertions, 0 deletions
diff --git a/meta-networking/recipes-support/dnsmasq/dnsmasq/CVE-2021-3448.patch b/meta-networking/recipes-support/dnsmasq/dnsmasq/CVE-2021-3448.patch new file mode 100644 index 0000000000..360931a83b --- /dev/null +++ b/meta-networking/recipes-support/dnsmasq/dnsmasq/CVE-2021-3448.patch | |||
| @@ -0,0 +1,1040 @@ | |||
| 1 | From 74d4fcd756a85bc1823232ea74334f7ccfb9d5d2 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Simon Kelley <simon@thekelleys.org.uk> | ||
| 3 | Date: Mon, 15 Mar 2021 21:59:51 +0000 | ||
| 4 | Subject: [PATCH] Use random source ports where possible if source | ||
| 5 | addresses/interfaces in use. | ||
| 6 | |||
| 7 | CVE-2021-3448 applies. | ||
| 8 | |||
| 9 | It's possible to specify the source address or interface to be | ||
| 10 | used when contacting upstream nameservers: server=8.8.8.8@1.2.3.4 | ||
| 11 | or server=8.8.8.8@1.2.3.4#66 or server=8.8.8.8@eth0, and all of | ||
| 12 | these have, until now, used a single socket, bound to a fixed | ||
| 13 | port. This was originally done to allow an error (non-existent | ||
| 14 | interface, or non-local address) to be detected at start-up. This | ||
| 15 | means that any upstream servers specified in such a way don't use | ||
| 16 | random source ports, and are more susceptible to cache-poisoning | ||
| 17 | attacks. | ||
| 18 | |||
| 19 | We now use random ports where possible, even when the | ||
| 20 | source is specified, so server=8.8.8.8@1.2.3.4 or | ||
| 21 | server=8.8.8.8@eth0 will use random source | ||
| 22 | ports. server=8.8.8.8@1.2.3.4#66 or any use of --query-port will | ||
| 23 | use the explicitly configured port, and should only be done with | ||
| 24 | understanding of the security implications. | ||
| 25 | Note that this change changes non-existing interface, or non-local | ||
| 26 | source address errors from fatal to run-time. The error will be | ||
| 27 | logged and communiction with the server not possible. | ||
| 28 | |||
| 29 | Upstream-Status: Backport | ||
| 30 | CVE: CVE-2021-3448 | ||
| 31 | Signed-off-by: Armin Kuster <akuster@mvista.com> | ||
| 32 | |||
| 33 | --- | ||
| 34 | CHANGELOG | 22 +++ | ||
| 35 | man/dnsmasq.8 | 4 +- | ||
| 36 | src/dnsmasq.c | 31 ++-- | ||
| 37 | src/dnsmasq.h | 26 ++-- | ||
| 38 | src/forward.c | 392 ++++++++++++++++++++++++++++++-------------------- | ||
| 39 | src/loop.c | 20 +-- | ||
| 40 | src/network.c | 110 +++++--------- | ||
| 41 | src/option.c | 3 +- | ||
| 42 | src/tftp.c | 6 +- | ||
| 43 | src/util.c | 2 +- | ||
| 44 | 10 files changed, 344 insertions(+), 272 deletions(-) | ||
| 45 | |||
| 46 | Index: dnsmasq-2.81/man/dnsmasq.8 | ||
| 47 | =================================================================== | ||
| 48 | --- dnsmasq-2.81.orig/man/dnsmasq.8 | ||
| 49 | +++ dnsmasq-2.81/man/dnsmasq.8 | ||
| 50 | @@ -489,7 +489,7 @@ source address specified but the port ma | ||
| 51 | part of the source address. Forcing queries to an interface is not | ||
| 52 | implemented on all platforms supported by dnsmasq. | ||
| 53 | .TP | ||
| 54 | -.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<source-ip>|<interface>[#<port>]] | ||
| 55 | +.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]] | ||
| 56 | This is functionally the same as | ||
| 57 | .B --server, | ||
| 58 | but provides some syntactic sugar to make specifying address-to-name queries easier. For example | ||
| 59 | Index: dnsmasq-2.81/src/dnsmasq.c | ||
| 60 | =================================================================== | ||
| 61 | --- dnsmasq-2.81.orig/src/dnsmasq.c | ||
| 62 | +++ dnsmasq-2.81/src/dnsmasq.c | ||
| 63 | @@ -1668,6 +1668,7 @@ static int set_dns_listeners(time_t now) | ||
| 64 | { | ||
| 65 | struct serverfd *serverfdp; | ||
| 66 | struct listener *listener; | ||
| 67 | + struct randfd_list *rfl; | ||
| 68 | int wait = 0, i; | ||
| 69 | |||
| 70 | #ifdef HAVE_TFTP | ||
| 71 | @@ -1688,11 +1689,14 @@ static int set_dns_listeners(time_t now) | ||
| 72 | for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) | ||
| 73 | poll_listen(serverfdp->fd, POLLIN); | ||
| 74 | |||
| 75 | - if (daemon->port != 0 && !daemon->osport) | ||
| 76 | - for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 77 | - if (daemon->randomsocks[i].refcount != 0) | ||
| 78 | - poll_listen(daemon->randomsocks[i].fd, POLLIN); | ||
| 79 | - | ||
| 80 | + for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 81 | + if (daemon->randomsocks[i].refcount != 0) | ||
| 82 | + poll_listen(daemon->randomsocks[i].fd, POLLIN); | ||
| 83 | + | ||
| 84 | + /* Check overflow random sockets too. */ | ||
| 85 | + for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) | ||
| 86 | + poll_listen(rfl->rfd->fd, POLLIN); | ||
| 87 | + | ||
| 88 | for (listener = daemon->listeners; listener; listener = listener->next) | ||
| 89 | { | ||
| 90 | /* only listen for queries if we have resources */ | ||
| 91 | @@ -1729,18 +1733,23 @@ static void check_dns_listeners(time_t n | ||
| 92 | { | ||
| 93 | struct serverfd *serverfdp; | ||
| 94 | struct listener *listener; | ||
| 95 | + struct randfd_list *rfl; | ||
| 96 | int i; | ||
| 97 | int pipefd[2]; | ||
| 98 | |||
| 99 | for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next) | ||
| 100 | if (poll_check(serverfdp->fd, POLLIN)) | ||
| 101 | - reply_query(serverfdp->fd, serverfdp->source_addr.sa.sa_family, now); | ||
| 102 | + reply_query(serverfdp->fd, now); | ||
| 103 | |||
| 104 | - if (daemon->port != 0 && !daemon->osport) | ||
| 105 | - for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 106 | - if (daemon->randomsocks[i].refcount != 0 && | ||
| 107 | - poll_check(daemon->randomsocks[i].fd, POLLIN)) | ||
| 108 | - reply_query(daemon->randomsocks[i].fd, daemon->randomsocks[i].family, now); | ||
| 109 | + for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 110 | + if (daemon->randomsocks[i].refcount != 0 && | ||
| 111 | + poll_check(daemon->randomsocks[i].fd, POLLIN)) | ||
| 112 | + reply_query(daemon->randomsocks[i].fd, now); | ||
| 113 | + | ||
| 114 | + /* Check overflow random sockets too. */ | ||
| 115 | + for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) | ||
| 116 | + if (poll_check(rfl->rfd->fd, POLLIN)) | ||
| 117 | + reply_query(rfl->rfd->fd, now); | ||
| 118 | |||
| 119 | /* Races. The child process can die before we read all of the data from the | ||
| 120 | pipe, or vice versa. Therefore send tcp_pids to zero when we wait() the | ||
| 121 | Index: dnsmasq-2.81/src/dnsmasq.h | ||
| 122 | =================================================================== | ||
| 123 | --- dnsmasq-2.81.orig/src/dnsmasq.h | ||
| 124 | +++ dnsmasq-2.81/src/dnsmasq.h | ||
| 125 | @@ -542,13 +542,20 @@ struct serverfd { | ||
| 126 | }; | ||
| 127 | |||
| 128 | struct randfd { | ||
| 129 | + struct server *serv; | ||
| 130 | int fd; | ||
| 131 | - unsigned short refcount, family; | ||
| 132 | + unsigned short refcount; /* refcount == 0xffff means overflow record. */ | ||
| 133 | }; | ||
| 134 | - | ||
| 135 | + | ||
| 136 | +struct randfd_list { | ||
| 137 | + struct randfd *rfd; | ||
| 138 | + struct randfd_list *next; | ||
| 139 | +}; | ||
| 140 | + | ||
| 141 | struct server { | ||
| 142 | union mysockaddr addr, source_addr; | ||
| 143 | char interface[IF_NAMESIZE+1]; | ||
| 144 | + unsigned int ifindex; /* corresponding to interface, above */ | ||
| 145 | struct serverfd *sfd; | ||
| 146 | char *domain; /* set if this server only handles a domain. */ | ||
| 147 | int flags, tcpfd, edns_pktsz; | ||
| 148 | @@ -669,8 +676,7 @@ struct frec { | ||
| 149 | struct frec_src *next; | ||
| 150 | } frec_src; | ||
| 151 | struct server *sentto; /* NULL means free */ | ||
| 152 | - struct randfd *rfd4; | ||
| 153 | - struct randfd *rfd6; | ||
| 154 | + struct randfd_list *rfds; | ||
| 155 | unsigned short new_id; | ||
| 156 | int fd, forwardall, flags; | ||
| 157 | time_t time; | ||
| 158 | @@ -1100,11 +1106,12 @@ extern struct daemon { | ||
| 159 | int forwardcount; | ||
| 160 | struct server *srv_save; /* Used for resend on DoD */ | ||
| 161 | size_t packet_len; /* " " */ | ||
| 162 | - struct randfd *rfd_save; /* " " */ | ||
| 163 | + int fd_save; /* " " */ | ||
| 164 | pid_t tcp_pids[MAX_PROCS]; | ||
| 165 | int tcp_pipes[MAX_PROCS]; | ||
| 166 | int pipe_to_parent; | ||
| 167 | struct randfd randomsocks[RANDOM_SOCKS]; | ||
| 168 | + struct randfd_list *rfl_spare, *rfl_poll; | ||
| 169 | int v6pktinfo; | ||
| 170 | struct addrlist *interface_addrs; /* list of all addresses/prefix lengths associated with all local interfaces */ | ||
| 171 | int log_id, log_display_id; /* ids of transactions for logging */ | ||
| 172 | @@ -1275,7 +1282,7 @@ void safe_strncpy(char *dest, const char | ||
| 173 | void safe_pipe(int *fd, int read_noblock); | ||
| 174 | void *whine_malloc(size_t size); | ||
| 175 | int sa_len(union mysockaddr *addr); | ||
| 176 | -int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2); | ||
| 177 | +int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2); | ||
| 178 | int hostname_isequal(const char *a, const char *b); | ||
| 179 | int hostname_issubdomain(char *a, char *b); | ||
| 180 | time_t dnsmasq_time(void); | ||
| 181 | @@ -1326,7 +1333,7 @@ char *parse_server(char *arg, union myso | ||
| 182 | int option_read_dynfile(char *file, int flags); | ||
| 183 | |||
| 184 | /* forward.c */ | ||
| 185 | -void reply_query(int fd, int family, time_t now); | ||
| 186 | +void reply_query(int fd, time_t now); | ||
| 187 | void receive_query(struct listener *listen, time_t now); | ||
| 188 | unsigned char *tcp_request(int confd, time_t now, | ||
| 189 | union mysockaddr *local_addr, struct in_addr netmask, int auth_dns); | ||
| 190 | @@ -1336,13 +1343,12 @@ int send_from(int fd, int nowild, char * | ||
| 191 | union mysockaddr *to, union all_addr *source, | ||
| 192 | unsigned int iface); | ||
| 193 | void resend_query(void); | ||
| 194 | -struct randfd *allocate_rfd(int family); | ||
| 195 | -void free_rfd(struct randfd *rfd); | ||
| 196 | +int allocate_rfd(struct randfd_list **fdlp, struct server *serv); | ||
| 197 | +void free_rfds(struct randfd_list **fdlp); | ||
| 198 | |||
| 199 | /* network.c */ | ||
| 200 | int indextoname(int fd, int index, char *name); | ||
| 201 | int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp); | ||
| 202 | -int random_sock(int family); | ||
| 203 | void pre_allocate_sfds(void); | ||
| 204 | int reload_servers(char *fname); | ||
| 205 | void mark_servers(int flag); | ||
| 206 | Index: dnsmasq-2.81/src/forward.c | ||
| 207 | =================================================================== | ||
| 208 | --- dnsmasq-2.81.orig/src/forward.c | ||
| 209 | +++ dnsmasq-2.81/src/forward.c | ||
| 210 | @@ -16,7 +16,7 @@ | ||
| 211 | |||
| 212 | #include "dnsmasq.h" | ||
| 213 | |||
| 214 | -static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash); | ||
| 215 | +static struct frec *lookup_frec(unsigned short id, int fd, void *hash); | ||
| 216 | static struct frec *lookup_frec_by_sender(unsigned short id, | ||
| 217 | union mysockaddr *addr, | ||
| 218 | void *hash); | ||
| 219 | @@ -307,26 +307,18 @@ static int forward_query(int udpfd, unio | ||
| 220 | if (find_pseudoheader(header, plen, NULL, &pheader, &is_sign, NULL) && !is_sign) | ||
| 221 | PUTSHORT(SAFE_PKTSZ, pheader); | ||
| 222 | |||
| 223 | - if (forward->sentto->addr.sa.sa_family == AF_INET) | ||
| 224 | - log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); | ||
| 225 | - else | ||
| 226 | - log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); | ||
| 227 | - | ||
| 228 | - | ||
| 229 | - if (forward->sentto->sfd) | ||
| 230 | - fd = forward->sentto->sfd->fd; | ||
| 231 | - else | ||
| 232 | + if ((fd = allocate_rfd(&forward->rfds, forward->sentto)) != -1) | ||
| 233 | { | ||
| 234 | - if (forward->sentto->addr.sa.sa_family == AF_INET6) | ||
| 235 | - fd = forward->rfd6->fd; | ||
| 236 | + if (forward->sentto->addr.sa.sa_family == AF_INET) | ||
| 237 | + log_query(F_NOEXTRA | F_DNSSEC | F_IPV4, "retry", (union all_addr *)&forward->sentto->addr.in.sin_addr, "dnssec"); | ||
| 238 | else | ||
| 239 | - fd = forward->rfd4->fd; | ||
| 240 | + log_query(F_NOEXTRA | F_DNSSEC | F_IPV6, "retry", (union all_addr *)&forward->sentto->addr.in6.sin6_addr, "dnssec"); | ||
| 241 | + | ||
| 242 | + while (retry_send(sendto(fd, (char *)header, plen, 0, | ||
| 243 | + &forward->sentto->addr.sa, | ||
| 244 | + sa_len(&forward->sentto->addr)))); | ||
| 245 | } | ||
| 246 | |||
| 247 | - while (retry_send(sendto(fd, (char *)header, plen, 0, | ||
| 248 | - &forward->sentto->addr.sa, | ||
| 249 | - sa_len(&forward->sentto->addr)))); | ||
| 250 | - | ||
| 251 | return 1; | ||
| 252 | } | ||
| 253 | #endif | ||
| 254 | @@ -501,49 +493,28 @@ static int forward_query(int udpfd, unio | ||
| 255 | |||
| 256 | while (1) | ||
| 257 | { | ||
| 258 | + int fd; | ||
| 259 | + | ||
| 260 | /* only send to servers dealing with our domain. | ||
| 261 | domain may be NULL, in which case server->domain | ||
| 262 | must be NULL also. */ | ||
| 263 | |||
| 264 | if (type == (start->flags & SERV_TYPE) && | ||
| 265 | (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) && | ||
| 266 | - !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP))) | ||
| 267 | + !(start->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && | ||
| 268 | + ((fd = allocate_rfd(&forward->rfds, start)) != -1)) | ||
| 269 | { | ||
| 270 | - int fd; | ||
| 271 | - | ||
| 272 | - /* find server socket to use, may need to get random one. */ | ||
| 273 | - if (start->sfd) | ||
| 274 | - fd = start->sfd->fd; | ||
| 275 | - else | ||
| 276 | - { | ||
| 277 | - if (start->addr.sa.sa_family == AF_INET6) | ||
| 278 | - { | ||
| 279 | - if (!forward->rfd6 && | ||
| 280 | - !(forward->rfd6 = allocate_rfd(AF_INET6))) | ||
| 281 | - break; | ||
| 282 | - daemon->rfd_save = forward->rfd6; | ||
| 283 | - fd = forward->rfd6->fd; | ||
| 284 | - } | ||
| 285 | - else | ||
| 286 | - { | ||
| 287 | - if (!forward->rfd4 && | ||
| 288 | - !(forward->rfd4 = allocate_rfd(AF_INET))) | ||
| 289 | - break; | ||
| 290 | - daemon->rfd_save = forward->rfd4; | ||
| 291 | - fd = forward->rfd4->fd; | ||
| 292 | - } | ||
| 293 | |||
| 294 | #ifdef HAVE_CONNTRACK | ||
| 295 | - /* Copy connection mark of incoming query to outgoing connection. */ | ||
| 296 | - if (option_bool(OPT_CONNTRACK)) | ||
| 297 | - { | ||
| 298 | - unsigned int mark; | ||
| 299 | - if (get_incoming_mark(&forward->source, &forward->dest, 0, &mark)) | ||
| 300 | - setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); | ||
| 301 | - } | ||
| 302 | -#endif | ||
| 303 | + /* Copy connection mark of incoming query to outgoing connection. */ | ||
| 304 | + if (option_bool(OPT_CONNTRACK)) | ||
| 305 | + { | ||
| 306 | + unsigned int mark; | ||
| 307 | + if (get_incoming_mark(&forward->frec_src.source, &forward->frec_src.dest, 0, &mark)) | ||
| 308 | + setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); | ||
| 309 | } | ||
| 310 | - | ||
| 311 | +#endif | ||
| 312 | + | ||
| 313 | #ifdef HAVE_DNSSEC | ||
| 314 | if (option_bool(OPT_DNSSEC_VALID) && (forward->flags & FREC_ADDED_PHEADER)) | ||
| 315 | { | ||
| 316 | @@ -574,6 +545,7 @@ static int forward_query(int udpfd, unio | ||
| 317 | /* Keep info in case we want to re-send this packet */ | ||
| 318 | daemon->srv_save = start; | ||
| 319 | daemon->packet_len = plen; | ||
| 320 | + daemon->fd_save = fd; | ||
| 321 | |||
| 322 | if (!gotname) | ||
| 323 | strcpy(daemon->namebuff, "query"); | ||
| 324 | @@ -590,7 +562,7 @@ static int forward_query(int udpfd, unio | ||
| 325 | break; | ||
| 326 | forward->forwardall++; | ||
| 327 | } | ||
| 328 | - } | ||
| 329 | + } | ||
| 330 | |||
| 331 | if (!(start = start->next)) | ||
| 332 | start = daemon->servers; | ||
| 333 | @@ -805,7 +777,7 @@ static size_t process_reply(struct dns_h | ||
| 334 | } | ||
| 335 | |||
| 336 | /* sets new last_server */ | ||
| 337 | -void reply_query(int fd, int family, time_t now) | ||
| 338 | +void reply_query(int fd, time_t now) | ||
| 339 | { | ||
| 340 | /* packet from peer server, extract data for cache, and send to | ||
| 341 | original requester */ | ||
| 342 | @@ -820,9 +792,9 @@ void reply_query(int fd, int family, tim | ||
| 343 | |||
| 344 | /* packet buffer overwritten */ | ||
| 345 | daemon->srv_save = NULL; | ||
| 346 | - | ||
| 347 | + | ||
| 348 | /* Determine the address of the server replying so that we can mark that as good */ | ||
| 349 | - if ((serveraddr.sa.sa_family = family) == AF_INET6) | ||
| 350 | + if (serveraddr.sa.sa_family == AF_INET6) | ||
| 351 | serveraddr.in6.sin6_flowinfo = 0; | ||
| 352 | |||
| 353 | header = (struct dns_header *)daemon->packet; | ||
| 354 | @@ -845,7 +817,7 @@ void reply_query(int fd, int family, tim | ||
| 355 | |||
| 356 | hash = hash_questions(header, n, daemon->namebuff); | ||
| 357 | |||
| 358 | - if (!(forward = lookup_frec(ntohs(header->id), fd, family, hash))) | ||
| 359 | + if (!(forward = lookup_frec(ntohs(header->id), fd, hash))) | ||
| 360 | return; | ||
| 361 | |||
| 362 | #ifdef HAVE_DUMPFILE | ||
| 363 | @@ -900,25 +872,8 @@ void reply_query(int fd, int family, tim | ||
| 364 | } | ||
| 365 | |||
| 366 | |||
| 367 | - if (start->sfd) | ||
| 368 | - fd = start->sfd->fd; | ||
| 369 | - else | ||
| 370 | - { | ||
| 371 | - if (start->addr.sa.sa_family == AF_INET6) | ||
| 372 | - { | ||
| 373 | - /* may have changed family */ | ||
| 374 | - if (!forward->rfd6) | ||
| 375 | - forward->rfd6 = allocate_rfd(AF_INET6); | ||
| 376 | - fd = forward->rfd6->fd; | ||
| 377 | - } | ||
| 378 | - else | ||
| 379 | - { | ||
| 380 | - /* may have changed family */ | ||
| 381 | - if (!forward->rfd4) | ||
| 382 | - forward->rfd4 = allocate_rfd(AF_INET); | ||
| 383 | - fd = forward->rfd4->fd; | ||
| 384 | - } | ||
| 385 | - } | ||
| 386 | + if ((fd = allocate_rfd(&forward->rfds, start)) == -1) | ||
| 387 | + return; | ||
| 388 | |||
| 389 | #ifdef HAVE_DUMPFILE | ||
| 390 | dump_packet(DUMP_SEC_QUERY, (void *)header, (size_t)plen, NULL, &start->addr); | ||
| 391 | @@ -1126,8 +1081,7 @@ void reply_query(int fd, int family, tim | ||
| 392 | } | ||
| 393 | |||
| 394 | new->sentto = server; | ||
| 395 | - new->rfd4 = NULL; | ||
| 396 | - new->rfd6 = NULL; | ||
| 397 | + new->rfds = NULL; | ||
| 398 | new->frec_src.next = NULL; | ||
| 399 | new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_HAS_EXTRADATA); | ||
| 400 | new->forwardall = 0; | ||
| 401 | @@ -1166,24 +1120,7 @@ void reply_query(int fd, int family, tim | ||
| 402 | /* Don't resend this. */ | ||
| 403 | daemon->srv_save = NULL; | ||
| 404 | |||
| 405 | - if (server->sfd) | ||
| 406 | - fd = server->sfd->fd; | ||
| 407 | - else | ||
| 408 | - { | ||
| 409 | - fd = -1; | ||
| 410 | - if (server->addr.sa.sa_family == AF_INET6) | ||
| 411 | - { | ||
| 412 | - if (new->rfd6 || (new->rfd6 = allocate_rfd(AF_INET6))) | ||
| 413 | - fd = new->rfd6->fd; | ||
| 414 | - } | ||
| 415 | - else | ||
| 416 | - { | ||
| 417 | - if (new->rfd4 || (new->rfd4 = allocate_rfd(AF_INET))) | ||
| 418 | - fd = new->rfd4->fd; | ||
| 419 | - } | ||
| 420 | - } | ||
| 421 | - | ||
| 422 | - if (fd != -1) | ||
| 423 | + if ((fd = allocate_rfd(&new->rfds, server)) != -1) | ||
| 424 | { | ||
| 425 | #ifdef HAVE_CONNTRACK | ||
| 426 | /* Copy connection mark of incoming query to outgoing connection. */ | ||
| 427 | @@ -1344,7 +1281,7 @@ void receive_query(struct listener *list | ||
| 428 | |||
| 429 | /* packet buffer overwritten */ | ||
| 430 | daemon->srv_save = NULL; | ||
| 431 | - | ||
| 432 | + | ||
| 433 | dst_addr_4.s_addr = dst_addr.addr4.s_addr = 0; | ||
| 434 | netmask.s_addr = 0; | ||
| 435 | |||
| 436 | @@ -2207,9 +2144,8 @@ static struct frec *allocate_frec(time_t | ||
| 437 | f->next = daemon->frec_list; | ||
| 438 | f->time = now; | ||
| 439 | f->sentto = NULL; | ||
| 440 | - f->rfd4 = NULL; | ||
| 441 | + f->rfds = NULL; | ||
| 442 | f->flags = 0; | ||
| 443 | - f->rfd6 = NULL; | ||
| 444 | #ifdef HAVE_DNSSEC | ||
| 445 | f->dependent = NULL; | ||
| 446 | f->blocking_query = NULL; | ||
| 447 | @@ -2221,46 +2157,192 @@ static struct frec *allocate_frec(time_t | ||
| 448 | return f; | ||
| 449 | } | ||
| 450 | |||
| 451 | -struct randfd *allocate_rfd(int family) | ||
| 452 | +/* return a UDP socket bound to a random port, have to cope with straying into | ||
| 453 | + occupied port nos and reserved ones. */ | ||
| 454 | +static int random_sock(struct server *s) | ||
| 455 | +{ | ||
| 456 | + int fd; | ||
| 457 | + | ||
| 458 | + if ((fd = socket(s->source_addr.sa.sa_family, SOCK_DGRAM, 0)) != -1) | ||
| 459 | + { | ||
| 460 | + if (local_bind(fd, &s->source_addr, s->interface, s->ifindex, 0)) | ||
| 461 | + return fd; | ||
| 462 | + | ||
| 463 | + if (s->interface[0] == 0) | ||
| 464 | + (void)prettyprint_addr(&s->source_addr, daemon->namebuff); | ||
| 465 | + else | ||
| 466 | + strcpy(daemon->namebuff, s->interface); | ||
| 467 | + | ||
| 468 | + my_syslog(LOG_ERR, _("failed to bind server socket to %s: %s"), | ||
| 469 | + daemon->namebuff, strerror(errno)); | ||
| 470 | + close(fd); | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + return -1; | ||
| 474 | +} | ||
| 475 | + | ||
| 476 | +/* compare source addresses and interface, serv2 can be null. */ | ||
| 477 | +static int server_isequal(const struct server *serv1, | ||
| 478 | + const struct server *serv2) | ||
| 479 | +{ | ||
| 480 | + return (serv2 && | ||
| 481 | + serv2->ifindex == serv1->ifindex && | ||
| 482 | + sockaddr_isequal(&serv2->source_addr, &serv1->source_addr) && | ||
| 483 | + strncmp(serv2->interface, serv1->interface, IF_NAMESIZE) == 0); | ||
| 484 | +} | ||
| 485 | + | ||
| 486 | +/* fdlp points to chain of randomfds already in use by transaction. | ||
| 487 | + If there's already a suitable one, return it, else allocate a | ||
| 488 | + new one and add it to the list. | ||
| 489 | + | ||
| 490 | + Not leaking any resources in the face of allocation failures | ||
| 491 | + is rather convoluted here. | ||
| 492 | + | ||
| 493 | + Note that rfd->serv may be NULL, when a server goes away. | ||
| 494 | +*/ | ||
| 495 | +int allocate_rfd(struct randfd_list **fdlp, struct server *serv) | ||
| 496 | { | ||
| 497 | static int finger = 0; | ||
| 498 | - int i; | ||
| 499 | + int i, j = 0; | ||
| 500 | + struct randfd_list *rfl; | ||
| 501 | + struct randfd *rfd = NULL; | ||
| 502 | + int fd = 0; | ||
| 503 | + | ||
| 504 | + /* If server has a pre-allocated fd, use that. */ | ||
| 505 | + if (serv->sfd) | ||
| 506 | + return serv->sfd->fd; | ||
| 507 | + | ||
| 508 | + /* existing suitable random port socket linked to this transaction? */ | ||
| 509 | + for (rfl = *fdlp; rfl; rfl = rfl->next) | ||
| 510 | + if (server_isequal(serv, rfl->rfd->serv)) | ||
| 511 | + return rfl->rfd->fd; | ||
| 512 | + | ||
| 513 | + /* No. need new link. */ | ||
| 514 | + if ((rfl = daemon->rfl_spare)) | ||
| 515 | + daemon->rfl_spare = rfl->next; | ||
| 516 | + else if (!(rfl = whine_malloc(sizeof(struct randfd_list)))) | ||
| 517 | + return -1; | ||
| 518 | |||
| 519 | /* limit the number of sockets we have open to avoid starvation of | ||
| 520 | (eg) TFTP. Once we have a reasonable number, randomness should be OK */ | ||
| 521 | - | ||
| 522 | for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 523 | if (daemon->randomsocks[i].refcount == 0) | ||
| 524 | { | ||
| 525 | - if ((daemon->randomsocks[i].fd = random_sock(family)) == -1) | ||
| 526 | - break; | ||
| 527 | - | ||
| 528 | - daemon->randomsocks[i].refcount = 1; | ||
| 529 | - daemon->randomsocks[i].family = family; | ||
| 530 | - return &daemon->randomsocks[i]; | ||
| 531 | + if ((fd = random_sock(serv)) != -1) | ||
| 532 | + { | ||
| 533 | + rfd = &daemon->randomsocks[i]; | ||
| 534 | + rfd->serv = serv; | ||
| 535 | + rfd->fd = fd; | ||
| 536 | + rfd->refcount = 1; | ||
| 537 | + } | ||
| 538 | + break; | ||
| 539 | } | ||
| 540 | |||
| 541 | /* No free ones or cannot get new socket, grab an existing one */ | ||
| 542 | - for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 543 | + if (!rfd) | ||
| 544 | + for (j = 0; j < RANDOM_SOCKS; j++) | ||
| 545 | + { | ||
| 546 | + i = (j + finger) % RANDOM_SOCKS; | ||
| 547 | + if (daemon->randomsocks[i].refcount != 0 && | ||
| 548 | + server_isequal(serv, daemon->randomsocks[i].serv) && | ||
| 549 | + daemon->randomsocks[i].refcount != 0xfffe) | ||
| 550 | + { | ||
| 551 | + finger = i + 1; | ||
| 552 | + rfd = &daemon->randomsocks[i]; | ||
| 553 | + rfd->refcount++; | ||
| 554 | + break; | ||
| 555 | + } | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + if (j == RANDOM_SOCKS) | ||
| 559 | { | ||
| 560 | - int j = (i+finger) % RANDOM_SOCKS; | ||
| 561 | - if (daemon->randomsocks[j].refcount != 0 && | ||
| 562 | - daemon->randomsocks[j].family == family && | ||
| 563 | - daemon->randomsocks[j].refcount != 0xffff) | ||
| 564 | + struct randfd_list *rfl_poll; | ||
| 565 | + | ||
| 566 | + /* there are no free slots, and non with the same parameters we can piggy-back on. | ||
| 567 | + We're going to have to allocate a new temporary record, distinguished by | ||
| 568 | + refcount == 0xffff. This will exist in the frec randfd list, never be shared, | ||
| 569 | + and be freed when no longer in use. It will also be held on | ||
| 570 | + the daemon->rfl_poll list so the poll system can find it. */ | ||
| 571 | + | ||
| 572 | + if ((rfl_poll = daemon->rfl_spare)) | ||
| 573 | + daemon->rfl_spare = rfl_poll->next; | ||
| 574 | + else | ||
| 575 | + rfl_poll = whine_malloc(sizeof(struct randfd_list)); | ||
| 576 | + | ||
| 577 | + if (!rfl_poll || | ||
| 578 | + !(rfd = whine_malloc(sizeof(struct randfd))) || | ||
| 579 | + (fd = random_sock(serv)) == -1) | ||
| 580 | { | ||
| 581 | - finger = j; | ||
| 582 | - daemon->randomsocks[j].refcount++; | ||
| 583 | - return &daemon->randomsocks[j]; | ||
| 584 | + | ||
| 585 | + /* Don't leak anything we may already have */ | ||
| 586 | + rfl->next = daemon->rfl_spare; | ||
| 587 | + daemon->rfl_spare = rfl; | ||
| 588 | + | ||
| 589 | + if (rfl_poll) | ||
| 590 | + { | ||
| 591 | + rfl_poll->next = daemon->rfl_spare; | ||
| 592 | + daemon->rfl_spare = rfl_poll; | ||
| 593 | + } | ||
| 594 | + | ||
| 595 | + if (rfd) | ||
| 596 | + free(rfd); | ||
| 597 | + | ||
| 598 | + return -1; /* doom */ | ||
| 599 | } | ||
| 600 | + | ||
| 601 | + /* Note rfd->serv not set here, since it's not reused */ | ||
| 602 | + rfd->fd = fd; | ||
| 603 | + rfd->refcount = 0xffff; /* marker for temp record */ | ||
| 604 | + | ||
| 605 | + rfl_poll->rfd = rfd; | ||
| 606 | + rfl_poll->next = daemon->rfl_poll; | ||
| 607 | + daemon->rfl_poll = rfl_poll; | ||
| 608 | } | ||
| 609 | |||
| 610 | - return NULL; /* doom */ | ||
| 611 | + rfl->rfd = rfd; | ||
| 612 | + rfl->next = *fdlp; | ||
| 613 | + *fdlp = rfl; | ||
| 614 | + | ||
| 615 | + return rfl->rfd->fd; | ||
| 616 | } | ||
| 617 | |||
| 618 | -void free_rfd(struct randfd *rfd) | ||
| 619 | +void free_rfds(struct randfd_list **fdlp) | ||
| 620 | { | ||
| 621 | - if (rfd && --(rfd->refcount) == 0) | ||
| 622 | - close(rfd->fd); | ||
| 623 | + struct randfd_list *tmp, *rfl, *poll, *next, **up; | ||
| 624 | + | ||
| 625 | + for (rfl = *fdlp; rfl; rfl = tmp) | ||
| 626 | + { | ||
| 627 | + if (rfl->rfd->refcount == 0xffff || --(rfl->rfd->refcount) == 0) | ||
| 628 | + close(rfl->rfd->fd); | ||
| 629 | + | ||
| 630 | + /* temporary overflow record */ | ||
| 631 | + if (rfl->rfd->refcount == 0xffff) | ||
| 632 | + { | ||
| 633 | + free(rfl->rfd); | ||
| 634 | + | ||
| 635 | + /* go through the link of all these by steam to delete. | ||
| 636 | + This list is expected to be almost always empty. */ | ||
| 637 | + for (poll = daemon->rfl_poll, up = &daemon->rfl_poll; poll; poll = next) | ||
| 638 | + { | ||
| 639 | + next = poll->next; | ||
| 640 | + | ||
| 641 | + if (poll->rfd == rfl->rfd) | ||
| 642 | + { | ||
| 643 | + *up = poll->next; | ||
| 644 | + poll->next = daemon->rfl_spare; | ||
| 645 | + daemon->rfl_spare = poll; | ||
| 646 | + } | ||
| 647 | + else | ||
| 648 | + up = &poll->next; | ||
| 649 | + } | ||
| 650 | + } | ||
| 651 | + | ||
| 652 | + tmp = rfl->next; | ||
| 653 | + rfl->next = daemon->rfl_spare; | ||
| 654 | + daemon->rfl_spare = rfl; | ||
| 655 | + } | ||
| 656 | + | ||
| 657 | + *fdlp = NULL; | ||
| 658 | } | ||
| 659 | |||
| 660 | static void free_frec(struct frec *f) | ||
| 661 | @@ -2276,12 +2358,9 @@ static void free_frec(struct frec *f) | ||
| 662 | } | ||
| 663 | |||
| 664 | f->frec_src.next = NULL; | ||
| 665 | - free_rfd(f->rfd4); | ||
| 666 | - f->rfd4 = NULL; | ||
| 667 | + free_rfds(&f->rfds); | ||
| 668 | f->sentto = NULL; | ||
| 669 | f->flags = 0; | ||
| 670 | - free_rfd(f->rfd6); | ||
| 671 | - f->rfd6 = NULL; | ||
| 672 | |||
| 673 | #ifdef HAVE_DNSSEC | ||
| 674 | if (f->stash) | ||
| 675 | @@ -2389,26 +2468,39 @@ struct frec *get_new_frec(time_t now, in | ||
| 676 | } | ||
| 677 | |||
| 678 | /* crc is all-ones if not known. */ | ||
| 679 | -static struct frec *lookup_frec(unsigned short id, int fd, int family, void *hash) | ||
| 680 | +static struct frec *lookup_frec(unsigned short id, int fd, void *hash) | ||
| 681 | { | ||
| 682 | struct frec *f; | ||
| 683 | + struct server *s; | ||
| 684 | + int type; | ||
| 685 | + struct randfd_list *fdl; | ||
| 686 | |||
| 687 | for(f = daemon->frec_list; f; f = f->next) | ||
| 688 | if (f->sentto && f->new_id == id && | ||
| 689 | (memcmp(hash, f->hash, HASH_SIZE) == 0)) | ||
| 690 | { | ||
| 691 | /* sent from random port */ | ||
| 692 | - if (family == AF_INET && f->rfd4 && f->rfd4->fd == fd) | ||
| 693 | + for (fdl = f->rfds; fdl; fdl = fdl->next) | ||
| 694 | + if (fdl->rfd->fd == fd) | ||
| 695 | return f; | ||
| 696 | + } | ||
| 697 | |||
| 698 | - if (family == AF_INET6 && f->rfd6 && f->rfd6->fd == fd) | ||
| 699 | - return f; | ||
| 700 | + /* Sent to upstream from socket associated with a server. | ||
| 701 | + Note we have to iterate over all the possible servers, since they may | ||
| 702 | + have different bound sockets. */ | ||
| 703 | + type = f->sentto->flags & SERV_TYPE; | ||
| 704 | + s = f->sentto; | ||
| 705 | + do { | ||
| 706 | + if ((type == (s->flags & SERV_TYPE)) && | ||
| 707 | + (type != SERV_HAS_DOMAIN || | ||
| 708 | + (s->domain && hostname_isequal(f->sentto->domain, s->domain))) && | ||
| 709 | + !(s->flags & (SERV_LITERAL_ADDRESS | SERV_LOOP)) && | ||
| 710 | + s->sfd && s->sfd->fd == fd) | ||
| 711 | + return f; | ||
| 712 | + | ||
| 713 | + s = s->next ? s->next : daemon->servers; | ||
| 714 | + } while (s != f->sentto); | ||
| 715 | |||
| 716 | - /* sent to upstream from bound socket. */ | ||
| 717 | - if (f->sentto->sfd && f->sentto->sfd->fd == fd) | ||
| 718 | - return f; | ||
| 719 | - } | ||
| 720 | - | ||
| 721 | return NULL; | ||
| 722 | } | ||
| 723 | |||
| 724 | @@ -2454,30 +2546,26 @@ static struct frec *lookup_frec_by_query | ||
| 725 | void resend_query() | ||
| 726 | { | ||
| 727 | if (daemon->srv_save) | ||
| 728 | - { | ||
| 729 | - int fd; | ||
| 730 | - | ||
| 731 | - if (daemon->srv_save->sfd) | ||
| 732 | - fd = daemon->srv_save->sfd->fd; | ||
| 733 | - else if (daemon->rfd_save && daemon->rfd_save->refcount != 0) | ||
| 734 | - fd = daemon->rfd_save->fd; | ||
| 735 | - else | ||
| 736 | - return; | ||
| 737 | - | ||
| 738 | - while(retry_send(sendto(fd, daemon->packet, daemon->packet_len, 0, | ||
| 739 | - &daemon->srv_save->addr.sa, | ||
| 740 | - sa_len(&daemon->srv_save->addr)))); | ||
| 741 | - } | ||
| 742 | + while(retry_send(sendto(daemon->fd_save, daemon->packet, daemon->packet_len, 0, | ||
| 743 | + &daemon->srv_save->addr.sa, | ||
| 744 | + sa_len(&daemon->srv_save->addr)))); | ||
| 745 | } | ||
| 746 | |||
| 747 | /* A server record is going away, remove references to it */ | ||
| 748 | void server_gone(struct server *server) | ||
| 749 | { | ||
| 750 | struct frec *f; | ||
| 751 | + int i; | ||
| 752 | |||
| 753 | for (f = daemon->frec_list; f; f = f->next) | ||
| 754 | if (f->sentto && f->sentto == server) | ||
| 755 | free_frec(f); | ||
| 756 | + | ||
| 757 | + /* If any random socket refers to this server, NULL the reference. | ||
| 758 | + No more references to the socket will be created in the future. */ | ||
| 759 | + for (i = 0; i < RANDOM_SOCKS; i++) | ||
| 760 | + if (daemon->randomsocks[i].refcount != 0 && daemon->randomsocks[i].serv == server) | ||
| 761 | + daemon->randomsocks[i].serv = NULL; | ||
| 762 | |||
| 763 | if (daemon->last_server == server) | ||
| 764 | daemon->last_server = NULL; | ||
| 765 | Index: dnsmasq-2.81/src/loop.c | ||
| 766 | =================================================================== | ||
| 767 | --- dnsmasq-2.81.orig/src/loop.c | ||
| 768 | +++ dnsmasq-2.81/src/loop.c | ||
| 769 | @@ -22,6 +22,7 @@ static ssize_t loop_make_probe(u32 uid); | ||
| 770 | void loop_send_probes() | ||
| 771 | { | ||
| 772 | struct server *serv; | ||
| 773 | + struct randfd_list *rfds = NULL; | ||
| 774 | |||
| 775 | if (!option_bool(OPT_LOOP_DETECT)) | ||
| 776 | return; | ||
| 777 | @@ -34,22 +35,15 @@ void loop_send_probes() | ||
| 778 | { | ||
| 779 | ssize_t len = loop_make_probe(serv->uid); | ||
| 780 | int fd; | ||
| 781 | - struct randfd *rfd = NULL; | ||
| 782 | |||
| 783 | - if (serv->sfd) | ||
| 784 | - fd = serv->sfd->fd; | ||
| 785 | - else | ||
| 786 | - { | ||
| 787 | - if (!(rfd = allocate_rfd(serv->addr.sa.sa_family))) | ||
| 788 | - continue; | ||
| 789 | - fd = rfd->fd; | ||
| 790 | - } | ||
| 791 | + if ((fd = allocate_rfd(&rfds, serv)) == -1) | ||
| 792 | + continue; | ||
| 793 | |||
| 794 | while (retry_send(sendto(fd, daemon->packet, len, 0, | ||
| 795 | &serv->addr.sa, sa_len(&serv->addr)))); | ||
| 796 | - | ||
| 797 | - free_rfd(rfd); | ||
| 798 | } | ||
| 799 | + | ||
| 800 | + free_rfds(&rfds); | ||
| 801 | } | ||
| 802 | |||
| 803 | static ssize_t loop_make_probe(u32 uid) | ||
| 804 | Index: dnsmasq-2.81/src/network.c | ||
| 805 | =================================================================== | ||
| 806 | --- dnsmasq-2.81.orig/src/network.c | ||
| 807 | +++ dnsmasq-2.81/src/network.c | ||
| 808 | @@ -545,6 +545,7 @@ int enumerate_interfaces(int reset) | ||
| 809 | #ifdef HAVE_AUTH | ||
| 810 | struct auth_zone *zone; | ||
| 811 | #endif | ||
| 812 | + struct server *serv; | ||
| 813 | |||
| 814 | /* Do this max once per select cycle - also inhibits netlink socket use | ||
| 815 | in TCP child processes. */ | ||
| 816 | @@ -562,7 +563,21 @@ int enumerate_interfaces(int reset) | ||
| 817 | |||
| 818 | if ((param.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) | ||
| 819 | return 0; | ||
| 820 | - | ||
| 821 | + | ||
| 822 | + /* iface indexes can change when interfaces are created/destroyed. | ||
| 823 | + We use them in the main forwarding control path, when the path | ||
| 824 | + to a server is specified by an interface, so cache them. | ||
| 825 | + Update the cache here. */ | ||
| 826 | + for (serv = daemon->servers; serv; serv = serv->next) | ||
| 827 | + if (strlen(serv->interface) != 0) | ||
| 828 | + { | ||
| 829 | + struct ifreq ifr; | ||
| 830 | + | ||
| 831 | + safe_strncpy(ifr.ifr_name, serv->interface, IF_NAMESIZE); | ||
| 832 | + if (ioctl(param.fd, SIOCGIFINDEX, &ifr) != -1) | ||
| 833 | + serv->ifindex = ifr.ifr_ifindex; | ||
| 834 | + } | ||
| 835 | + | ||
| 836 | /* Mark interfaces for garbage collection */ | ||
| 837 | for (iface = daemon->interfaces; iface; iface = iface->next) | ||
| 838 | iface->found = 0; | ||
| 839 | @@ -658,7 +673,7 @@ int enumerate_interfaces(int reset) | ||
| 840 | |||
| 841 | errno = errsave; | ||
| 842 | spare = param.spare; | ||
| 843 | - | ||
| 844 | + | ||
| 845 | return ret; | ||
| 846 | } | ||
| 847 | |||
| 848 | @@ -798,10 +813,10 @@ int tcp_interface(int fd, int af) | ||
| 849 | /* use mshdr so that the CMSDG_* macros are available */ | ||
| 850 | msg.msg_control = daemon->packet; | ||
| 851 | msg.msg_controllen = len = daemon->packet_buff_sz; | ||
| 852 | - | ||
| 853 | + | ||
| 854 | /* we overwrote the buffer... */ | ||
| 855 | daemon->srv_save = NULL; | ||
| 856 | - | ||
| 857 | + | ||
| 858 | if (af == AF_INET) | ||
| 859 | { | ||
| 860 | if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) != -1 && | ||
| 861 | @@ -1102,59 +1117,6 @@ void join_multicast(int dienow) | ||
| 862 | } | ||
| 863 | #endif | ||
| 864 | |||
| 865 | -/* return a UDP socket bound to a random port, have to cope with straying into | ||
| 866 | - occupied port nos and reserved ones. */ | ||
| 867 | -int random_sock(int family) | ||
| 868 | -{ | ||
| 869 | - int fd; | ||
| 870 | - | ||
| 871 | - if ((fd = socket(family, SOCK_DGRAM, 0)) != -1) | ||
| 872 | - { | ||
| 873 | - union mysockaddr addr; | ||
| 874 | - unsigned int ports_avail = ((unsigned short)daemon->max_port - (unsigned short)daemon->min_port) + 1; | ||
| 875 | - int tries = ports_avail < 30 ? 3 * ports_avail : 100; | ||
| 876 | - | ||
| 877 | - memset(&addr, 0, sizeof(addr)); | ||
| 878 | - addr.sa.sa_family = family; | ||
| 879 | - | ||
| 880 | - /* don't loop forever if all ports in use. */ | ||
| 881 | - | ||
| 882 | - if (fix_fd(fd)) | ||
| 883 | - while(tries--) | ||
| 884 | - { | ||
| 885 | - unsigned short port = htons(daemon->min_port + (rand16() % ((unsigned short)ports_avail))); | ||
| 886 | - | ||
| 887 | - if (family == AF_INET) | ||
| 888 | - { | ||
| 889 | - addr.in.sin_addr.s_addr = INADDR_ANY; | ||
| 890 | - addr.in.sin_port = port; | ||
| 891 | -#ifdef HAVE_SOCKADDR_SA_LEN | ||
| 892 | - addr.in.sin_len = sizeof(struct sockaddr_in); | ||
| 893 | -#endif | ||
| 894 | - } | ||
| 895 | - else | ||
| 896 | - { | ||
| 897 | - addr.in6.sin6_addr = in6addr_any; | ||
| 898 | - addr.in6.sin6_port = port; | ||
| 899 | -#ifdef HAVE_SOCKADDR_SA_LEN | ||
| 900 | - addr.in6.sin6_len = sizeof(struct sockaddr_in6); | ||
| 901 | -#endif | ||
| 902 | - } | ||
| 903 | - | ||
| 904 | - if (bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == 0) | ||
| 905 | - return fd; | ||
| 906 | - | ||
| 907 | - if (errno != EADDRINUSE && errno != EACCES) | ||
| 908 | - break; | ||
| 909 | - } | ||
| 910 | - | ||
| 911 | - close(fd); | ||
| 912 | - } | ||
| 913 | - | ||
| 914 | - return -1; | ||
| 915 | -} | ||
| 916 | - | ||
| 917 | - | ||
| 918 | int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp) | ||
| 919 | { | ||
| 920 | union mysockaddr addr_copy = *addr; | ||
| 921 | @@ -1199,38 +1161,33 @@ int local_bind(int fd, union mysockaddr | ||
| 922 | return 1; | ||
| 923 | } | ||
| 924 | |||
| 925 | -static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname) | ||
| 926 | +static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsigned int ifindex) | ||
| 927 | { | ||
| 928 | struct serverfd *sfd; | ||
| 929 | - unsigned int ifindex = 0; | ||
| 930 | int errsave; | ||
| 931 | int opt = 1; | ||
| 932 | |||
| 933 | /* when using random ports, servers which would otherwise use | ||
| 934 | - the INADDR_ANY/port0 socket have sfd set to NULL */ | ||
| 935 | - if (!daemon->osport && intname[0] == 0) | ||
| 936 | + the INADDR_ANY/port0 socket have sfd set to NULL, this is | ||
| 937 | + anything without an explictly set source port. */ | ||
| 938 | + if (!daemon->osport) | ||
| 939 | { | ||
| 940 | errno = 0; | ||
| 941 | |||
| 942 | if (addr->sa.sa_family == AF_INET && | ||
| 943 | - addr->in.sin_addr.s_addr == INADDR_ANY && | ||
| 944 | addr->in.sin_port == htons(0)) | ||
| 945 | return NULL; | ||
| 946 | |||
| 947 | if (addr->sa.sa_family == AF_INET6 && | ||
| 948 | - memcmp(&addr->in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 && | ||
| 949 | addr->in6.sin6_port == htons(0)) | ||
| 950 | return NULL; | ||
| 951 | } | ||
| 952 | |||
| 953 | - if (intname && strlen(intname) != 0) | ||
| 954 | - ifindex = if_nametoindex(intname); /* index == 0 when not binding to an interface */ | ||
| 955 | - | ||
| 956 | /* may have a suitable one already */ | ||
| 957 | for (sfd = daemon->sfds; sfd; sfd = sfd->next ) | ||
| 958 | - if (sockaddr_isequal(&sfd->source_addr, addr) && | ||
| 959 | - strcmp(intname, sfd->interface) == 0 && | ||
| 960 | - ifindex == sfd->ifindex) | ||
| 961 | + if (ifindex == sfd->ifindex && | ||
| 962 | + sockaddr_isequal(&sfd->source_addr, addr) && | ||
| 963 | + strcmp(intname, sfd->interface) == 0) | ||
| 964 | return sfd; | ||
| 965 | |||
| 966 | /* need to make a new one. */ | ||
| 967 | @@ -1281,7 +1238,7 @@ void pre_allocate_sfds(void) | ||
| 968 | #ifdef HAVE_SOCKADDR_SA_LEN | ||
| 969 | addr.in.sin_len = sizeof(struct sockaddr_in); | ||
| 970 | #endif | ||
| 971 | - if ((sfd = allocate_sfd(&addr, ""))) | ||
| 972 | + if ((sfd = allocate_sfd(&addr, "", 0))) | ||
| 973 | sfd->preallocated = 1; | ||
| 974 | |||
| 975 | memset(&addr, 0, sizeof(addr)); | ||
| 976 | @@ -1291,13 +1248,13 @@ void pre_allocate_sfds(void) | ||
| 977 | #ifdef HAVE_SOCKADDR_SA_LEN | ||
| 978 | addr.in6.sin6_len = sizeof(struct sockaddr_in6); | ||
| 979 | #endif | ||
| 980 | - if ((sfd = allocate_sfd(&addr, ""))) | ||
| 981 | + if ((sfd = allocate_sfd(&addr, "", 0))) | ||
| 982 | sfd->preallocated = 1; | ||
| 983 | } | ||
| 984 | |||
| 985 | for (srv = daemon->servers; srv; srv = srv->next) | ||
| 986 | if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) && | ||
| 987 | - !allocate_sfd(&srv->source_addr, srv->interface) && | ||
| 988 | + !allocate_sfd(&srv->source_addr, srv->interface, srv->ifindex) && | ||
| 989 | errno != 0 && | ||
| 990 | option_bool(OPT_NOWILD)) | ||
| 991 | { | ||
| 992 | @@ -1506,7 +1463,7 @@ void check_servers(void) | ||
| 993 | |||
| 994 | /* Do we need a socket set? */ | ||
| 995 | if (!serv->sfd && | ||
| 996 | - !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) && | ||
| 997 | + !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && | ||
| 998 | errno != 0) | ||
| 999 | { | ||
| 1000 | my_syslog(LOG_WARNING, | ||
| 1001 | Index: dnsmasq-2.81/src/option.c | ||
| 1002 | =================================================================== | ||
| 1003 | --- dnsmasq-2.81.orig/src/option.c | ||
| 1004 | +++ dnsmasq-2.81/src/option.c | ||
| 1005 | @@ -810,7 +810,8 @@ char *parse_server(char *arg, union myso | ||
| 1006 | if (interface_opt) | ||
| 1007 | { | ||
| 1008 | #if defined(SO_BINDTODEVICE) | ||
| 1009 | - safe_strncpy(interface, interface_opt, IF_NAMESIZE); | ||
| 1010 | + safe_strncpy(interface, source, IF_NAMESIZE); | ||
| 1011 | + source = interface_opt; | ||
| 1012 | #else | ||
| 1013 | return _("interface binding not supported"); | ||
| 1014 | #endif | ||
| 1015 | Index: dnsmasq-2.81/src/tftp.c | ||
| 1016 | =================================================================== | ||
| 1017 | --- dnsmasq-2.81.orig/src/tftp.c | ||
| 1018 | +++ dnsmasq-2.81/src/tftp.c | ||
| 1019 | @@ -601,7 +601,7 @@ void check_tftp_listeners(time_t now) | ||
| 1020 | |||
| 1021 | /* we overwrote the buffer... */ | ||
| 1022 | daemon->srv_save = NULL; | ||
| 1023 | - | ||
| 1024 | + | ||
| 1025 | if ((len = get_block(daemon->packet, transfer)) == -1) | ||
| 1026 | { | ||
| 1027 | len = tftp_err_oops(daemon->packet, transfer->file->filename); | ||
| 1028 | Index: dnsmasq-2.81/src/util.c | ||
| 1029 | =================================================================== | ||
| 1030 | --- dnsmasq-2.81.orig/src/util.c | ||
| 1031 | +++ dnsmasq-2.81/src/util.c | ||
| 1032 | @@ -316,7 +316,7 @@ void *whine_malloc(size_t size) | ||
| 1033 | return ret; | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | -int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2) | ||
| 1037 | +int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2) | ||
| 1038 | { | ||
| 1039 | if (s1->sa.sa_family == s2->sa.sa_family) | ||
| 1040 | { | ||
diff --git a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb index a1dc0f3a0a..2fb389915b 100644 --- a/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb +++ b/meta-networking/recipes-support/dnsmasq/dnsmasq_2.81.bb | |||
| @@ -10,4 +10,5 @@ SRC_URI += "\ | |||
| 10 | file://CVE-2020-25685-2.patch \ | 10 | file://CVE-2020-25685-2.patch \ |
| 11 | file://CVE-2020-25686-1.patch \ | 11 | file://CVE-2020-25686-1.patch \ |
| 12 | file://CVE-2020-25686-2.patch \ | 12 | file://CVE-2020-25686-2.patch \ |
| 13 | file://CVE-2021-3448.patch \ | ||
| 13 | " | 14 | " |
