static void test_recv_order(const struct test_params p, int fd[], int mod) { char recv_buf[8], send_buf[8]; struct msghdr msg; struct iovec recv_io = { recv_buf, 8 }; struct epoll_event ev; int epfd, conn, i, sport, expected; uint32_t data, ndata; epfd = epoll_create(1); if (epfd < 0) error(1, errno, "failed to create epoll"); for (i = 0; i < p.recv_socks; ++i) { ev.events = EPOLLIN; ev.data.fd = fd[i]; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], &ev)) error(1, errno, "failed to register sock %d epoll", i); } memset(&msg, 0, sizeof(msg)); msg.msg_iov = &recv_io; msg.msg_iovlen = 1; for (data = 0; data < p.recv_socks * 2; ++data) { sport = p.send_port_min + data; ndata = htonl(data); memcpy(send_buf, &ndata, sizeof(ndata)); send_from(p, sport, send_buf, sizeof(ndata)); i = epoll_wait(epfd, &ev, 1, -1); if (i < 0) error(1, errno, "epoll wait failed"); if (p.protocol == SOCK_STREAM) { conn = accept(ev.data.fd, NULL, NULL); if (conn < 0) error(1, errno, "error accepting"); i = recvmsg(conn, &msg, 0); close(conn); } else { i = recvmsg(ev.data.fd, &msg, 0); } if (i < 0) error(1, errno, "recvmsg error"); if (i != sizeof(ndata)) error(1, 0, "expected size %zd got %d", sizeof(ndata), i); for (i = 0; i < p.recv_socks; ++i) if (ev.data.fd == fd[i]) break; memcpy(&ndata, recv_buf, sizeof(ndata)); fprintf(stderr, "Socket %d: %d\n", i, ntohl(ndata)); expected = (sport % mod); if (i != expected) error(1, 0, "expected socket %d", expected); } }
void receive_query(struct listener *listen, time_t now) { HEADER *header = (HEADER *)daemon->packet; union mysockaddr source_addr; unsigned short type; struct all_addr dst_addr; struct in_addr netmask, dst_addr_4; size_t m; ssize_t n; int if_index = 0; struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmptr; union { struct cmsghdr align; /* this ensures alignment */ #ifdef HAVE_IPV6 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #endif #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(IP_RECVDSTADDR) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; /* packet buffer overwritten */ daemon->srv_save = NULL; if (listen->family == AF_INET && (daemon->options & OPT_NOWILD)) { dst_addr_4 = listen->iface->addr.in.sin_addr; netmask = listen->iface->netmask; } else { dst_addr_4.s_addr = 0; netmask.s_addr = 0; } iov[0].iov_base = daemon->packet; iov[0].iov_len = daemon->edns_pktsz; msg.msg_control = control_u.control; msg.msg_controllen = sizeof(control_u); msg.msg_flags = 0; msg.msg_name = &source_addr; msg.msg_namelen = sizeof(source_addr); msg.msg_iov = iov; msg.msg_iovlen = 1; if ((n = recvmsg(listen->fd, &msg, 0)) == -1) return; if (n < (int)sizeof(HEADER) || (msg.msg_flags & MSG_TRUNC) || header->qr) return; source_addr.sa.sa_family = listen->family; #ifdef HAVE_IPV6 if (listen->family == AF_INET6) source_addr.in6.sin6_flowinfo = 0; #endif if (!(daemon->options & OPT_NOWILD)) { struct ifreq ifr; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; #if defined(HAVE_LINUX_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) { dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst; if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex; } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (listen->family == AF_INET) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr)); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index; } #endif #ifdef HAVE_IPV6 if (listen->family == AF_INET6) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == IPV6_PKTINFO) { dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr; if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex; } } #endif /* enforce available interface configuration */ if (!indextoname(listen->fd, if_index, ifr.ifr_name) || !iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index)) return; if (listen->family == AF_INET && (daemon->options & OPT_LOCALISE) && ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1) return; netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; } if (extract_request(header, (size_t)n, daemon->namebuff, &type)) { char types[20]; querystr(types, type); if (listen->family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&source_addr.in.sin_addr, types); #ifdef HAVE_IPV6 else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&source_addr.in6.sin6_addr, types); #endif } m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, dst_addr_4, netmask, now); if (m >= 1) { send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index); daemon->local_answer++; } else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, header, (size_t)n, now, NULL)) daemon->queries_forwarded++; else daemon->local_answer++; }
/* 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 */ } }
static int forward_query(int udpfd, union mysockaddr *udpaddr, struct all_addr *dst_addr, unsigned int dst_iface, HEADER *header, size_t plen, time_t now, struct frec *forward) { char *domain = NULL; int type = 0; struct all_addr *addrp = NULL; unsigned int crc = questions_crc(header, plen, daemon->namebuff); unsigned short flags = 0; unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL); struct server *start = NULL; /* may be no servers available. */ if (!daemon->servers) forward = NULL; else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) { /* retry on existing query, send to all available servers */ domain = forward->sentto->domain; forward->sentto->failed_queries++; if (!(daemon->options & OPT_ORDER)) { forward->forwardall = 1; daemon->last_server = NULL; } type = forward->sentto->flags & SERV_TYPE; if (!(start = forward->sentto->next)) start = daemon->servers; /* at end of list, recycle */ header->id = htons(forward->new_id); } else { if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain); if (!flags && !(forward = get_new_frec(now, NULL))) /* table full - server failure. */ flags = F_NEG; if (forward) { /* force unchanging id for signed packets */ int is_sign; find_pseudoheader(header, plen, NULL, NULL, &is_sign); forward->source = *udpaddr; forward->dest = *dst_addr; forward->iface = dst_iface; forward->orig_id = ntohs(header->id); forward->new_id = get_id(is_sign, forward->orig_id, crc); forward->fd = udpfd; forward->crc = crc; forward->forwardall = 0; header->id = htons(forward->new_id); /* In strict_order mode, or when using domain specific servers always try servers in the order specified in resolv.conf, otherwise, use the one last known to work. */ if (type != 0 || (daemon->options & OPT_ORDER)) start = daemon->servers; else if (!(start = daemon->last_server) || daemon->forwardcount++ > FORWARD_TEST || difftime(now, daemon->forwardtime) > FORWARD_TIME) { start = daemon->servers; forward->forwardall = 1; daemon->forwardcount = 0; daemon->forwardtime = now; } } } /* check for send errors here (no route to host) if we fail to send to all nameservers, send back an error packet straight away (helps modem users when offline) */ if (!flags && forward) { struct server *firstsentto = start; int forwarded = 0; while (1) { /* 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)) { 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 ANDROID // Mark the socket so it goes out on the correct network. Note // that we never clear the mark, only re-set it the next time we // allocate a new random fd. This is because we buffer DNS // queries (in daemon->srv_save, daemon->packet_len) and socket // file descriptors (in daemon->rfd_save) with the expectation of // being able to use them again. // // Server fds are marked separately in allocate_sfd. setsockopt(fd, SOL_SOCKET, SO_MARK, &start->mark, sizeof(start->mark)); #endif } if (sendto(fd, (char *)header, plen, 0, &start->addr.sa, sa_len(&start->addr)) == -1) { if (retry_send()) continue; } else { /* Keep info in case we want to re-send this packet */ daemon->srv_save = start; daemon->packet_len = plen; if (!gotname) strcpy(daemon->namebuff, "query"); if (start->addr.sa.sa_family == AF_INET) log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in.sin_addr, NULL); #ifdef HAVE_IPV6 else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in6.sin6_addr, NULL); #endif start->queries++; forwarded = 1; forward->sentto = start; if (!forward->forwardall) break; forward->forwardall++; } } if (!(start = start->next)) start = daemon->servers; if (start == firstsentto) break; } if (forwarded) return 1; /* could not send on, prepare to return */ header->id = htons(forward->orig_id); free_frec(forward); /* cancel */ } /* could not send on, return empty answer or address if known for whole domain */ if (udpfd != -1) { plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface); } return 0; }
time_t periodic_slaac(time_t now, struct dhcp_lease *leases) { struct dhcp_context *context; struct dhcp_lease *lease; struct slaac_address *slaac; time_t next_event = 0; for (context = daemon->ra_contexts; context; context = context->next) if ((context->flags & CONTEXT_RA_NAME)) break; /* nothing configured */ if (!context) return 0; while (ping_id == 0) ping_id = rand16(); if (map_rebuild) { map_rebuild = 0; build_subnet_map(); } for (lease = leases; lease; lease = lease->next) for (slaac = lease->slaac_address; slaac; slaac = slaac->next) { /* confirmed or given up? */ if (slaac->backoff == 0 || slaac->ping_time == 0) continue; if (difftime(slaac->ping_time, now) <= 0.0) { struct ping_packet *ping; struct sockaddr_in6 addr; int err; save_counter(0); ping = expand(sizeof(struct ping_packet)); ping->type = ICMP6_ECHO_REQUEST; ping->code = 0; ping->identifier = ping_id; ping->sequence_no = slaac->backoff; memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SOCKADDR_SA_LEN addr.sin6_len = sizeof(struct sockaddr_in6); #endif addr.sin6_family = AF_INET6; addr.sin6_port = htons(IPPROTO_ICMPV6); addr.sin6_addr = slaac->addr; if (send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0), (union mysockaddr *)&addr, (struct all_addr *)&slaac->local, lease->last_interface, &err)) { slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */ if (slaac->backoff > 4) slaac->ping_time += rand16()/4000; /* 0 - 15 */ if (slaac->backoff < 12) slaac->backoff++; } else if (err == EHOSTUNREACH) slaac->ping_time = 0; /* Give up */ } if (slaac->ping_time != 0 && (next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0)) next_event = slaac->ping_time; } return next_event; }
void receive_query(struct listener *listen, time_t now) { struct dns_header *header = (struct dns_header *)daemon->packet; union mysockaddr source_addr; unsigned short type; struct all_addr dst_addr; struct in_addr netmask, dst_addr_4; size_t m; ssize_t n; int if_index = 0; struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmptr; union { struct cmsghdr align; /* this ensures alignment */ #ifdef HAVE_IPV6 char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; #endif #if defined(HAVE_LINUX_NETWORK) char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; #elif defined(IP_RECVDSTADDR) && defined(HAVE_SOLARIS_NETWORK) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(unsigned int))]; #elif defined(IP_RECVDSTADDR) char control[CMSG_SPACE(sizeof(struct in_addr)) + CMSG_SPACE(sizeof(struct sockaddr_dl))]; #endif } control_u; /* packet buffer overwritten */ daemon->srv_save = NULL; if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD)) { dst_addr_4 = listen->iface->addr.in.sin_addr; netmask = listen->iface->netmask; } else { dst_addr_4.s_addr = 0; netmask.s_addr = 0; } iov[0].iov_base = daemon->packet; iov[0].iov_len = daemon->edns_pktsz; msg.msg_control = control_u.control; msg.msg_controllen = sizeof(control_u); msg.msg_flags = 0; msg.msg_name = &source_addr; msg.msg_namelen = sizeof(source_addr); msg.msg_iov = iov; msg.msg_iovlen = 1; if ((n = recvmsg(listen->fd, &msg, 0)) == -1) return; if (n < (int)sizeof(struct dns_header) || (msg.msg_flags & MSG_TRUNC) || (header->hb3 & HB3_QR)) return; source_addr.sa.sa_family = listen->family; #ifdef HAVE_IPV6 if (listen->family == AF_INET6) source_addr.in6.sin6_flowinfo = 0; #endif if (!option_bool(OPT_NOWILD)) { struct ifreq ifr; if (msg.msg_controllen < sizeof(struct cmsghdr)) return; #if defined(HAVE_LINUX_NETWORK) if (listen->family == AF_INET) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) { union { unsigned char *c; struct in_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); dst_addr_4 = dst_addr.addr.addr4 = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) if (listen->family == AF_INET) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { union { unsigned char *c; unsigned int *i; struct in_addr *a; #ifndef HAVE_SOLARIS_NETWORK struct sockaddr_dl *s; #endif } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) dst_addr_4 = dst_addr.addr.addr4 = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) #ifdef HAVE_SOLARIS_NETWORK if_index = *(p.i); #else if_index = p.s->sdl_index; #endif } } #endif #ifdef HAVE_IPV6 if (listen->family == AF_INET6) { for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) { union { unsigned char *c; struct in6_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); dst_addr.addr.addr6 = p.p->ipi6_addr; if_index = p.p->ipi6_ifindex; } } #endif /* enforce available interface configuration */ if (!indextoname(listen->fd, if_index, ifr.ifr_name) || !iface_check(listen->family, &dst_addr, ifr.ifr_name)) return; if (listen->family == AF_INET && option_bool(OPT_LOCALISE)) { struct irec *iface; /* get the netmask of the interface whch has the address we were sent to. This is no neccessarily the interface we arrived on. */ for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->addr.sa.sa_family == AF_INET && iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr) break; /* interface may be new */ if (!iface) enumerate_interfaces(); for (iface = daemon->interfaces; iface; iface = iface->next) if (iface->addr.sa.sa_family == AF_INET && iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr) break; /* If we failed, abandon localisation */ if (iface) netmask = iface->netmask; else dst_addr_4.s_addr = 0; } } if (extract_request(header, (size_t)n, daemon->namebuff, &type)) { char types[20]; querystr(types, type); if (listen->family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&source_addr.in.sin_addr, types); #ifdef HAVE_IPV6 else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&source_addr.in6.sin6_addr, types); #endif } m = answer_request (header, ((char *) header) + PACKETSZ, (size_t)n, dst_addr_4, netmask, now); if (m >= 1) { send_from(listen->fd, option_bool(OPT_NOWILD), (char *)header, m, &source_addr, &dst_addr, if_index); daemon->local_answer++; } else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index, header, (size_t)n, now, NULL)) daemon->queries_forwarded++; else daemon->local_answer++; }
static int forward_query(int udpfd, union mysockaddr *udpaddr, struct all_addr *dst_addr, unsigned int dst_iface, struct dns_header *header, size_t plen, time_t now, struct frec *forward) { char *domain = NULL; int type = 0, norebind = 0; struct all_addr *addrp = NULL; unsigned int crc = questions_crc(header, plen, daemon->namebuff); unsigned int flags = 0; unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL); struct server *start = NULL; /* RFC 4035: sect 4.6 para 2 */ header->hb4 &= ~HB4_AD; /* may be no servers available. */ if (!daemon->servers) forward = NULL; else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) { /* retry on existing query, send to all available servers */ domain = forward->sentto->domain; forward->sentto->failed_queries++; if (!option_bool(OPT_ORDER)) { forward->forwardall = 1; daemon->last_server = NULL; } type = forward->sentto->flags & SERV_TYPE; if (!(start = forward->sentto->next)) start = daemon->servers; /* at end of list, recycle */ header->id = htons(forward->new_id); } else { if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind); if (!flags && !(forward = get_new_frec(now, NULL))) /* table full - server failure. */ flags = F_NEG; if (forward) { forward->source = *udpaddr; forward->dest = *dst_addr; forward->iface = dst_iface; forward->orig_id = ntohs(header->id); forward->new_id = get_id(crc); forward->fd = udpfd; forward->crc = crc; forward->forwardall = 0; if (norebind) forward->flags |= FREC_NOREBIND; if (header->hb4 & HB4_CD) forward->flags |= FREC_CHECKING_DISABLED; header->id = htons(forward->new_id); /* In strict_order mode, always try servers in the order specified in resolv.conf, if a domain is given always try all the available servers, otherwise, use the one last known to work. */ if (type == 0) { if (option_bool(OPT_ORDER)) start = daemon->servers; else if (!(start = daemon->last_server) || daemon->forwardcount++ > FORWARD_TEST || difftime(now, daemon->forwardtime) > FORWARD_TIME) { start = daemon->servers; forward->forwardall = 1; daemon->forwardcount = 0; daemon->forwardtime = now; } } else { start = daemon->servers; if (!option_bool(OPT_ORDER)) forward->forwardall = 1; } } } /* check for send errors here (no route to host) if we fail to send to all nameservers, send back an error packet straight away (helps modem users when offline) */ if (!flags && forward) { struct server *firstsentto = start; int forwarded = 0; if (udpaddr && option_bool(OPT_ADD_MAC)) plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr); while (1) { /* 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)) { 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(udpaddr, dst_addr, 0, &mark)) setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int)); } #endif } if (sendto(fd, (char *)header, plen, 0, &start->addr.sa, sa_len(&start->addr)) == -1) { if (retry_send()) continue; } else { /* Keep info in case we want to re-send this packet */ daemon->srv_save = start; daemon->packet_len = plen; if (!gotname) strcpy(daemon->namebuff, "query"); if (start->addr.sa.sa_family == AF_INET) log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in.sin_addr, NULL); #ifdef HAVE_IPV6 else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in6.sin6_addr, NULL); #endif start->queries++; forwarded = 1; forward->sentto = start; if (!forward->forwardall) break; forward->forwardall++; } } if (!(start = start->next)) start = daemon->servers; if (start == firstsentto) break; } if (forwarded) return 1; /* could not send on, prepare to return */ header->id = htons(forward->orig_id); free_frec(forward); /* cancel */ } /* could not send on, return empty answer or address if known for whole domain */ if (udpfd != -1) { plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); send_from(udpfd, option_bool(OPT_NOWILD), (char *)header, plen, udpaddr, dst_addr, dst_iface); } return 0; }
/* returns new last_server */ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *udpaddr, struct all_addr *dst_addr, unsigned int dst_iface, HEADER *header, size_t plen, time_t now, struct frec *forward) { char *domain = NULL; int type = 0; struct all_addr *addrp = NULL; unsigned int crc = questions_crc(header, plen, daemon->namebuff); unsigned short flags = 0; unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL); struct server *start = NULL; /* may be no servers available. */ if (!daemon->servers) forward = NULL; else if (forward || (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc))) { /* retry on existing query, send to all available servers */ domain = forward->sentto->domain; if (!(daemon->options & OPT_ORDER)) { forward->forwardall = 1; daemon->last_server = NULL; } type = forward->sentto->flags & SERV_TYPE; if (!(start = forward->sentto->next)) start = daemon->servers; /* at end of list, recycle */ header->id = htons(forward->new_id); } else { if (gotname) flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain); if (!flags && !(forward = get_new_frec(daemon, now, NULL))) /* table full - server failure. */ flags = F_NEG; if (forward) { forward->source = *udpaddr; forward->dest = *dst_addr; forward->iface = dst_iface; forward->new_id = get_id(); forward->fd = udpfd; forward->orig_id = ntohs(header->id); forward->crc = crc; forward->forwardall = 0; header->id = htons(forward->new_id); /* In strict_order mode, or when using domain specific servers always try servers in the order specified in resolv.conf, otherwise, use the one last known to work. */ if (type != 0 || (daemon->options & OPT_ORDER)) start = daemon->servers; else if (!(start = daemon->last_server)) { start = daemon->servers; forward->forwardall = 1; } } } /* check for send errors here (no route to host) if we fail to send to all nameservers, send back an error packet straight away (helps modem users when offline) */ if (!flags && forward) { struct server *firstsentto = start; int forwarded = 0; while (1) { /* 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)) { if (sendto(start->sfd->fd, (char *)header, plen, 0, &start->addr.sa, sa_len(&start->addr)) == -1) { if (retry_send()) continue; } else { /* Keep info in case we want to re-send this packet */ daemon->srv_save = start; daemon->packet_len = plen; if (!gotname) strcpy(daemon->namebuff, "query"); if (start->addr.sa.sa_family == AF_INET) log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in.sin_addr, 0, NULL, 0); #ifdef HAVE_IPV6 else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&start->addr.in6.sin6_addr, 0, NULL, 0); #endif forwarded = 1; forward->sentto = start; if (!forward->forwardall) break; forward->forwardall++; } } if (!(start = start->next)) start = daemon->servers; if (start == firstsentto) break; } if (forwarded) return; /* could not send on, prepare to return */ header->id = htons(forward->orig_id); forward->new_id = 0; /* cancel */ } /* could not send on, return empty answer or address if known for whole domain */ if (udpfd != -1) { plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl); send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface); } return; }