import of dnsmasq-2.15.tar.gz

This commit is contained in:
Simon Kelley
2004-09-20 19:20:58 +01:00
parent 3be34541c2
commit 36717eeefc
10 changed files with 314 additions and 147 deletions

View File

@@ -1190,3 +1190,27 @@ version 2.14
Added "keep-in-foreground" option. Thanks to Sean Added "keep-in-foreground" option. Thanks to Sean
MacLennan for the patch. 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.

View File

@@ -5,7 +5,7 @@
############################################################################### ###############################################################################
Name: dnsmasq Name: dnsmasq
Version: 2.14 Version: 2.15
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: System Environment/Daemons Group: System Environment/Daemons

View File

@@ -5,7 +5,7 @@
############################################################################### ###############################################################################
Name: dnsmasq Name: dnsmasq
Version: 2.14 Version: 2.15
Release: 1 Release: 1
Copyright: GPL Copyright: GPL
Group: Productivity/Networking/DNS/Servers Group: Productivity/Networking/DNS/Servers

View File

@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */ /* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.14" #define VERSION "2.15"
#define FTABSIZ 150 /* max number of outstanding requests */ #define FTABSIZ 150 /* max number of outstanding requests */
#define MAX_PROCS 20 /* max no children for TCP requests */ #define MAX_PROCS 20 /* max no children for TCP requests */

View File

@@ -209,9 +209,19 @@ void dhcp_packet(struct daemon *daemon, time_t now)
iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (iface_netmask.s_addr && if (iface_netmask.s_addr &&
is_same_net(iface_addr, context->start, iface_netmask) && (is_same_net(iface_addr, context->start, iface_netmask) ||
is_same_net(iface_addr, context->end, iface_netmask)) is_same_net(iface_addr, context->end, iface_netmask)))
context->netmask = 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. /* 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) if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; 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; context->broadcast = iface_broadcast;
else else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr; 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) int address_available(struct dhcp_context *context, struct in_addr taddr)
{ {
/* Check is an address is OK for this network, ie /* Check is an address is OK for this network, check all
within allowable range and not in an existing lease */ possible ranges. */
unsigned int addr, start, end; unsigned int start, end, addr = ntohl(taddr.s_addr);
/* static leases only. */ for (; context; context = context->current)
if (context->static_only) {
return 0; start = ntohl(context->start.s_addr);
end = ntohl(context->end.s_addr);
addr = ntohl(taddr.s_addr); if (!context->static_only &&
start = ntohl(context->start.s_addr); addr >= start &&
end = ntohl(context->end.s_addr); addr <= end)
return 1;
}
if (addr < start) return 0;
return 0;
if (addr > end)
return 0;
return 1;
} }
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr) struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr)
@@ -403,40 +411,39 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
struct in_addr start, addr ; struct in_addr start, addr ;
unsigned int i, j; unsigned int i, j;
/* check if no dynamic leases. */ for (; context; context = context->current)
if (context->static_only) 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))
{ {
if (icmp_ping(daemon, addr)) /* pick a seed based on hwaddr then iterate until we find a free address. */
/* perturb address selection so that we are for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
less likely to try this address again. */ j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
context->addr_epoch++;
else start.s_addr = addr.s_addr =
{ htonl(ntohl(context->start.s_addr) +
*addrp = addr; (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
return 1;
} 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; return 0;
} }

View File

@@ -294,7 +294,7 @@ struct dhcp_context {
struct in_addr start, end; /* range of available addresses */ struct in_addr start, end; /* range of available addresses */
int static_only; int static_only;
struct dhcp_netid netid; struct dhcp_netid netid;
struct dhcp_context *next; struct dhcp_context *next, *current;
}; };
typedef unsigned char u8; typedef unsigned char u8;
@@ -393,11 +393,15 @@ int setup_reply(HEADER *header, unsigned int qlen,
unsigned long local_ttl); unsigned long local_ttl);
void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff,
time_t now, struct doctor *doctors); 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 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, int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
struct bogus_addr *addr, time_t now); 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 */ /* util.c */
unsigned short rand16(void); unsigned short rand16(void);

View File

@@ -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, static unsigned short search_servers(struct daemon *daemon, time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain) unsigned short qtype, char *qdomain, int *type, char **domain)
{ {
/* If the query ends in the domain in one of our servers, set /* If the query ends in the domain in one of our servers, set
@@ -133,18 +133,22 @@ 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; unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_FOR_NODOTS; *type = SERV_FOR_NODOTS;
flags = 0;
if (serv->flags & SERV_NO_ADDR) if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN; flags = F_NXDOMAIN;
else if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & qtype)) else if (serv->flags & SERV_LITERAL_ADDRESS)
{ {
flags = sflag; if (sflag & qtype)
if (serv->addr.sa.sa_family == AF_INET) {
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr; flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif #endif
}
else if (!flags)
flags = F_NOERR;
} }
} }
else if (serv->flags & SERV_HAS_DOMAIN) 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; *type = SERV_HAS_DOMAIN;
*domain = serv->domain; *domain = serv->domain;
matchlen = domainlen; matchlen = domainlen;
flags = 0;
if (serv->flags & SERV_NO_ADDR) if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN; flags = F_NXDOMAIN;
else if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & qtype)) else if (serv->flags & SERV_LITERAL_ADDRESS)
{ {
flags = qtype; if ((sflag | F_QUERY ) & qtype)
if (serv->addr.sa.sa_family == AF_INET) {
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr; flags = qtype;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr; *addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif #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) if (flags & F_QUERY)
log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0); 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); log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0);
} }
else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.')) 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; 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); log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0);
return flags; return flags;
@@ -223,7 +234,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
else else
{ {
if (gotname) 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))) if (!flags && !(forward = get_new_frec(now)))
/* table full - server failure. */ /* 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, 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 /* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge than we allow, trim it so that we don't get overlarge
requests for the client. */ requests for the client. */
if ((pheader = find_pseudoheader(header, n))) if ((pheader = find_pseudoheader(header, n, &plen, &sizep)))
{ {
unsigned short udpsz; unsigned short udpsz;
unsigned char *psave = pheader; unsigned char *psave = sizep;
GETSHORT(udpsz, pheader); GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz) if (udpsz > daemon->edns_pktsz)
PUTSHORT(daemon->edns_pktsz, psave); PUTSHORT(daemon->edns_pktsz, psave);
} }
@@ -350,20 +362,52 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
return 0; 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 && if (!(daemon->bogus_addr &&
header->rcode == NOERROR && check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)))
check_for_bogus_wildcard(header, (unsigned int)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) /* if we forwarded a query for a locally known name (because it was for
extract_addresses(header, (unsigned int)n, daemon->namebuff, now, daemon->doctors); an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
else if (!(daemon->options & OPT_NO_NEG)) since we know that the domain exists, even if upstream doesn't */
extract_neg_addrs(header, (unsigned int)n, daemon->namebuff, now); 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 */ /* 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); header->id = htons(forward->orig_id);
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n, 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; char *domain = NULL;
if (gotname) 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) if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers; 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 /* 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 lose the information after one query. We make this call for the alias and
bogus-nxdomain side-effects. */ 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; break;
} }

View File

@@ -161,7 +161,7 @@ static char *usage =
struct daemon *read_opts (int argc, char **argv) struct daemon *read_opts (int argc, char **argv)
{ {
struct daemon *daemon = safe_malloc(sizeof(struct daemon)); struct daemon *daemon = safe_malloc(sizeof(struct daemon));
char *buff = safe_malloc(MAXDNAME); char *problem = NULL, *buff = safe_malloc(MAXDNAME);
int option = 0, i; int option = 0, i;
FILE *file_save = NULL, *f = NULL; FILE *file_save = NULL, *f = NULL;
char *file_name_save = NULL, *conffile = CONFFILE; char *file_name_save = NULL, *conffile = CONFFILE;
@@ -187,6 +187,8 @@ struct daemon *read_opts (int argc, char **argv)
while (1) while (1)
{ {
problem = NULL;
if (!f) if (!f)
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL); option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
@@ -366,7 +368,10 @@ struct daemon *read_opts (int argc, char **argv)
if (comma) if (comma)
*(comma++) = 0; *(comma++) = 0;
if (!canonicalise(optarg) || (comma && !canonicalise(comma))) if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
option = '?'; {
option = '?';
problem = "bad MX name";
}
else else
{ {
struct mx_record *new = safe_malloc(sizeof(struct mx_record)); struct mx_record *new = safe_malloc(sizeof(struct mx_record));
@@ -380,7 +385,10 @@ struct daemon *read_opts (int argc, char **argv)
case 't': case 't':
if (!canonicalise(optarg)) if (!canonicalise(optarg))
option = '?'; {
option = '?';
problem = "bad MX target";
}
else else
daemon->mxtarget = safe_string_alloc(optarg); daemon->mxtarget = safe_string_alloc(optarg);
break; break;
@@ -391,7 +399,10 @@ struct daemon *read_opts (int argc, char **argv)
case 'H': case 'H':
if (daemon->addn_hosts) if (daemon->addn_hosts)
option = '?'; {
option = '?';
problem = "only one addn hosts file allowed";
}
else else
daemon->addn_hosts = safe_string_alloc(optarg); daemon->addn_hosts = safe_string_alloc(optarg);
break; break;
@@ -563,7 +574,10 @@ struct daemon *read_opts (int argc, char **argv)
{ {
*portno = 0; *portno = 0;
if (!atoi_check(portno+1, &source_port)) 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; *portno = 0;
if (!atoi_check(portno+1, &serv_port)) if (!atoi_check(portno+1, &serv_port))
option = '?'; {
option = '?';
problem = "bad port";
}
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
@@ -717,6 +734,8 @@ struct daemon *read_opts (int argc, char **argv)
new->netid.net = NULL; new->netid.net = NULL;
new->static_only = 0; new->static_only = 0;
problem = "bad dhcp-range";
for (cp = optarg; *cp; cp++) for (cp = optarg; *cp; cp++)
if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9'))) if (!(*cp == ' ' || *cp == '.' || (*cp >='0' && *cp <= '9')))
break; break;
@@ -755,6 +774,17 @@ struct daemon *read_opts (int argc, char **argv)
new->end = tmp; 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 == '?') if (option == '?')
{ {
free(new); free(new);
@@ -763,10 +793,6 @@ struct daemon *read_opts (int argc, char **argv)
else else
daemon->dhcp = new; 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], '.') && if (k >= 4 && strchr(a[3], '.') &&
((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1)) ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
leasepos = 4; leasepos = 4;
@@ -955,6 +981,7 @@ struct daemon *read_opts (int argc, char **argv)
if (option == '?') if (option == '?')
{ {
problem = "bad dhcp-host";
if (new->flags & CONFIG_NAME) if (new->flags & CONFIG_NAME)
free(new->hostname); free(new->hostname);
if (new->flags & CONFIG_CLID) if (new->flags & CONFIG_CLID)
@@ -1003,6 +1030,7 @@ struct daemon *read_opts (int argc, char **argv)
if ((new->opt = atoi(optarg)) == 0) if ((new->opt = atoi(optarg)) == 0)
{ {
option = '?'; option = '?';
problem = "bad dhcp-opt";
if (new->netid) if (new->netid)
free(new->netid); free(new->netid);
free(new); free(new);
@@ -1203,11 +1231,12 @@ struct daemon *read_opts (int argc, char **argv)
{ {
if (f) 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); complain(buff, NULL);
} }
else else
die("bad command line options: try --help.", NULL); die("bad command line options: %s.", problem ? problem : "try --help");
} }
} }

View File

@@ -306,9 +306,44 @@ static unsigned char *skip_questions(HEADER *header, unsigned int plen)
return ansp; 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); int i, arcount = ntohs(header->arcount);
unsigned char *ansp; unsigned char *ansp;
@@ -330,7 +365,7 @@ unsigned char *find_pseudoheader(HEADER *header, unsigned int plen)
for (i = 0; i < arcount; i++) for (i = 0; i < arcount; i++)
{ {
unsigned char *save; unsigned char *save, *start = ansp;
if (!(ansp = skip_name(ansp, header, plen))) if (!(ansp = skip_name(ansp, header, plen)))
return NULL; return NULL;
@@ -340,9 +375,15 @@ unsigned char *find_pseudoheader(HEADER *header, unsigned int plen)
GETSHORT(rdlen, ansp); GETSHORT(rdlen, ansp);
if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen)
return NULL; return NULL;
if (type == T_OPT) ansp += rdlen;
return save; if (type == T_OPT)
ansp += rdlen; {
if (len)
*len = ansp - start;
if (p)
*p = save;
return start;
}
} }
return NULL; return NULL;
@@ -397,16 +438,12 @@ static unsigned char *add_text_record(unsigned int nameoffset, unsigned char *p,
/* On receiving an NXDOMAIN or NODATA reply, determine which names are known /* 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. */ 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; unsigned char *p;
int i, found_soa = 0; int i, found_soa = 0;
int qtype, qclass, rdlen; int qtype, qclass, rdlen;
unsigned long ttl, minttl = 0; 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 /* there may be more than one question with some questions
answered. We don't generate negative entries from those. */ answered. We don't generate negative entries from those. */
@@ -555,7 +592,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name,
#endif #endif
else if (qtype == T_PTR) else if (qtype == T_PTR)
{ {
/* PTR record */ /* PTR record */
struct all_addr addr; struct all_addr addr;
int name_encoding = in_arpa_name_2_addr(name, &addr); int name_encoding = in_arpa_name_2_addr(name, &addr);
if (name_encoding) if (name_encoding)
@@ -736,6 +773,21 @@ int setup_reply(HEADER *header, unsigned int qlen,
return p - (unsigned char *)header; 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? /* 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 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 forward rather than answering from the cache, which doesn't include
security information. */ security information. */
if ((pheader = find_pseudoheader(header, qlen))) if (find_pseudoheader(header, qlen, NULL, &pheader))
{ {
unsigned short udpsz, ext_rcode, flags; unsigned short udpsz, ext_rcode, flags;
unsigned char *psave = pheader; unsigned char *psave = pheader;

View File

@@ -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) 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; unsigned char *opt, *clid;
struct dhcp_lease *lease, *ltmp; struct dhcp_lease *lease, *ltmp;
struct dhcp_vendor *vendor; struct dhcp_vendor *vendor;
@@ -149,11 +149,24 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
(mess->giaddr.s_addr ? mess->giaddr : (mess->giaddr.s_addr ? mess->giaddr :
(mess->ciaddr.s_addr ? mess->ciaddr : iface_addr)); (mess->ciaddr.s_addr ? mess->ciaddr : iface_addr));
for (context = daemon->dhcp; context; context = context->next) /* More than one context may match, we build a chain of them all on ->current
if (context->netmask.s_addr && Note that if netmasks, netid or lease times don't match, odd things may happen. */
is_same_net(addr, context->start, context->netmask) &&
is_same_net(addr, context->end, context->netmask)) for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
break; 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) if (!context)
{ {
@@ -165,13 +178,6 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
mess->op = BOOTREPLY; mess->op = BOOTREPLY;
/* start to build netid chain */
if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
if (mess_type == 0) if (mess_type == 0)
{ {
/* BOOTP request */ /* BOOTP request */
@@ -268,8 +274,6 @@ 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)) if (have_config(config, CONFIG_NETID))
{ {
config->netid.next = netid; config->netid.next = netid;
@@ -326,6 +330,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
/* do we have a lease in store? */ /* do we have a lease in store? */
lease = lease_find_by_client(clid, clid_len); 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))) if ((opt = option_find(mess, sz, OPTION_LEASE_TIME)))
{ {
unsigned int req_time = option_uint(opt, 4); unsigned int req_time = option_uint(opt, 4);
@@ -392,7 +398,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
} }
else else
/* make sure this host gets a different address next time. */ /* make sure this host gets a different address next time. */
context->addr_epoch++; for (; context; context = context->current)
context->addr_epoch++;
return 0; return 0;