static void dbus_read_servers(DBusMessage *message) { struct server *serv, *tmp, **up; DBusMessageIter iter; union mysockaddr addr, source_addr; char *domain; dbus_message_iter_init(message, &iter); for (serv = daemon->servers; serv; serv = serv->next) if (serv->flags & SERV_FROM_DBUS) serv->flags |= SERV_MARK; while (1) { int skip = 0; if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) { u32 a; dbus_message_iter_get_basic(&iter, &a); dbus_message_iter_next (&iter); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in.sin_len = addr.in.sin_len = sizeof(struct sockaddr_in); #endif addr.in.sin_addr.s_addr = ntohl(a); source_addr.in.sin_family = addr.in.sin_family = AF_INET; addr.in.sin_port = htons(NAMESERVER_PORT); source_addr.in.sin_addr.s_addr = INADDR_ANY; source_addr.in.sin_port = htons(daemon->query_port); } else if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_BYTE) { unsigned char p[sizeof(struct in6_addr)]; unsigned int i; skip = 1; for(i = 0; i < sizeof(struct in6_addr); i++) { dbus_message_iter_get_basic(&iter, &p[i]); dbus_message_iter_next (&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE) break; } #ifndef HAVE_IPV6 my_syslog(LOG_WARNING, _("attempt to set an IPv6 server address via DBus - no IPv6 support")); #else if (i == sizeof(struct in6_addr)-1) { memcpy(&addr.in6.sin6_addr, p, sizeof(struct in6_addr)); #ifdef HAVE_SOCKADDR_SA_LEN source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(struct sockaddr_in6); #endif source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6; addr.in6.sin6_port = htons(NAMESERVER_PORT); source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0; source_addr.in6.sin6_scope_id = addr.in6.sin6_scope_id = 0; source_addr.in6.sin6_addr = in6addr_any; source_addr.in6.sin6_port = htons(daemon->query_port); skip = 0; } #endif } else break; do { if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) { dbus_message_iter_get_basic(&iter, &domain); dbus_message_iter_next (&iter); } else domain = NULL; if (!skip) { for (serv = daemon->servers; serv; serv = serv->next) if ((serv->flags & SERV_FROM_DBUS) && (serv->flags & SERV_MARK)) { if (!(serv->flags & SERV_HAS_DOMAIN) && !domain) { serv->flags &= ~SERV_MARK; break; } if ((serv->flags & SERV_HAS_DOMAIN) && domain && hostname_isequal(domain, serv->domain)) { serv->flags &= ~SERV_MARK; break; } } if (!serv && (serv = whine_malloc(sizeof (struct server)))) { memset(serv, 0, sizeof(struct server)); if (domain) serv->domain = whine_malloc(strlen(domain)+1); if (domain && !serv->domain) { free(serv); serv = NULL; } else { serv->next = daemon->servers; daemon->servers = serv; serv->flags = SERV_FROM_DBUS; if (domain) { strcpy(serv->domain, domain); serv->flags |= SERV_HAS_DOMAIN; } } } if (serv) { if (source_addr.in.sin_family == AF_INET && addr.in.sin_addr.s_addr == 0 && serv->domain) serv->flags |= SERV_NO_ADDR; else { serv->flags &= ~SERV_NO_ADDR; serv->addr = addr; serv->source_addr = source_addr; } } } } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) { tmp = serv->next; if (serv->flags & SERV_MARK) { server_gone(serv); *up = serv->next; free(serv); } else up = &serv->next; } }
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; }
/* The daemon forks before calling this: it should deal with one connection, blocking as neccessary, and then return. Note, need to be a bit careful about resources for debug mode, when the fork is suppressed: that's done by the caller. */ unsigned char *tcp_request(int confd, time_t now, struct in_addr local_addr, struct in_addr netmask) { int size = 0; size_t m; unsigned short qtype, gotname; unsigned char c1, c2; /* Max TCP packet + slop */ unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ); HEADER *header; struct server *last_server; while (1) { if (!packet || !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || !(size = c1 << 8 | c2) || !read_write(confd, packet, size, 1)) return packet; if (size < (int)sizeof(HEADER)) continue; header = (HEADER *)packet; if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) { union mysockaddr peer_addr; socklen_t peer_len = sizeof(union mysockaddr); if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1) { char types[20]; querystr(types, qtype); if (peer_addr.sa.sa_family == AF_INET) log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&peer_addr.in.sin_addr, types); #ifdef HAVE_IPV6 else log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&peer_addr.in6.sin6_addr, types); #endif } } /* m > 0 if answered from cache */ m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, local_addr, netmask, now); /* Do this by steam now we're not in the select() loop */ check_log_writer(NULL); if (m == 0) { unsigned short flags = 0; struct all_addr *addrp = NULL; int type = 0; char *domain = NULL; if (gotname) flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain); if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server) last_server = daemon->servers; else last_server = daemon->last_server; if (!flags && last_server) { struct server *firstsendto = NULL; unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff); /* Loop round available servers until we succeed in connecting to one. Note that this code subtley ensures that consecutive queries on this connection which can go to the same server, do so. */ while (1) { if (!firstsendto) firstsendto = last_server; else { if (!(last_server = last_server->next)) last_server = daemon->servers; if (last_server == firstsendto) break; } /* server for wrong domain */ if (type != (last_server->flags & SERV_TYPE) || (type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain))) continue; if ((last_server->tcpfd == -1) && (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 && (!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, last_server->mark, 1) || connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1)) { close(last_server->tcpfd); last_server->tcpfd = -1; } if (last_server->tcpfd == -1) continue; c1 = size >> 8; c2 = size; if (!read_write(last_server->tcpfd, &c1, 1, 0) || !read_write(last_server->tcpfd, &c2, 1, 0) || !read_write(last_server->tcpfd, packet, size, 0) || !read_write(last_server->tcpfd, &c1, 1, 1) || !read_write(last_server->tcpfd, &c2, 1, 1)) { close(last_server->tcpfd); last_server->tcpfd = -1; continue; } m = (c1 << 8) | c2; if (!read_write(last_server->tcpfd, packet, m, 1)) return packet; if (!gotname) strcpy(daemon->namebuff, "query"); if (last_server->addr.sa.sa_family == AF_INET) log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, (struct all_addr *)&last_server->addr.in.sin_addr, NULL); #ifdef HAVE_IPV6 else log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL); #endif /* There's no point in updating the cache, since this process will exit and lose the information after a few queries. We make this call for the alias and bogus-nxdomain side-effects. */ /* If the crc of the question section doesn't match the crc we sent, then someone might be attempting to insert bogus values into the cache by sending replies containing questions and bogus answers. */ if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff)) m = process_reply(header, now, last_server, (unsigned int)m); break; } } /* In case of local answer or no connections made. */ if (m == 0) m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl); } check_log_writer(NULL); c1 = m>>8; c2 = m; if (!read_write(confd, &c1, 1, 0) || !read_write(confd, &c2, 1, 0) || !read_write(confd, packet, m, 0)) return packet; }
int is_name_synthetic(int flags, char *name, struct all_addr *addr) { char *p; struct cond_domain *c = NULL; int prot = AF_INET; #ifdef HAVE_IPV6 if (flags & F_IPV6) prot = AF_INET6; #endif for (c = daemon->synth_domains; c; c = c->next) { int found = 0; char *tail, *pref; for (tail = name, pref = c->prefix; *tail != 0 && pref && *pref != 0; tail++, pref++) { unsigned int c1 = (unsigned char) *pref; unsigned int c2 = (unsigned char) *tail; if (c1 >= 'A' && c1 <= 'Z') c1 += 'a' - 'A'; if (c2 >= 'A' && c2 <= 'Z') c2 += 'a' - 'A'; if (c1 != c2) break; } if (pref && *pref != 0) continue; /* prefix match fail */ /* NB, must not alter name if we return zero */ for (p = tail; *p; p++) { char c = *p; if ((c >='0' && c <= '9') || c == '-') continue; #ifdef HAVE_IPV6 if (prot == AF_INET6 && ((c >='A' && c <= 'F') || (c >='a' && c <= 'f'))) continue; #endif break; } if (*p != '.') continue; *p = 0; #ifdef HAVE_IPV6 if (prot == AF_INET6 && strstr(tail, "--ffff-") == tail) { /* special hack for v4-mapped. */ memcpy(tail, "::ffff:", 7); for (p = tail + 7; *p; p++) if (*p == '-') *p = '.'; } else #endif { /* swap . or : for - */ for (p = tail; *p; p++) if (*p == '-') { if (prot == AF_INET) *p = '.'; #ifdef HAVE_IPV6 else *p = ':'; #endif } } if (hostname_isequal(c->domain, p+1) && inet_pton(prot, tail, addr)) { if (prot == AF_INET) { if (!c->is6 && ntohl(addr->addr.addr4.s_addr) >= ntohl(c->start.s_addr) && ntohl(addr->addr.addr4.s_addr) <= ntohl(c->end.s_addr)) found = 1; } #ifdef HAVE_IPV6 else { u64 addrpart = addr6part(&addr->addr.addr6); if (c->is6 && is_same_net6(&addr->addr.addr6, &c->start6, 64) && addrpart >= addr6part(&c->start6) && addrpart <= addr6part(&c->end6)) found = 1; } #endif } /* restore name */ for (p = tail; *p; p++) if (*p == '.' || *p == ':') *p = '-'; *p = '.'; if (found) return 1; } return 0; }
static unsigned short search_servers(time_t now, struct all_addr **addrpp, unsigned short qtype, char *qdomain, int *type, char **domain) { /* If the query ends in the domain in one of our servers, set domain to point to that name. We find the largest match to allow both domain.org and sub.domain.org to exist. */ unsigned int namelen = strlen(qdomain); unsigned int matchlen = 0; struct server *serv; unsigned short flags = 0; for (serv = daemon->servers; serv; serv=serv->next) /* domain matches take priority over NODOTS matches */ if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0) { unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_FOR_NODOTS; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if (sflag & qtype) { flags = sflag; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; } } else if (serv->flags & SERV_HAS_DOMAIN) { unsigned int domainlen = strlen(serv->domain); char *matchstart = qdomain + namelen - domainlen; if (namelen >= domainlen && hostname_isequal(matchstart, serv->domain) && domainlen >= matchlen && (domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' )) { unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_HAS_DOMAIN; *domain = serv->domain; matchlen = domainlen; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if (sflag & qtype) { flags = sflag; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; } } } if (flags == 0 && !(qtype & F_BIGNAME) && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) /* don't forward simple names, make exception for NS queries and empty name. */ flags = F_NXDOMAIN; if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now)) flags = F_NOERR; if (flags) { int logflags = 0; if (flags == F_NXDOMAIN || flags == F_NOERR) logflags = F_NEG | qtype; log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL); } return flags; }
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; }
static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind) { /* If the query ends in the domain in one of our servers, set domain to point to that name. We find the largest match to allow both domain.org and sub.domain.org to exist. */ unsigned int namelen = strlen(qdomain); unsigned int matchlen = 0; struct server *serv; unsigned int flags = 0; for (serv = daemon->servers; serv; serv=serv->next) /* domain matches take priority over NODOTS matches */ if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0) { unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_FOR_NODOTS; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if (sflag & qtype) { flags = sflag; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; } } else if (serv->flags & SERV_HAS_DOMAIN) { unsigned int domainlen = strlen(serv->domain); char *matchstart = qdomain + namelen - domainlen; if (namelen >= domainlen && hostname_isequal(matchstart, serv->domain) && (domainlen == 0 || namelen == domainlen || *(matchstart-1) == '.' )) { if (serv->flags & SERV_NO_REBIND) *norebind = 1; else { unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; /* implement priority rules for --address and --server for same domain. --address wins if the address is for the correct AF --server wins otherwise. */ if (domainlen != 0 && domainlen == matchlen) { if ((serv->flags & SERV_LITERAL_ADDRESS)) { if (!(sflag & qtype) && flags == 0) continue; } else { if (flags & (F_IPV4 | F_IPV6)) continue; } } if (domainlen >= matchlen) { *type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND); *domain = serv->domain; matchlen = domainlen; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if (sflag & qtype) { flags = sflag; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags || (flags & F_NXDOMAIN)) flags = F_NOERR; } else flags = 0; } } } } if (flags == 0 && !(qtype & F_QUERY) && option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) /* don't forward A or AAAA queries for simple names, except the empty name */ flags = F_NOERR; if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now)) flags = F_NOERR; if (flags) { int logflags = 0; if (flags == F_NXDOMAIN || flags == F_NOERR) logflags = F_NEG | qtype; log_query(logflags | flags | F_CONFIG | F_FORWARD, qdomain, *addrpp, NULL); } else if ((*type) & SERV_USE_RESOLV) { *type = 0; /* use normal servers for this domain */ *domain = NULL; } return flags; }
/* 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; }
static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp, unsigned short qtype, char *qdomain, int *type, char **domain) { /* If the query ends in the domain in one of our servers, set domain to point to that name. We find the largest match to allow both domain.org and sub.domain.org to exist. */ unsigned int namelen = strlen(qdomain); unsigned int matchlen = 0; struct server *serv; unsigned short flags = 0; for (serv = daemon->servers; serv; serv=serv->next) /* domain matches take priority over NODOTS matches */ if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0) { unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_FOR_NODOTS; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if (sflag & qtype) { flags = sflag; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags) flags = F_NOERR; } } else if (serv->flags & SERV_HAS_DOMAIN) { unsigned int domainlen = strlen(serv->domain); char *matchstart = qdomain + namelen - domainlen; if (namelen >= domainlen && hostname_isequal(matchstart, serv->domain) && domainlen >= matchlen && (domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' )) { unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_HAS_DOMAIN; *domain = serv->domain; matchlen = domainlen; if (serv->flags & SERV_NO_ADDR) flags = F_NXDOMAIN; else if (serv->flags & SERV_LITERAL_ADDRESS) { if ((sflag | F_QUERY ) & qtype) { flags = qtype; if (serv->addr.sa.sa_family == AF_INET) *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; #ifdef HAVE_IPV6 else *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif } else if (!flags) flags = F_NOERR; } } } if (flags & ~(F_NOERR | F_NXDOMAIN)) /* flags set here means a literal found */ { if (flags & F_QUERY) log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0, NULL, 0); else log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0); } else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0) flags = F_NXDOMAIN; if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon)) flags = F_NOERR; if (flags == F_NXDOMAIN || flags == F_NOERR) log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0, NULL, 0); return flags; }
static void add_update_server(union mysockaddr *addr, union mysockaddr *source_addr, const char *interface, const char *domain) { struct server *serv; /* See if there is a suitable candidate, and unmark */ for (serv = daemon->servers; serv; serv = serv->next) if ((serv->flags & SERV_FROM_DBUS) && (serv->flags & SERV_MARK)) { if (domain) { if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain)) continue; } else { if (serv->flags & SERV_HAS_DOMAIN) continue; } serv->flags &= ~SERV_MARK; break; } if (!serv && (serv = whine_malloc(sizeof (struct server)))) { /* Not found, create a new one. */ memset(serv, 0, sizeof(struct server)); if (domain && !(serv->domain = whine_malloc(strlen(domain)+1))) { free(serv); serv = NULL; } else { serv->next = daemon->servers; daemon->servers = serv; serv->flags = SERV_FROM_DBUS; if (domain) { strcpy(serv->domain, domain); serv->flags |= SERV_HAS_DOMAIN; } } } if (serv) { if (interface) strcpy(serv->interface, interface); else serv->interface[0] = 0; if (source_addr->in.sin_family == AF_INET && addr->in.sin_addr.s_addr == 0 && serv->domain) serv->flags |= SERV_NO_ADDR; else { serv->flags &= ~SERV_NO_ADDR; serv->addr = *addr; serv->source_addr = *source_addr; } } }