diff --git a/CHANGELOG b/CHANGELOG index 9e41672..e4304ff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1190,3 +1190,27 @@ version 2.14 Added "keep-in-foreground" option. Thanks to Sean MacLennan for the patch. + +version 2.15 + Fixed NXDOMAIN/NODATA confusion for locally known + names. We now return a NODATA reponse for names which are + locally known. Now a query for (eg AAAA or MX) for a name + with an IPv4 address in /etc/hosts which fails upstream + will generate a NODATA response. Note that the query + is still tried upstream, but a NXDOMAIN reply gets + converted to NODATA. Thanks to Eric de Thouars, Eric + Spakman and Mike Mestnik for bug reports/testing. + + Allow multiple dhcp-ranges within the same network. The + original intention was that there would be a dhcp-range + option for each network served, but there's no real reason + not to allow discontinuous ranges within a network so this + release adds support for that. + + Check for dhcp-ranges which are inconsistent with their + netmask, and generate errors or warnings. + + Improve error messages when there are problems with + configuration. + + diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 7d7a728..3594e3f 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.14 +Version: 2.15 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index 696f8b5..b379e0a 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.14 +Version: 2.15 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers diff --git a/src/config.h b/src/config.h index 01eaac9..756093c 100644 --- a/src/config.h +++ b/src/config.h @@ -12,7 +12,7 @@ /* Author's email: simon@thekelleys.org.uk */ -#define VERSION "2.14" +#define VERSION "2.15" #define FTABSIZ 150 /* max number of outstanding requests */ #define MAX_PROCS 20 /* max no children for TCP requests */ diff --git a/src/dhcp.c b/src/dhcp.c index 0cbcb6e..05b7e26 100644 --- a/src/dhcp.c +++ b/src/dhcp.c @@ -209,9 +209,19 @@ void dhcp_packet(struct daemon *daemon, time_t now) iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; if (iface_netmask.s_addr && - is_same_net(iface_addr, context->start, iface_netmask) && - is_same_net(iface_addr, context->end, iface_netmask)) - context->netmask = iface_netmask; + (is_same_net(iface_addr, context->start, iface_netmask) || + is_same_net(iface_addr, context->end, iface_netmask))) + { + context->netmask = iface_netmask; + if (!(is_same_net(iface_addr, context->start, iface_netmask) && + is_same_net(iface_addr, context->end, iface_netmask))) + { + strcpy(daemon->dhcp_buff, inet_ntoa(context->start)); + strcpy(daemon->dhcp_buff2, inet_ntoa(context->end)); + syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s", + daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask)); + } + } } /* Determine "default" default routes. These are to this server or the relay agent. @@ -226,7 +236,8 @@ void dhcp_packet(struct daemon *daemon, time_t now) { if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1) iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; - if (iface_broadcast.s_addr) + if (iface_broadcast.s_addr && + is_same_net(iface_broadcast, context->start, context->netmask)) context->broadcast = iface_broadcast; else context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; @@ -361,26 +372,23 @@ void dhcp_packet(struct daemon *daemon, time_t now) int address_available(struct dhcp_context *context, struct in_addr taddr) { - /* Check is an address is OK for this network, ie - within allowable range and not in an existing lease */ + /* Check is an address is OK for this network, check all + possible ranges. */ - unsigned int addr, start, end; + unsigned int start, end, addr = ntohl(taddr.s_addr); - /* static leases only. */ - if (context->static_only) - return 0; + for (; context; context = context->current) + { + start = ntohl(context->start.s_addr); + end = ntohl(context->end.s_addr); - addr = ntohl(taddr.s_addr); - start = ntohl(context->start.s_addr); - end = ntohl(context->end.s_addr); + if (!context->static_only && + addr >= start && + addr <= end) + return 1; + } - if (addr < start) - return 0; - - if (addr > end) - return 0; - - return 1; + return 0; } struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) @@ -402,41 +410,40 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon, struct in_addr start, addr ; unsigned int i, j; - - /* check if no dynamic leases. */ - if (context->static_only) - return 0; - /* pick a seed based on hwaddr then iterate until we find a free address. */ - for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++) - j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16); - - start.s_addr = addr.s_addr = - htonl(ntohl(context->start.s_addr) + - (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr)))); - - do { - if (!lease_find_by_addr(addr) && - !config_find_by_address(daemon->dhcp_conf, addr)) + for (; context; context = context->current) + if (!context->static_only) { - if (icmp_ping(daemon, addr)) - /* perturb address selection so that we are - less likely to try this address again. */ - context->addr_epoch++; - else - { - *addrp = addr; - return 1; - } - } + /* pick a seed based on hwaddr then iterate until we find a free address. */ + for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++) + j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16); + + start.s_addr = addr.s_addr = + htonl(ntohl(context->start.s_addr) + + (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr)))); + + do { + if (!lease_find_by_addr(addr) && + !config_find_by_address(daemon->dhcp_conf, addr)) + { + if (icmp_ping(daemon, addr)) + /* perturb address selection so that we are + less likely to try this address again. */ + context->addr_epoch++; + else + { + *addrp = addr; + return 1; + } + } - addr.s_addr = htonl(ntohl(addr.s_addr) + 1); - - if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1)) - addr = context->start; - - } while (addr.s_addr != start.s_addr); - + addr.s_addr = htonl(ntohl(addr.s_addr) + 1); + + if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1)) + addr = context->start; + + } while (addr.s_addr != start.s_addr); + } return 0; } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 172b986..c16169d 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -294,7 +294,7 @@ struct dhcp_context { struct in_addr start, end; /* range of available addresses */ int static_only; struct dhcp_netid netid; - struct dhcp_context *next; + struct dhcp_context *next, *current; }; typedef unsigned char u8; @@ -393,11 +393,15 @@ int setup_reply(HEADER *header, unsigned int qlen, unsigned long local_ttl); void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, time_t now, struct doctor *doctors); -void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now); +void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now, unsigned short flags); int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now); int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, struct bogus_addr *addr, time_t now); -unsigned char *find_pseudoheader(HEADER *header, unsigned int plen); +unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, + unsigned int *len, unsigned char **p); +int check_for_local_domain(char *name, time_t now, struct mx_record *mx); +int resize_packet(HEADER *header, unsigned int plen, + unsigned char *pheader, unsigned int hlen); /* util.c */ unsigned short rand16(void); diff --git a/src/forward.c b/src/forward.c index 5e4ad8e..3731bdb 100644 --- a/src/forward.c +++ b/src/forward.c @@ -114,8 +114,8 @@ static void send_from(int fd, int nowild, char *packet, int len, } } -unsigned short search_servers(struct daemon *daemon, struct all_addr **addrpp, - unsigned short qtype, char *qdomain, int *type, char **domain) +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 @@ -133,19 +133,23 @@ unsigned short search_servers(struct daemon *daemon, struct all_addr **addrpp, { unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6; *type = SERV_FOR_NODOTS; - flags = 0; if (serv->flags & SERV_NO_ADDR) - flags = F_NXDOMAIN; - else if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & qtype)) - { - flags = sflag; - if (serv->addr.sa.sa_family == AF_INET) - *addrpp = (struct all_addr *)&serv->addr.in.sin_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; + else + *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif - } + } + else if (!flags) + flags = F_NOERR; + } } else if (serv->flags & SERV_HAS_DOMAIN) { @@ -158,23 +162,27 @@ unsigned short search_servers(struct daemon *daemon, struct all_addr **addrpp, *type = SERV_HAS_DOMAIN; *domain = serv->domain; matchlen = domainlen; - flags = 0; if (serv->flags & SERV_NO_ADDR) - flags = F_NXDOMAIN; - else if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & qtype)) + flags = F_NXDOMAIN; + else if (serv->flags & SERV_LITERAL_ADDRESS) { - flags = qtype; - if (serv->addr.sa.sa_family == AF_INET) - *addrpp = (struct all_addr *)&serv->addr.in.sin_addr; + 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; + else + *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; #endif + } + else if (!flags) + flags = F_NOERR; } } } - if (flags & ~F_NXDOMAIN) /* flags set here means a literal found */ + 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); @@ -182,9 +190,12 @@ unsigned short search_servers(struct daemon *daemon, struct all_addr **addrpp, log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0); } else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.')) + flags = F_NXDOMAIN; + + if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames)) flags = F_NOERR; - if (flags & (F_NOERR | F_NXDOMAIN)) + if (flags == F_NXDOMAIN || flags == F_NOERR) log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0); return flags; @@ -223,7 +234,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud else { if (gotname) - flags = search_servers(daemon, &addrp, gotname, daemon->namebuff, &type, &domain); + flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain); if (!flags && !(forward = get_new_frec(now))) /* table full - server failure. */ @@ -316,20 +327,21 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud } static int process_reply(struct daemon *daemon, HEADER *header, time_t now, - union mysockaddr *serveraddr, int n) + union mysockaddr *serveraddr, unsigned int n) { - unsigned char *pheader; + unsigned char *pheader, *sizep; + unsigned int plen; /* If upstream is advertising a larger UDP packet size than we allow, trim it so that we don't get overlarge requests for the client. */ - if ((pheader = find_pseudoheader(header, n))) + if ((pheader = find_pseudoheader(header, n, &plen, &sizep))) { unsigned short udpsz; - unsigned char *psave = pheader; + unsigned char *psave = sizep; - GETSHORT(udpsz, pheader); + GETSHORT(udpsz, sizep); if (udpsz > daemon->edns_pktsz) PUTSHORT(daemon->edns_pktsz, psave); } @@ -350,20 +362,52 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now, return 0; } - if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY) + if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN)) + return n; + + if (header->rcode == NOERROR && ntohs(header->ancount) != 0) { if (!(daemon->bogus_addr && - header->rcode == NOERROR && - check_for_bogus_wildcard(header, (unsigned int)n, daemon->namebuff, daemon->bogus_addr, now))) + check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))) + extract_addresses(header, n, daemon->namebuff, now, daemon->doctors); + } + else + { + unsigned short flags = F_NEG; + int munged = 0; + + if (header->rcode == NXDOMAIN) { - if (header->rcode == NOERROR && ntohs(header->ancount) != 0) - extract_addresses(header, (unsigned int)n, daemon->namebuff, now, daemon->doctors); - else if (!(daemon->options & OPT_NO_NEG)) - extract_neg_addrs(header, (unsigned int)n, daemon->namebuff, now); + /* if we forwarded a query for a locally known name (because it was for + an unknown type) and the answer is NXDOMAIN, convert that to NODATA, + since we know that the domain exists, even if upstream doesn't */ + if (extract_request(header, n, daemon->namebuff, NULL) && + check_for_local_domain(daemon->namebuff, now, daemon->mxnames)) + { + munged = 1; + header->rcode = NOERROR; + } + else + flags |= F_NXDOMAIN; + } + + if (!(daemon->options & OPT_NO_NEG)) + extract_neg_addrs(header, n, daemon->namebuff, now, flags); + + /* do this after extract_neg_addrs. Ensure NODATA reply and remove + nameserver info. */ + if (munged) + { + header->ancount = htons(0); + header->nscount = htons(0); + header->arcount = htons(0); } } - - return 1; + + /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide + sections of the packet. Find the new length here and put back pseudoheader + if it was removed. */ + return resize_packet(header, n, pheader, plen); } /* sets new last_server */ @@ -401,7 +445,7 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now) } } - if (process_reply(daemon, header, now, &serveraddr, n)) + if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n))) { header->id = htons(forward->orig_id); send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n, @@ -654,7 +698,7 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now) char *domain = NULL; if (gotname) - flags = search_servers(daemon, &addrp, gotname, daemon->namebuff, &type, &domain); + flags = search_servers(daemon, now, &addrp, gotname, daemon->namebuff, &type, &domain); if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server) last_server = daemon->servers; @@ -729,7 +773,7 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now) /* There's no point in updating the cache, since this process will exit and lose the information after one query. We make this call for the alias and bogus-nxdomain side-effects. */ - process_reply(daemon, header, now, &last_server->addr, m); + m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m); break; } diff --git a/src/option.c b/src/option.c index 84096ed..3fe4492 100644 --- a/src/option.c +++ b/src/option.c @@ -161,7 +161,7 @@ static char *usage = struct daemon *read_opts (int argc, char **argv) { struct daemon *daemon = safe_malloc(sizeof(struct daemon)); - char *buff = safe_malloc(MAXDNAME); + char *problem = NULL, *buff = safe_malloc(MAXDNAME); int option = 0, i; FILE *file_save = NULL, *f = NULL; char *file_name_save = NULL, *conffile = CONFFILE; @@ -187,6 +187,8 @@ struct daemon *read_opts (int argc, char **argv) while (1) { + problem = NULL; + if (!f) #ifdef HAVE_GETOPT_LONG option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL); @@ -302,7 +304,7 @@ struct daemon *read_opts (int argc, char **argv) complain(buff, NULL); continue; } - + switch (option) { case 'C': @@ -366,7 +368,10 @@ struct daemon *read_opts (int argc, char **argv) if (comma) *(comma++) = 0; if (!canonicalise(optarg) || (comma && !canonicalise(comma))) - option = '?'; + { + option = '?'; + problem = "bad MX name"; + } else { struct mx_record *new = safe_malloc(sizeof(struct mx_record)); @@ -380,7 +385,10 @@ struct daemon *read_opts (int argc, char **argv) case 't': if (!canonicalise(optarg)) - option = '?'; + { + option = '?'; + problem = "bad MX target"; + } else daemon->mxtarget = safe_string_alloc(optarg); break; @@ -391,7 +399,10 @@ struct daemon *read_opts (int argc, char **argv) case 'H': if (daemon->addn_hosts) - option = '?'; + { + option = '?'; + problem = "only one addn hosts file allowed"; + } else daemon->addn_hosts = safe_string_alloc(optarg); break; @@ -563,7 +574,10 @@ struct daemon *read_opts (int argc, char **argv) { *portno = 0; if (!atoi_check(portno+1, &source_port)) - option = '?'; + { + option = '?'; + problem = "bad port"; + } } } @@ -571,7 +585,10 @@ struct daemon *read_opts (int argc, char **argv) { *portno = 0; if (!atoi_check(portno+1, &serv_port)) - option = '?'; + { + option = '?'; + problem = "bad port"; + } } #ifdef HAVE_IPV6 @@ -717,6 +734,8 @@ struct daemon *read_opts (int argc, char **argv) new->netid.net = NULL; new->static_only = 0; + problem = "bad dhcp-range"; + for (cp = optarg; *cp; cp++) if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9'))) break; @@ -755,6 +774,17 @@ struct daemon *read_opts (int argc, char **argv) new->end = tmp; } + if (option != '?' && k >= 3 && strchr(a[2], '.') && + ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1)) + { + leasepos = 3; + if (!is_same_net(new->start, new->end, new->netmask)) + { + problem = "inconsistent DHCP range"; + option = '?'; + } + } + if (option == '?') { free(new); @@ -762,11 +792,7 @@ struct daemon *read_opts (int argc, char **argv) } else daemon->dhcp = new; - - if (k >= 3 && strchr(a[2], '.') && - ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1)) - leasepos = 3; - + if (k >= 4 && strchr(a[3], '.') && ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1)) leasepos = 4; @@ -955,6 +981,7 @@ struct daemon *read_opts (int argc, char **argv) if (option == '?') { + problem = "bad dhcp-host"; if (new->flags & CONFIG_NAME) free(new->hostname); if (new->flags & CONFIG_CLID) @@ -1003,6 +1030,7 @@ struct daemon *read_opts (int argc, char **argv) if ((new->opt = atoi(optarg)) == 0) { option = '?'; + problem = "bad dhcp-opt"; if (new->netid) free(new->netid); free(new); @@ -1203,11 +1231,12 @@ struct daemon *read_opts (int argc, char **argv) { if (f) { - sprintf(buff, "error at line %d of %s ", lineno, conffile); + sprintf(buff, "%s at line %d of %s ", + problem ? problem : "error", lineno, conffile); complain(buff, NULL); } else - die("bad command line options: try --help.", NULL); + die("bad command line options: %s.", problem ? problem : "try --help"); } } diff --git a/src/rfc1035.c b/src/rfc1035.c index 4dbf56c..3dc980e 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -306,9 +306,44 @@ static unsigned char *skip_questions(HEADER *header, unsigned int plen) return ansp; } -unsigned char *find_pseudoheader(HEADER *header, unsigned int plen) +int resize_packet(HEADER *header, unsigned int plen, unsigned char *pheader, unsigned int hlen) { - /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. */ + int i; + unsigned char *ansp = skip_questions(header, plen); + unsigned short rdlen; + + if (!ansp) + return 0; + + for (i = 0; + i < (ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount)); + i++) + { + if (!(ansp = skip_name(ansp, header, plen))) + return 0; + ansp += 8; /* type, class, TTL */ + GETSHORT(rdlen, ansp); + if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) + return 0; + ansp += rdlen; + } + + /* restore pseudoheader */ + if (pheader && ntohs(header->arcount) == 0) + { + /* must use memmove, may overlap */ + memmove(ansp, pheader, hlen); + header->arcount = htons(1); + ansp += hlen; + } + + return ansp - (unsigned char *)header; +} + +unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, unsigned int *len, unsigned char **p) +{ + /* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it. + also return length of pseudoheader in *len and pointer to the UDP size in *p */ int i, arcount = ntohs(header->arcount); unsigned char *ansp; @@ -330,7 +365,7 @@ unsigned char *find_pseudoheader(HEADER *header, unsigned int plen) for (i = 0; i < arcount; i++) { - unsigned char *save; + unsigned char *save, *start = ansp; if (!(ansp = skip_name(ansp, header, plen))) return NULL; @@ -340,9 +375,15 @@ unsigned char *find_pseudoheader(HEADER *header, unsigned int plen) GETSHORT(rdlen, ansp); if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) return NULL; - if (type == T_OPT) - return save; - ansp += rdlen; + ansp += rdlen; + if (type == T_OPT) + { + if (len) + *len = ansp - start; + if (p) + *p = save; + return start; + } } return NULL; @@ -397,17 +438,13 @@ static unsigned char *add_text_record(unsigned int nameoffset, unsigned char *p, /* On receiving an NXDOMAIN or NODATA reply, determine which names are known not to exist for negative caching. name if a working buffer passed in. */ -void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now) +void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now, unsigned short flags) { unsigned char *p; int i, found_soa = 0; int qtype, qclass, rdlen; unsigned long ttl, minttl = 0; - unsigned short flags = F_NEG; - - if (header->rcode == NXDOMAIN) - flags |= F_NXDOMAIN; - + /* there may be more than one question with some questions answered. We don't generate negative entries from those. */ if (ntohs(header->ancount) != 0) @@ -555,7 +592,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, #endif else if (qtype == T_PTR) { - /* PTR record */ + /* PTR record */ struct all_addr addr; int name_encoding = in_arpa_name_2_addr(name, &addr); if (name_encoding) @@ -735,7 +772,22 @@ int setup_reply(HEADER *header, unsigned int qlen, return p - (unsigned char *)header; } - + +/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */ +int check_for_local_domain(char *name, time_t now, struct mx_record *mx) +{ + struct crec *crecp; + + if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4|F_IPV6)) && + (crecp->flags & (F_HOSTS | F_DHCP))) + return 1; + + for (; mx; mx = mx->next) + if (hostname_isequal(name, mx->mxname)) + return 1; + + return 0; +} /* Is the packet a reply with the answer address equal to addr? If so mung is into an NXDOMAIN reply and also put that information @@ -811,7 +863,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon forward rather than answering from the cache, which doesn't include security information. */ - if ((pheader = find_pseudoheader(header, qlen))) + if (find_pseudoheader(header, qlen, NULL, &pheader)) { unsigned short udpsz, ext_rcode, flags; unsigned char *psave = pheader; diff --git a/src/rfc2131.c b/src/rfc2131.c index 4698d92..2739462 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -80,7 +80,7 @@ static int have_config(struct dhcp_config *config, unsigned int mask) int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now) { - struct dhcp_context *context; + struct dhcp_context *context, *context_tmp; unsigned char *opt, *clid; struct dhcp_lease *lease, *ltmp; struct dhcp_vendor *vendor; @@ -148,12 +148,25 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam subnet_addr.s_addr ? subnet_addr : (mess->giaddr.s_addr ? mess->giaddr : (mess->ciaddr.s_addr ? mess->ciaddr : iface_addr)); - - for (context = daemon->dhcp; context; context = context->next) - if (context->netmask.s_addr && - is_same_net(addr, context->start, context->netmask) && - is_same_net(addr, context->end, context->netmask)) - break; + + /* More than one context may match, we build a chain of them all on ->current + Note that if netmasks, netid or lease times don't match, odd things may happen. */ + + for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next) + if (context_tmp->netmask.s_addr && + is_same_net(addr, context_tmp->start, context_tmp->netmask) && + is_same_net(addr, context_tmp->end, context_tmp->netmask)) + { + context_tmp->current = context; + context = context_tmp; + + /* start to build netid chain */ + if (context_tmp->netid.net) + { + context_tmp->netid.next = netid; + netid = &context_tmp->netid; + } + } if (!context) { @@ -164,14 +177,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } mess->op = BOOTREPLY; - - /* start to build netid chain */ - if (context->netid.net) - { - context->netid.next = netid; - netid = &context->netid; - } - + if (mess_type == 0) { /* BOOTP request */ @@ -268,14 +274,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } } - def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; - if (have_config(config, CONFIG_NETID)) { config->netid.next = netid; netid = &config->netid; } - + /* Theres a chance that carefully chosen data could match the same vendor/user option twice and make a loop in the netid chain. */ for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next) @@ -326,10 +330,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam /* do we have a lease in store? */ lease = lease_find_by_client(clid, clid_len); + def_time = have_config(config, CONFIG_TIME) ? config->lease_time : context->lease_time; + if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) { unsigned int req_time = option_uint(opt, 4); - + if (def_time == 0xffffffff || (req_time != 0xffffffff && req_time < def_time)) expires_time = renewal_time = req_time; @@ -392,7 +398,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam } else /* make sure this host gets a different address next time. */ - context->addr_epoch++; + for (; context; context = context->current) + context->addr_epoch++; return 0;