static struct irec *add_iface(struct irec *list, unsigned int flags, char *name, union mysockaddr *addr, struct iname *names, struct iname *addrs) { struct irec *iface; /* we may need to check the whitelist */ if (names) { struct iname *tmp; for(tmp = names; tmp; tmp = tmp->next) if (strcmp(tmp->name, name) == 0) { tmp->found = 1; break; } if (!(flags & IFF_LOOPBACK) && !tmp) /* not on whitelist and not loopback */ return list; } if (addrs) { struct iname *tmp; for(tmp = addrs; tmp; tmp = tmp->next) if (sockaddr_isequal(&tmp->addr, addr)) { tmp->found = 1; break; } if (!tmp) /* not on whitelist */ return list; } /* check whether the interface IP has been added already it is possible to have multiple interfaces with the same address. */ for (iface = list; iface; iface = iface->next) if (sockaddr_isequal(&iface->addr, addr)) break; if (iface) return list; /* If OK, add it to the head of the list */ iface = safe_malloc(sizeof(struct irec)); iface->addr = *addr; iface->next = list; return iface; }
static struct frec *lookup_frec_by_sender(unsigned short id, union mysockaddr *addr) { int i; for(i=0; i<FTABSIZ; i++) { struct frec *f = &ftab[i]; if (f->new_id && f->orig_id == id && sockaddr_isequal(&f->source, addr)) return f; } return NULL; }
/* sets new last_server */ void reply_query(int fd, int family, time_t now) { /* packet from peer server, extract data for cache, and send to original requester */ HEADER *header; union mysockaddr serveraddr; struct frec *forward; socklen_t addrlen = sizeof(serveraddr); 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 = 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 (!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) { 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; } } } 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 ((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 */ } }