import of dnsmasq-2.53.tar.gz

This commit is contained in:
Simon Kelley committed 2012-01-05 17:31:15 +00:00
1 parent 316e2730ac
commit 8ef5ada238
34 files changed
+6223 -4426

No files matched your search

+9 -6
View File
@@ -1045,7 +1045,14 @@ void cache_add_dhcp_entry(char *host_name,
/* check all addresses associated with name */
if (crec->flags & F_HOSTS)
{
if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
/* if in hosts, don't need DHCP record */
in_hosts = 1;
if (crec->flags & F_CNAME)
my_syslog(LOG_WARNING,
_("%s is a CNAME, not giving it to the DHCP lease of %s"),
host_name, inet_ntoa(*host_address));
else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
{
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
my_syslog(LOG_WARNING,
@@ -1053,11 +1060,7 @@ void cache_add_dhcp_entry(char *host_name,
"the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address),
record_source(crec->uid), daemon->namebuff);
return;
}
else
/* if in hosts, don't need DHCP record */
in_hosts = 1;
}
}
else if (!(crec->flags & F_DHCP))
{
+4 -4
View File
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define VERSION "2.52"
#define VERSION "2.54"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -22,7 +22,7 @@
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 10 /* or 10 seconds */
#define FORWARD_TIME 20 /* or 10 seconds */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
@@ -230,13 +230,13 @@ NOTES:
#elif defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__DragonFly__) || \
defined (__FreeBSD_kernel__)
defined(__FreeBSD_kernel__)
#define HAVE_BSD_NETWORK
/* Later verions of FreeBSD have getopt_long() */
#if defined(optional_argument) && defined(required_argument)
# define HAVE_GETOPT_LONG
#endif
#if !defined (__FreeBSD_kernel__)
#if !defined(__FreeBSD_kernel__)
# define HAVE_ARC4RANDOM
#endif
#define HAVE_SOCKADDR_SA_LEN
+114 -42
View File
@@ -127,7 +127,10 @@ void dhcp_packet(time_t now, int pxe_fd)
int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL;
struct iface_param parm;
#ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req;
#endif
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
@@ -189,8 +192,13 @@ void dhcp_packet(time_t now, int pxe_fd)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
iface_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
if (((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_addr.s_addr != INADDR_BROADCAST)
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.p->ipi_ifindex;
if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
@@ -198,20 +206,37 @@ void dhcp_packet(time_t now, int pxe_fd)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
iface_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
{
union {
unsigned char *c;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.s->sdl_index;
}
#elif defined(HAVE_SOLARIS_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
iface_index = *((unsigned int *)CMSG_DATA(cmptr));
{
union {
unsigned char *c;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
iface_index = *(p.i);
}
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name))
return;
#ifdef HAVE_LINUX_NETWORK
/* ARP fiddling uses original interface even if we pretend to use a different one. */
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST))
@@ -231,18 +256,14 @@ void dhcp_packet(time_t now, int pxe_fd)
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* interface may have been changed by alias in iface_check */
if (!addrp)
{
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
/* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
break;
if (!context)
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
@@ -253,6 +274,27 @@ void dhcp_packet(time_t now, int pxe_fd)
parm.current = NULL;
parm.ind = iface_index;
/* interface may have been changed by alias in iface_check, make sure it gets priority in case
there is more than one address on the interface in the same subnet */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
{
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
{
struct in_addr netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
{
struct in_addr broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
}
}
}
if (!iface_enumerate(&parm, complete_context, NULL))
return;
lease_prune(NULL, now); /* lose any expired leases */
@@ -324,15 +366,14 @@ void dhcp_packet(time_t now, int pxe_fd)
{
/* unicast to unconfigured client. Inject mac address direct into ARP cache.
struct sockaddr limits size to 14 bytes. */
struct arpreq req;
dest.sin_addr = mess->yiaddr;
dest.sin_port = htons(daemon->dhcp_client_port);
*((struct sockaddr_in *)&req.arp_pa) = dest;
req.arp_ha.sa_family = mess->htype;
memcpy(req.arp_ha.sa_data, mess->chaddr, mess->hlen);
strncpy(req.arp_dev, ifr.ifr_name, 16);
req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &req);
memcpy(&arp_req.arp_pa, &dest, sizeof(struct sockaddr_in));
arp_req.arp_ha.sa_family = mess->htype;
memcpy(arp_req.arp_ha.sa_data, mess->chaddr, mess->hlen);
/* interface name already copied in */
arp_req.arp_flags = ATF_COM;
ioctl(daemon->dhcpfd, SIOCSARP, &arp_req);
}
#elif defined(HAVE_SOLARIS_NETWORK)
else if ((ntohs(mess->flags) & 0x8000) || mess->hlen != ETHER_ADDR_LEN || mess->htype != ARPHRD_ETHER)
@@ -491,13 +532,15 @@ struct dhcp_context *narrow_context(struct dhcp_context *context,
if (!(tmp = address_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask) &&
if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (is_same_net(taddr, tmp->start, tmp->netmask))
if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask))
break;
}
@@ -530,7 +573,8 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotnee
for (; check; check = check->next)
{
if (check->net[0] != '#')
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
@@ -546,6 +590,22 @@ int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotnee
return 1;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now)
@@ -559,9 +619,10 @@ int address_allocate(struct dhcp_context *context,
int i, pass;
unsigned int j;
/* hash hwaddr */
/* hash hwaddr: use the SDBM hashing algorithm. Seems to give good
dispersal even with similarly-valued "strings". */
for (j = 0, i = 0; i < hw_len; i++)
j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
j += hwaddr[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
@@ -977,29 +1038,40 @@ void dhcp_update_configs(struct dhcp_config *configs)
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and
it gets stripped. The set of legal domain names is bigger than the set of legal hostnames
so check here that the domain name is legal as a hostname. */
so check here that the domain name is legal as a hostname.
NOTE: we're only allowed to overwrite daemon->dhcp_buff if we succeed. */
char *host_from_dns(struct in_addr addr)
{
struct crec *lookup;
char *hostname = NULL;
char *d1, *d2;
if (daemon->port == 0)
return NULL; /* DNS disabled. */
lookup = cache_find_by_addr(NULL, (struct all_addr *)&addr, 0, F_IPV4);
if (lookup && (lookup->flags & F_HOSTS))
{
hostname = daemon->dhcp_buff;
strncpy(hostname, cache_get_name(lookup), 256);
hostname[255] = 0;
d1 = strip_hostname(hostname);
d2 = get_domain(addr);
if (!legal_hostname(hostname) || (d1 && (!d2 || !hostname_isequal(d1, d2))))
hostname = NULL;
char *dot, *hostname = cache_get_name(lookup);
dot = strchr(hostname, '.');
if (dot && strlen(dot+1) != 0)
{
char *d2 = get_domain(addr);
if (!d2 || !hostname_isequal(dot+1, d2))
return NULL; /* wrong domain */
}
if (!legal_hostname(hostname))
return NULL;
strncpy(daemon->dhcp_buff, hostname, 256);
daemon->dhcp_buff[255] = 0;
strip_hostname(daemon->dhcp_buff);
return daemon->dhcp_buff;
}
return hostname;
return NULL;
}
/* return domain or NULL if none. */
+47 -26
View File
@@ -63,7 +63,6 @@ static void check_dns_listeners(fd_set *set, time_t now);
static void sig_handler(int sig);
static void async_event(int pipe, time_t now);
static void fatal_event(struct event_desc *ev);
static void poll_resolv(void);
int main (int argc, char **argv)
{
@@ -142,7 +141,7 @@ int main (int argc, char **argv)
#endif
#ifndef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif
@@ -184,7 +183,7 @@ int main (int argc, char **argv)
die(_("no interface with address %s"), daemon->namebuff, EC_BADNET);
}
}
else if ((daemon->port != 0 || (daemon->options & OPT_TFTP)) &&
else if ((daemon->port != 0 || daemon->tftp_interfaces || daemon->tftp_unlimited) &&
!(daemon->listeners = create_wildcard_listeners()))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
@@ -278,8 +277,6 @@ int main (int argc, char **argv)
if (!(daemon->options & OPT_DEBUG))
{
int nullfd;
/* The following code "daemonizes" the process.
See Stevens section 12.4 */
@@ -344,16 +341,19 @@ int main (int argc, char **argv)
_exit(0);
}
}
/* open stdout etc to /dev/null */
nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
log_err = log_start(ent_pw, err_pipe[1]);
log_err = log_start(ent_pw, err_pipe[1]);
if (!(daemon->options & OPT_DEBUG))
{
/* open stdout etc to /dev/null */
int nullfd = open("/dev/null", O_RDWR);
dup2(nullfd, STDOUT_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(nullfd, STDIN_FILENO);
close(nullfd);
}
/* if we are to run scripts, we need to fork a helper before dropping root. */
daemon->helperfd = -1;
@@ -508,7 +508,7 @@ int main (int argc, char **argv)
#endif
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
#ifdef FD_SETSIZE
if (FD_SETSIZE < (unsigned)max_fd)
@@ -647,10 +647,11 @@ int main (int argc, char **argv)
difftime(now, daemon->last_resolv) > 1.0 ||
difftime(now, daemon->last_resolv) < -1.0)
{
daemon->last_resolv = now;
/* poll_resolv doesn't need to reload first time through, since
that's queued anyway. */
if (daemon->port != 0 && !(daemon->options & OPT_NO_POLL))
poll_resolv();
poll_resolv(0, daemon->last_resolv != 0, now);
daemon->last_resolv = now;
}
if (FD_ISSET(piperead, &rset))
@@ -898,7 +899,7 @@ static void async_event(int pipe, time_t now)
}
}
static void poll_resolv()
void poll_resolv(int force, int do_reload, time_t now)
{
struct resolvc *res, *latest;
struct stat statbuf;
@@ -906,19 +907,37 @@ static void poll_resolv()
/* There may be more than one possible file.
Go through and find the one which changed _last_.
Warn of any which can't be read. */
if (daemon->port == 0 || (daemon->options & OPT_NO_POLL))
return;
for (latest = NULL, res = daemon->resolv_files; res; res = res->next)
if (stat(res->name, &statbuf) == -1)
{
if (force)
{
res->mtime = 0;
continue;
}
if (!res->logged)
my_syslog(LOG_WARNING, _("failed to access %s: %s"), res->name, strerror(errno));
res->logged = 1;
if (res->mtime != 0)
{
/* existing file evaporated, force selection of the latest
file even if its mtime hasn't changed since we last looked */
poll_resolv(1, do_reload, now);
return;
}
}
else
{
res->logged = 0;
if (statbuf.st_mtime != res->mtime)
{
res->mtime = statbuf.st_mtime;
if (force || (statbuf.st_mtime != res->mtime))
{
res->mtime = statbuf.st_mtime;
if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
@@ -935,8 +954,8 @@ static void poll_resolv()
my_syslog(LOG_INFO, _("reading %s"), latest->name);
warned = 0;
check_servers();
if (daemon->options & OPT_RELOAD)
cache_reload();
if ((daemon->options & OPT_RELOAD) && do_reload)
clear_cache_and_reload(now);
}
else
{
@@ -1125,11 +1144,13 @@ static void check_dns_listeners(fd_set *set, time_t now)
dst_addr_4.s_addr = 0;
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
terminate the process. */
if (!(daemon->options & OPT_DEBUG))
alarm(CHILD_LIFETIME);
#endif
/* start with no upstream connections. */
for (s = daemon->servers; s; s = s->next)
s->tcpfd = -1;
+49 -12
View File
@@ -22,8 +22,8 @@
# define _FILE_OFFSET_BITS 64
#endif
/* Get linux C library versions. */
#ifdef __linux__
/* Get linux C library versions and define _GNU_SOURCE for kFreeBSD. */
#if defined(__linux__) || defined(__GLIBC__)
# define _GNU_SOURCE
# include <features.h>
#endif
@@ -188,7 +188,7 @@ struct event_desc {
#define OPT_LEASE_RO (1u<<22)
#define OPT_ALL_SERVERS (1u<<23)
#define OPT_RELOAD (1u<<24)
#define OPT_TFTP (1u<<25)
#define OPT_LOCAL_REBIND (1u<<25)
#define OPT_TFTP_SECURE (1u<<26)
#define OPT_TFTP_NOBLOCK (1u<<27)
#define OPT_LOG_OPTS (1u<<28)
@@ -319,6 +319,8 @@ union mysockaddr {
#define SERV_MARK 256 /* for mark-and-delete */
#define SERV_TYPE (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
#define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
struct serverfd {
int fd;
@@ -345,7 +347,8 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int dhcp_ok, mtu;
int tftp_ok, mtu;
char *name;
struct irec *next;
};
@@ -391,7 +394,7 @@ struct frec {
#endif
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall;
int fd, forwardall, norebind;
unsigned int crc;
time_t time;
struct frec *next;
@@ -437,6 +440,12 @@ struct dhcp_netid_list {
struct dhcp_netid_list *next;
};
struct tag_if {
struct dhcp_netid_list *set;
struct dhcp_netid *tag;
struct tag_if *next;
};
struct hwaddr_config {
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
@@ -449,7 +458,7 @@ struct dhcp_config {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
char *hostname, *domain;
struct dhcp_netid netid;
struct dhcp_netid_list *netid;
struct in_addr addr;
time_t decline_time;
unsigned int lease_time;
@@ -462,7 +471,6 @@ struct dhcp_config {
#define CONFIG_TIME 8
#define CONFIG_NAME 16
#define CONFIG_ADDR 32
#define CONFIG_NETID 64
#define CONFIG_NOCLID 128
#define CONFIG_FROM_ETHERS 256 /* entry created by /etc/ethers */
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
@@ -548,6 +556,7 @@ struct dhcp_context {
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
int flags;
char *interface;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
@@ -598,6 +607,23 @@ struct tftp_transfer {
struct tftp_transfer *next;
};
struct addr_list {
struct in_addr addr;
struct addr_list *next;
};
struct interface_list {
char *interface;
struct interface_list *next;
};
struct tftp_prefix {
char *interface;
char *prefix;
struct tftp_prefix *next;
};
extern struct daemon {
/* datastuctures representing the command-line and
config file arguments. All set (including defaults)
@@ -628,7 +654,7 @@ extern struct daemon {
int max_logs; /* queue limit */
int cachesize, ftabsize;
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl;
unsigned long local_ttl, neg_ttl, max_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_config *dhcp_conf;
@@ -637,8 +663,12 @@ extern struct daemon {
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
struct pxe_service *pxe_services;
struct tag_if *tag_if;
struct addr_list *override_relays;
int override;
int enable_pxe;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *force_broadcast, *bootp_dynamic;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
char *dhcp_hosts_file, *dhcp_opts_file;
int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port;
@@ -647,6 +677,9 @@ extern struct daemon {
struct doctor *doctors;
unsigned short edns_pktsz;
char *tftp_prefix;
struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
int tftp_unlimited;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
@@ -675,7 +708,7 @@ extern struct daemon {
int dhcp_raw_fd, dhcp_icmp_fd;
#endif
struct iovec dhcp_packet;
char *dhcp_buff, *dhcp_buff2;
char *dhcp_buff, *dhcp_buff2, *dhcp_buff3;
struct ping_result *ping_results;
FILE *lease_stream;
struct dhcp_bridge *bridges;
@@ -719,7 +752,8 @@ unsigned short extract_request(HEADER *header, size_t qlen,
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags,
unsigned long local_ttl);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff, time_t now);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind);
size_t answer_request(HEADER *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
@@ -803,9 +837,11 @@ struct dhcp_context *address_available(struct dhcp_context *context,
struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr,
struct dhcp_netid *netids);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);int address_allocate(struct dhcp_context *context,
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
@@ -856,6 +892,7 @@ int icmp_ping(struct in_addr addr);
#endif
void send_event(int fd, int event, int data);
void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now);
/* netlink.c */
#ifdef HAVE_LINUX_NETWORK
+111 -62
View File
@@ -65,15 +65,15 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
if (to->sa.sa_family == AF_INET)
{
#if defined(HAVE_LINUX_NETWORK)
struct in_pktinfo *pkt = (struct in_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi_ifindex = 0;
pkt->ipi_spec_dst = source->addr.addr4;
struct in_pktinfo p;
p.ipi_ifindex = 0;
p.ipi_spec_dst = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
struct in_addr *a = (struct in_addr *)CMSG_DATA(cmptr);
*a = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_SENDSRCADDR;
@@ -82,9 +82,10 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
else
#ifdef HAVE_IPV6
{
struct in6_pktinfo *pkt = (struct in6_pktinfo *)CMSG_DATA(cmptr);
pkt->ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
pkt->ipi6_addr = source->addr.addr6;
struct in6_pktinfo p;
p.ipi6_ifindex = iface; /* Need iface for IPv6 to handle link-local addrs */
p.ipi6_addr = source->addr.addr6;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = daemon->v6pktinfo;
cmptr->cmsg_level = IPV6_LEVEL;
@@ -111,7 +112,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
}
static unsigned short search_servers(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, int *norebind)
{
/* If the query ends in the domain in one of our servers, set
@@ -153,38 +154,44 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
char *matchstart = qdomain + namelen - domainlen;
if (namelen >= domainlen &&
hostname_isequal(matchstart, serv->domain) &&
domainlen >= matchlen &&
(domainlen == 0 || namelen == domainlen || *(serv->domain) == '.' || *(matchstart-1) == '.' ))
(domainlen == 0 || namelen == domainlen || *(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 (serv->flags & SERV_NO_REBIND)
*norebind = 1;
else if (domainlen >= matchlen)
{
if (sflag & qtype)
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*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)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
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_NXDOMAIN))
flags = F_NOERR;
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
}
else
flags = 0;
}
}
}
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;
@@ -197,7 +204,11 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
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;
}
@@ -206,7 +217,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
HEADER *header, size_t plen, time_t now, struct frec *forward)
{
char *domain = NULL;
int type = 0;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned short flags = 0;
@@ -234,7 +245,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (!flags && !(forward = get_new_frec(now, NULL)))
/* table full - server failure. */
@@ -250,22 +261,33 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
forward->norebind = norebind;
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,
/* 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 || (daemon->options & OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server) ||
daemon->forwardcount++ > FORWARD_TEST ||
difftime(now, daemon->forwardtime) > FORWARD_TIME)
if (type == 0)
{
if (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;
}
}
else
{
start = daemon->servers;
forward->forwardall = 1;
daemon->forwardcount = 0;
daemon->forwardtime = now;
if (!(daemon->options & OPT_ORDER))
forward->forwardall = 1;
}
}
}
@@ -374,7 +396,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n)
struct server *server, size_t n, int check_rebind)
{
unsigned char *pheader, *sizep;
int munged = 0, is_sign;
@@ -428,9 +450,9 @@ static size_t process_reply(HEADER *header, time_t now,
header->rcode = NOERROR;
}
if (extract_addresses(header, n, daemon->namebuff, now))
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected"));
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
}
}
@@ -543,7 +565,12 @@ void reply_query(int fd, int family, time_t now)
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
{
if ((nn = process_reply(header, now, server, (size_t)n)))
int check_rebind = !forward->norebind;
if (!(daemon->options & OPT_NO_REBIND))
check_rebind = 0;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
@@ -635,21 +662,37 @@ void receive_query(struct listener *listen, time_t now)
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;
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))
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)
#ifdef HAVE_SOLARIS_NETWORK
if_index = *((unsigned int *)CMSG_DATA(cmptr));
#else
if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
{
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
@@ -659,8 +702,14 @@ void receive_query(struct listener *listen, time_t now)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
{
dst_addr.addr.addr6 = ((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_addr;
if_index =((struct in6_pktinfo *)CMSG_DATA(cmptr))->ipi6_ifindex;
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
@@ -717,7 +766,7 @@ void receive_query(struct listener *listen, time_t now)
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
{
int size = 0;
int size = 0, norebind = 0;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
@@ -776,7 +825,7 @@ unsigned char *tcp_request(int confd, time_t now,
char *domain = NULL;
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain);
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
@@ -792,7 +841,7 @@ unsigned char *tcp_request(int confd, time_t now,
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
@@ -857,7 +906,7 @@ unsigned char *tcp_request(int confd, time_t now,
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);
m = process_reply(header, now, last_server, (unsigned int)m, (daemon->options & OPT_NO_REBIND) && !norebind );
break;
}
+6 -3
View File
@@ -245,9 +245,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
if (data.action != ACTION_DEL)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
+3 -2
View File
@@ -29,11 +29,12 @@ void lease_init(time_t now)
int clid_len, hw_len, hw_type;
FILE *leasestream;
/* These two each hold a DHCP option max size 255
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
leases_left = daemon->dhcp_max;
if (daemon->options & OPT_LEASE_RO)
+52 -41
View File
@@ -30,7 +30,8 @@
/* defaults in case we die() before we log_start() */
static int log_fac = LOG_DAEMON;
static int log_stderr = 0;
static int log_stderr = 0;
static int echo_stderr = 0;
static int log_fd = -1;
static int log_to_file = 0;
static int entries_alloced = 0;
@@ -54,7 +55,7 @@ int log_start(struct passwd *ent_pw, int errfd)
{
int ret = 0;
log_stderr = !!(daemon->options & OPT_DEBUG);
echo_stderr = !!(daemon->options & OPT_DEBUG);
if (daemon->log_fac != -1)
log_fac = daemon->log_fac;
@@ -67,6 +68,12 @@ int log_start(struct passwd *ent_pw, int errfd)
{
log_to_file = 1;
daemon->max_logs = 0;
if (strcmp(daemon->log_file, "-") == 0)
{
log_stderr = 1;
echo_stderr = 0;
log_fd = dup(STDERR_FILENO);
}
}
max_logs = daemon->max_logs;
@@ -90,7 +97,7 @@ int log_start(struct passwd *ent_pw, int errfd)
change the ownership here so that the file is always owned by
the dnsmasq user. Then logrotate can just copy the owner.
Failure of the chown call is OK, (for instance when started as non-root) */
if (log_to_file && ent_pw && ent_pw->pw_uid != 0 &&
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0 &&
fchown(log_fd, ent_pw->pw_uid, -1) != 0)
ret = errno;
@@ -99,37 +106,34 @@ int log_start(struct passwd *ent_pw, int errfd)
int log_reopen(char *log_file)
{
if (log_fd != -1)
close(log_fd);
/* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
{
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
return log_fd != -1;
}
else
if (!log_stderr)
{
if (log_fd != -1)
close(log_fd);
/* NOTE: umask is set to 022 by the time this gets called */
if (log_file)
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
else
{
#ifdef HAVE_SOLARIS_NETWORK
/* Solaris logging is "different", /dev/log is not unix-domain socket.
Just leave log_fd == -1 and use the vsyslog call for everything.... */
/* Solaris logging is "different", /dev/log is not unix-domain socket.
Just leave log_fd == -1 and use the vsyslog call for everything.... */
# define _PATH_LOG "" /* dummy */
log_fd = -1;
return 1;
#else
{
int flags;
log_fd = socket(AF_UNIX, connection_type, 0);
if (log_fd == -1)
return 0;
/* if max_logs is zero, leave the socket blocking */
if (max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
}
int flags;
log_fd = socket(AF_UNIX, connection_type, 0);
/* if max_logs is zero, leave the socket blocking */
if (log_fd != -1 && max_logs != 0 && (flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags | O_NONBLOCK);
#endif
return 1;
}
}
return log_fd != -1;
}
static void free_entry(void)
@@ -274,7 +278,7 @@ void my_syslog(int priority, const char *format, ...)
priority &= LOG_PRIMASK;
#endif
if (log_stderr)
if (echo_stderr)
{
fprintf(stderr, "dnsmasq%s: ", func);
va_start(ap, format);
@@ -394,14 +398,19 @@ void check_log_writer(fd_set *set)
void flush_log(void)
{
/* block until queue empty */
if (log_fd != -1)
/* write until queue empty */
while (log_fd != -1)
{
int flags;
if ((flags = fcntl(log_fd, F_GETFL)) != -1)
fcntl(log_fd, F_SETFL, flags & ~O_NONBLOCK);
struct timespec waiter;
log_write();
close(log_fd);
if (!entries)
{
close(log_fd);
break;
}
waiter.tv_sec = 0;
waiter.tv_nsec = 1000000; /* 1 ms */
nanosleep(&waiter, NULL);
}
}
@@ -412,11 +421,13 @@ void die(char *message, char *arg1, int exit_code)
if (!arg1)
arg1 = errmess;
log_stderr = 1; /* print as well as log when we die.... */
fputc('\n', stderr); /* prettyfy startup-script message */
if (!log_stderr)
{
echo_stderr = 1; /* print as well as log when we die.... */
fputc('\n', stderr); /* prettyfy startup-script message */
}
my_syslog(LOG_CRIT, message, arg1, errmess);
log_stderr = 0;
echo_stderr = 0;
my_syslog(LOG_CRIT, _("FAILED to start up"));
flush_log();
+2 -2
View File
@@ -281,8 +281,8 @@ static void nl_routechange(struct nlmsghdr *h)
return;
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
poll_resolv(1, 1, dnsmasq_time());
if (daemon->srv_save)
{
if (daemon->srv_save->sfd)
+104 -65
View File
@@ -53,7 +53,44 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (daemon->if_names || (addr && daemon->if_addrs))
{
#ifdef HAVE_DHCP
struct dhcp_context *range;
#endif
ret = 0;
#ifdef HAVE_DHCP
for (range = daemon->dhcp; range; range = range->next)
if (range->interface && strcmp(range->interface, name) == 0)
ret = 1;
#endif
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (addr && tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
ret = tmp->used = 1;
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
&addr->addr.addr6))
ret = tmp->used = 1;
#endif
}
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = 0;
if (indexp)
{
/* One form of bridging on BSD has the property that packets
@@ -85,33 +122,6 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
}
}
if (daemon->if_names || (addr && daemon->if_addrs))
{
ret = 0;
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (addr && tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
ret = tmp->used = 1;
#ifdef HAVE_IPV6
else if (family == AF_INET6 &&
IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr,
&addr->addr.addr6))
ret = tmp->used = 1;
#endif
}
}
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = 0;
return ret;
}
@@ -121,9 +131,12 @@ static int iface_allowed(struct irec **irecp, int if_index,
struct irec *iface;
int fd, mtu = 0, loopback;
struct ifreq ifr;
int dhcp_ok = 1;
int tftp_ok = daemon->tftp_unlimited;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
struct interface_list *ir = NULL;
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
@@ -173,27 +186,45 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
}
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
dhcp_ok = 0;
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
return 1;
#ifdef HAVE_TFTP
/* implement wierd TFTP service rules */
if (addr->sa.sa_family == AF_INET)
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, ifr.ifr_name) == 0)
{
tftp_ok = 1;
break;
}
#endif
if (!ir)
{
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
return 1;
#ifdef HAVE_DHCP
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
tftp_ok = 0;
#endif
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
return 1;
#endif
}
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
{
iface->addr = *addr;
iface->netmask = netmask;
iface->dhcp_ok = dhcp_ok;
iface->tftp_ok = tftp_ok;
iface->mtu = mtu;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
@@ -377,7 +408,7 @@ struct listener *create_wildcard_listeners(void)
}
#ifdef HAVE_TFTP
if (daemon->options & OPT_TFTP)
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
{
addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
@@ -475,7 +506,7 @@ struct listener *create_bound_listeners(void)
}
#ifdef HAVE_TFTP
if ((daemon->options & OPT_TFTP) && iface->addr.sa.sa_family == AF_INET && iface->dhcp_ok)
if (iface->addr.sa.sa_family == AF_INET && iface->tftp_ok)
{
short save = iface->addr.in.sin_port;
iface->addr.in.sin_port = htons(TFTP_PORT);
@@ -666,7 +697,7 @@ void pre_allocate_sfds(void)
}
for (srv = daemon->servers; srv; srv = srv->next)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)) &&
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
!allocate_sfd(&srv->source_addr, srv->interface) &&
errno != 0 &&
(daemon->options & OPT_NOWILD))
@@ -697,7 +728,7 @@ void check_servers(void)
{
tmp = new->next;
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR)))
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
port = prettyprint_addr(&new->addr, daemon->namebuff);
@@ -736,25 +767,30 @@ void check_servers(void)
new->next = ret;
ret = new;
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS))
if (!(new->flags & SERV_NO_REBIND))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{
char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0)
s1 = _("default"), s2 = "";
else
s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (new->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
}
else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else
s1 = _("domain"), s2 = new->domain;
if (new->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
else if (new->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface);
else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
}
daemon->servers = ret;
@@ -881,16 +917,19 @@ struct in_addr get_ifaddr(char *intr)
{
struct listener *l;
struct ifreq ifr;
struct sockaddr_in ret;
ret.sin_addr.s_addr = -1;
for (l = daemon->listeners; l && l->family != AF_INET; l = l->next);
strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
if (!l || ioctl(l->fd, SIOCGIFADDR, &ifr) == -1)
((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr = -1;
if (l && ioctl(l->fd, SIOCGIFADDR, &ifr) != -1)
memcpy(&ret, &ifr.ifr_addr, sizeof(ret));
return ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
return ret.sin_addr;
}
+272 -99
View File
@@ -102,6 +102,12 @@ struct myoption {
#define LOPT_PXE_PROMT 291
#define LOPT_PXE_SERV 292
#define LOPT_TEST 293
#define LOPT_TAG_IF 294
#define LOPT_PROXY 295
#define LOPT_GEN_NAMES 296
#define LOPT_MAXTTL 297
#define LOPT_NO_REBIND 298
#define LOPT_LOC_REBND 299
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -176,7 +182,7 @@ static const struct myoption opts[] =
{ "dns-forward-max", 1, 0, '0' },
{ "clear-on-reload", 0, 0, LOPT_RELOAD },
{ "dhcp-ignore-names", 2, 0, LOPT_NO_NAMES },
{ "enable-tftp", 0, 0, LOPT_TFTP },
{ "enable-tftp", 2, 0, LOPT_TFTP },
{ "tftp-secure", 0, 0, LOPT_SECURE },
{ "tftp-unique-root", 0, 0, LOPT_APREF },
{ "tftp-root", 1, 0, LOPT_PREFIX },
@@ -197,10 +203,12 @@ static const struct myoption opts[] =
{ "dhcp-no-override", 0, 0, LOPT_OVERRIDE },
{ "tftp-port-range", 1, 0, LOPT_TFTPPORTS },
{ "stop-dns-rebind", 0, 0, LOPT_REBIND },
{ "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
{ "all-servers", 0, 0, LOPT_NOLAST },
{ "dhcp-match", 1, 0, LOPT_MATCH },
{ "dhcp-broadcast", 1, 0, LOPT_BROADCAST },
{ "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
{ "neg-ttl", 1, 0, LOPT_NEGTTL },
{ "max-ttl", 1, 0, LOPT_MAXTTL },
{ "dhcp-alternate-port", 2, 0, LOPT_ALTPORT },
{ "dhcp-scriptuser", 1, 0, LOPT_SCRIPTUSR },
{ "min-port", 1, 0, LOPT_MINPORT },
@@ -209,6 +217,10 @@ static const struct myoption opts[] =
{ "pxe-prompt", 1, 0, LOPT_PXE_PROMT },
{ "pxe-service", 1, 0, LOPT_PXE_SERV },
{ "test", 0, 0, LOPT_TEST },
{ "tag-if", 1, 0, LOPT_TAG_IF },
{ "dhcp-proxy", 2, 0, LOPT_PROXY },
{ "dhcp-generate-names", 2, 0, LOPT_GEN_NAMES },
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ NULL, 0, 0, 0 }
};
@@ -239,18 +251,19 @@ static struct {
{ 'F', ARG_DUP, "ipaddr,ipaddr,time", gettext_noop("Enable DHCP in the range given with lease duration."), NULL },
{ 'g', ARG_ONE, "groupname", gettext_noop("Change to this group after startup (defaults to %s)."), CHGRP },
{ 'G', ARG_DUP, "<hostspec>", gettext_noop("Set address or hostname for a specified machine."), NULL },
{ LOPT_DHCP_HOST, ARG_ONE, "<filename>", gettext_noop("Read DHCP host specs from file"), NULL },
{ LOPT_DHCP_OPTS, ARG_ONE, "<filename>", gettext_noop("Read DHCP option specs from file"), NULL },
{ LOPT_DHCP_HOST, ARG_ONE, "<filename>", gettext_noop("Read DHCP host specs from file."), NULL },
{ LOPT_DHCP_OPTS, ARG_ONE, "<filename>", gettext_noop("Read DHCP option specs from file."), NULL },
{ LOPT_TAG_IF, ARG_DUP, "tag-expression", gettext_noop("Evaluate conditional tag expression."), NULL },
{ 'h', OPT_NO_HOSTS, NULL, gettext_noop("Do NOT load %s file."), HOSTSFILE },
{ 'H', ARG_DUP, "path", gettext_noop("Specify a hosts file to be read in addition to %s."), HOSTSFILE },
{ 'i', ARG_DUP, "interface", gettext_noop("Specify interface(s) to listen on."), NULL },
{ 'I', ARG_DUP, "int", gettext_noop("Specify interface(s) NOT to listen on.") , NULL },
{ 'j', ARG_DUP, "<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
{ LOPT_CIRCUIT, ARG_DUP, "<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
{ LOPT_REMOTE, ARG_DUP, "<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
{ LOPT_SUBSCR, ARG_DUP, "<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
{ 'J', ARG_DUP, "=<id>[,<id>]", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
{ LOPT_BROADCAST, ARG_DUP, "=<id>[,<id>]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
{ 'j', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP user class to tag."), NULL },
{ LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 circuit-id to tag."), NULL },
{ LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 remote-id to tag."), NULL },
{ LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 subscriber-id to tag."), NULL },
{ 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with tag set."), NULL },
{ LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force broadcast replies for hosts with tag set."), NULL },
{ 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, do NOT run in debug mode."), NULL },
{ 'K', OPT_AUTHORITATIVE, NULL, gettext_noop("Assume we are the only DHCP server on the local network."), NULL },
{ 'l', ARG_ONE, "path", gettext_noop("Specify where to store DHCP leases (defaults to %s)."), LEASEFILE },
@@ -274,8 +287,9 @@ static struct {
{ 't', ARG_ONE, "host_name", gettext_noop("Specify default target in an MX record."), NULL },
{ 'T', ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for replies from /etc/hosts."), NULL },
{ LOPT_NEGTTL, ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for negative caching."), NULL },
{ LOPT_MAXTTL, ARG_ONE, "time", gettext_noop("Specify time-to-live in seconds for maximum TTL to send to clients."), NULL },
{ 'u', ARG_ONE, "username", gettext_noop("Change to this user after startup. (defaults to %s)."), CHUSER },
{ 'U', ARG_DUP, "<id>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
{ 'U', ARG_DUP, "set:<tag>,<class>", gettext_noop("Map DHCP vendor class to tag."), NULL },
{ 'v', 0, NULL, gettext_noop("Display dnsmasq version and copyright information."), NULL },
{ 'V', ARG_DUP, "addr,addr,mask", gettext_noop("Translate IPv4 addresses from upstream servers."), NULL },
{ 'W', ARG_DUP, "name,target,...", gettext_noop("Specify a SRV record."), NULL },
@@ -290,8 +304,8 @@ static struct {
{ 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
{ '1', OPT_DBUS, NULL, gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
{ '2', ARG_DUP, "interface", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
{ '3', ARG_DUP, "[=<id>[,<id>]]", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
{ '4', ARG_DUP, "<id>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
{ '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
{ LOPT_BRIDGE, ARG_DUP, "iface,alias,..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
{ '6', ARG_ONE, "path", gettext_noop("Script to run on DHCP lease creation and destruction."), NULL },
@@ -300,10 +314,10 @@ static struct {
{ '9', OPT_LEASE_RO, NULL, gettext_noop("Do not use leasefile."), NULL },
{ '0', ARG_ONE, "<queries>", gettext_noop("Maximum number of concurrent DNS queries. (defaults to %s)"), "!" },
{ LOPT_RELOAD, OPT_RELOAD, NULL, gettext_noop("Clear DNS cache when reloading %s."), RESOLVFILE },
{ LOPT_NO_NAMES, ARG_DUP, "[=<id>[,<id>]]", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
{ LOPT_NO_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Ignore hostnames provided by DHCP clients."), NULL },
{ LOPT_OVERRIDE, OPT_NO_OVERRIDE, NULL, gettext_noop("Do NOT reuse filename and server fields for extra DHCP options."), NULL },
{ LOPT_TFTP, OPT_TFTP, NULL, gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ LOPT_PREFIX, ARG_ONE, "<directory>", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_TFTP, ARG_DUP, "[=<interface>]", gettext_noop("Enable integrated read-only TFTP server."), NULL },
{ LOPT_PREFIX, ARG_ONE, "<dir>[,<iface>]", gettext_noop("Export files by TFTP only from the specified subtree."), NULL },
{ LOPT_APREF, OPT_TFTP_APREF, NULL, gettext_noop("Add client IP address to tftp-root."), NULL },
{ LOPT_SECURE, OPT_TFTP_SECURE, NULL, gettext_noop("Allow access only to files owned by the user running dnsmasq."), NULL },
{ LOPT_TFTP_MAX, ARG_ONE, "<connections>", gettext_noop("Maximum number of conncurrent TFTP transfers (defaults to %s)."), "#" },
@@ -312,13 +326,17 @@ static struct {
{ LOPT_LOG_OPTS, OPT_LOG_OPTS, NULL, gettext_noop("Extra logging for DHCP."), NULL },
{ LOPT_MAX_LOGS, ARG_ONE, "[=<log lines>]", gettext_noop("Enable async. logging; optionally set queue length."), NULL },
{ LOPT_REBIND, OPT_NO_REBIND, NULL, gettext_noop("Stop DNS rebinding. Filter private IP ranges when resolving."), NULL },
{ LOPT_LOC_REBND, OPT_LOCAL_REBIND, NULL, gettext_noop("Allow rebinding of 127.0.0.0/8, for RBL servers."), NULL },
{ LOPT_NO_REBIND, ARG_DUP, "/domain/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
{ LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
{ LOPT_MATCH, ARG_DUP, "<netid>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
{ LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
{ LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
{ LOPT_SCRIPTUSR, ARG_ONE, "<username>", gettext_noop("Run lease-change script as this user."), NULL },
{ LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
{ LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
{ LOPT_DHCP_FQDN, OPT_DHCP_FQDN, NULL, gettext_noop("Use only fully qualified domain names for DHCP clients."), NULL },
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]...", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ip_address>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
@@ -519,9 +537,9 @@ static char *split_chr(char *s, char c)
p = comma;
*comma = ' ';
for (; isspace((int)*comma); comma++);
for (; *comma == ' '; comma++);
for (; (p >= s) && isspace((int)*p); p--)
for (; (p >= s) && *p == ' '; p--)
*p = 0;
return comma;
@@ -637,7 +655,7 @@ static void do_usage(void)
sprintf(buff, " ");
sprintf(buff+4, "--%s%s%s", opts[j].name, eq, desc);
printf("%-36.36s", buff);
printf("%-40.40s", buff);
if (usage[i].arg)
{
@@ -663,6 +681,22 @@ static void display_opts(void)
printf("%3d %s\n", opttab[i].val, opttab[i].name);
}
static int is_tag_prefix(char *arg)
{
if (arg && (strstr(arg, "net:") == arg || strstr(arg, "tag:") == arg))
return 1;
return 0;
}
static char *set_prefix(char *arg)
{
if (strstr(arg, "set:") == arg)
return arg+4;
return arg;
}
/* This is too insanely large to keep in-line in the switch */
static char *parse_dhcp_opt(char *arg, int flags)
{
@@ -730,11 +764,11 @@ static char *parse_dhcp_opt(char *arg, int flags)
else
{
new->netid = opt_malloc(sizeof (struct dhcp_netid));
/* allow optional "net:" for consistency */
if (strstr(arg, "net:") == arg)
/* allow optional "net:" or "tag:" for consistency */
if (is_tag_prefix(arg))
new->netid->net = opt_string_alloc(arg+4);
else
new->netid->net = opt_string_alloc(arg);
new->netid->net = opt_string_alloc(set_prefix(arg));
new->netid->next = np;
np = new->netid;
}
@@ -1121,7 +1155,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
case '8': /* --log-facility */
/* may be a filename */
if (strchr(arg, '/'))
if (strchr(arg, '/') || strcmp (arg, "-") == 0)
daemon->log_file = opt_string_alloc(arg);
else
{
@@ -1391,21 +1425,26 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
} while (arg);
break;
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
case 'S': /* --server */
case LOPT_LOCAL: /* --local */
case 'A': /* --address */
case LOPT_NO_REBIND: /* --rebind-domain-ok */
{
struct server *serv, *newlist = NULL;
unhide_metas(arg);
if (arg && *arg == '/')
if (arg && (*arg == '/' || option == LOPT_NO_REBIND))
{
char *end;
arg++;
while ((end = split_chr(arg, '/')))
int rebind = !(*arg == '/');
char *end = NULL;
if (!rebind)
arg++;
while (rebind || (end = split_chr(arg, '/')))
{
char *domain = NULL;
/* elide leading dots - they are implied in the search algorithm */
while (*arg == '.') arg++;
/* # matches everything and becomes a zero length domain string */
if (strcmp(arg, "#") == 0)
domain = "";
@@ -1418,6 +1457,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
serv->domain = domain;
serv->flags = domain ? SERV_HAS_DOMAIN : SERV_FOR_NODOTS;
arg = end;
if (rebind)
break;
}
if (!newlist)
{
@@ -1438,10 +1479,20 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
if (!(newlist->flags & SERV_TYPE))
option = '?';
}
else if (option == LOPT_NO_REBIND)
newlist->flags |= SERV_NO_REBIND;
if (!arg || !*arg)
{
newlist->flags |= SERV_NO_ADDR; /* no server */
if (!(newlist->flags & SERV_NO_REBIND))
newlist->flags |= SERV_NO_ADDR; /* no server */
if (newlist->flags & SERV_LITERAL_ADDRESS)
option = '?';
}
else if (strcmp(arg, "#") == 0)
{
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
if (newlist->flags & SERV_LITERAL_ADDRESS)
option = '?';
}
@@ -1590,12 +1641,15 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
case 'T': /* --local-ttl */
case LOPT_NEGTTL: /* --neg-ttl */
case LOPT_MAXTTL: /* --max-ttl */
{
int ttl;
if (!atoi_check(arg, &ttl))
option = '?';
else if (option == LOPT_NEGTTL)
daemon->neg_ttl = (unsigned long)ttl;
else if (option == LOPT_MAXTTL)
daemon->max_ttl = (unsigned long)ttl;
else
daemon->local_ttl = (unsigned long)ttl;
break;
@@ -1609,13 +1663,35 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
#endif
#ifdef HAVE_TFTP
case LOPT_TFTP: /* --enable-tftp */
if (arg)
{
struct interface_list *new = opt_malloc(sizeof(struct interface_list));
new->interface = opt_string_alloc(arg);
new->next = daemon->tftp_interfaces;
daemon->tftp_interfaces = new;
}
else
daemon->tftp_unlimited = 1;
break;
case LOPT_TFTP_MAX: /* --tftp-max */
if (!atoi_check(arg, &daemon->tftp_max))
option = '?';
break;
case LOPT_PREFIX: /* --tftp-prefix */
daemon->tftp_prefix = opt_string_alloc(arg);
comma = split(arg);
if (comma)
{
struct tftp_prefix *new = opt_malloc(sizeof(struct tftp_prefix));
new->interface = opt_string_alloc(comma);
new->prefix = opt_string_alloc(arg);
new->next = daemon->if_prefix;
daemon->if_prefix = new;
}
else
daemon->tftp_prefix = opt_string_alloc(arg);
break;
case LOPT_TFTPPORTS: /* --tftp-port-range */
@@ -1679,7 +1755,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->netid.net = NULL;
new->filter = NULL;
new->flags = 0;
new->interface = NULL;
gen_prob = _("bad dhcp-range");
if (!arg)
@@ -1696,7 +1773,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
if (*cp != ',' && (comma = split(arg)))
{
if (strstr(arg, "net:") == arg)
if (strstr(arg, "interface:") == arg)
new->interface = opt_string_alloc(arg+10);
else if (is_tag_prefix(arg))
{
struct dhcp_netid *tt = opt_malloc(sizeof (struct dhcp_netid));
tt->net = opt_string_alloc(arg+4);
@@ -1706,7 +1785,9 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
else
{
if (new->netid.net)
problem = _("only one netid tag allowed");
problem = _("only one tag allowed");
else if (strstr(arg, "set:") == arg)
new->netid.net = opt_string_alloc(arg+4);
else
new->netid.net = opt_string_alloc(arg);
}
@@ -1814,7 +1895,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->next = daemon->dhcp_conf;
new->flags = (option == LOPT_BANK) ? CONFIG_BANK : 0;
new->hwaddr = NULL;
new->netid = NULL;
if ((a[0] = arg))
for (k = 1; k < 6; k++)
if (!(a[k] = split(a[k-1])))
@@ -1851,15 +1933,17 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
}
}
}
else if (strstr(arg, "net:") == arg)
/* dhcp-host has strange backwards-compat needs. */
else if (strstr(arg, "net:") == arg || strstr(arg, "set:") == arg)
{
int len = strlen(arg + 4) + 1;
if ((new->netid.net = opt_malloc(len)))
{
new->flags |= CONFIG_NETID;
strcpy(new->netid.net, arg+4);
unhide_metas(new->netid.net);
}
struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
newtag->net = opt_malloc(strlen(arg + 4) + 1);
newlist->next = new->netid;
new->netid = newlist;
newlist->list = newtag;
strcpy(newtag->net, arg+4);
unhide_metas(newtag->net);
}
else
{
@@ -1943,6 +2027,72 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
daemon->dhcp_conf = new;
break;
}
case LOPT_TAG_IF: /* --tag-if */
{
struct tag_if *new = opt_malloc(sizeof(struct tag_if));
new->tag = NULL;
new->set = NULL;
new->next = NULL;
/* preserve order */
if (!daemon->tag_if)
daemon->tag_if = new;
else
{
struct tag_if *tmp;
for (tmp = daemon->tag_if; tmp->next; tmp = tmp->next);
tmp->next = new;
}
while (arg)
{
size_t len;
comma = split(arg);
len = strlen(arg);
if (len < 5)
{
new->set = NULL;
break;
}
else
{
struct dhcp_netid *newtag = opt_malloc(sizeof(struct dhcp_netid));
newtag->net = opt_malloc(len - 3);
strcpy(newtag->net, arg+4);
unhide_metas(newtag->net);
if (strstr(arg, "set:") == arg)
{
struct dhcp_netid_list *newlist = opt_malloc(sizeof(struct dhcp_netid_list));
newlist->next = new->set;
new->set = newlist;
newlist->list = newtag;
}
else if (strstr(arg, "tag:") == arg)
{
newtag->next = new->tag;
new->tag = newtag;
}
else
{
new->set = NULL;
break;
}
}
arg = comma;
}
if (!new->set)
problem = _("bad tag-if");
break;
}
case 'O': /* --dhcp-option */
case LOPT_FORCE: /* --dhcp-option-force */
@@ -1957,7 +2107,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
case 'M': /* --dhcp-boot */
{
struct dhcp_netid *id = NULL;
while (arg && strstr(arg, "net:") == arg)
while (is_tag_prefix(arg))
{
struct dhcp_netid *newid = opt_malloc(sizeof(struct dhcp_netid));
newid->next = id;
@@ -2011,8 +2161,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->netid = NULL;
new->opt = 10; /* PXE_MENU_PROMPT */
while (arg && strstr(arg, "net:") == arg)
{
while (is_tag_prefix(arg))
{
struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
comma = split(arg);
nn->next = new->netid;
@@ -2057,7 +2207,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->netid = NULL;
new->server.s_addr = 0;
while (arg && strstr(arg, "net:") == arg)
while (is_tag_prefix(arg))
{
struct dhcp_netid *nn = opt_malloc(sizeof (struct dhcp_netid));
comma = split(arg);
@@ -2133,10 +2283,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
else
{
struct dhcp_mac *new = opt_malloc(sizeof(struct dhcp_mac));
if (strstr(arg, "net:") == arg)
new->netid.net = opt_string_alloc(arg+4);
else
new->netid.net = opt_string_alloc(arg);
new->netid.net = opt_string_alloc(set_prefix(arg));
unhide_metas(comma);
new->hwaddr_len = parse_hex(comma, new->hwaddr, DHCP_CHADDR_MAX, &new->mask, &new->hwaddr_type);
new->next = daemon->dhcp_macs;
@@ -2158,10 +2305,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
char *p;
int dig = 0;
struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor));
if (strstr(arg, "net:") == arg)
new->netid.net = opt_string_alloc(arg+4);
else
new->netid.net = opt_string_alloc(arg);
new->netid.net = opt_string_alloc(set_prefix(arg));
/* check for hex string - must digits may include : must not have nothing else,
only allowed for agent-options. */
for (p = comma; *p; p++)
@@ -2227,7 +2371,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
case 'J': /* --dhcp-ignore */
case LOPT_NO_NAMES: /* --dhcp-ignore-names */
case LOPT_BROADCAST: /* --dhcp-broadcast */
case '3': /* --bootp-dynamic */
case '3': /* --bootp-dynamic */
case LOPT_GEN_NAMES: /* --dhcp-generate-names */
{
struct dhcp_netid_list *new = opt_malloc(sizeof(struct dhcp_netid_list));
struct dhcp_netid *list = NULL;
@@ -2246,6 +2391,11 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->next = daemon->bootp_dynamic;
daemon->bootp_dynamic = new;
}
else if (option == LOPT_GEN_NAMES)
{
new->next = daemon->dhcp_gen_names;
daemon->dhcp_gen_names = new;
}
else
{
new->next = daemon->dhcp_ignore_names;
@@ -2257,7 +2407,7 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
comma = split(arg);
member->next = list;
list = member;
if (strstr(arg, "net:") == arg)
if (is_tag_prefix(arg))
member->net = opt_string_alloc(arg+4);
else
member->net = opt_string_alloc(arg);
@@ -2267,6 +2417,19 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
new->list = list;
break;
}
case LOPT_PROXY: /* --dhcp-proxy */
daemon->override = 1;
while (arg) {
struct addr_list *new = opt_malloc(sizeof(struct addr_list));
comma = split(arg);
if ((new->addr.s_addr = inet_addr(arg)) == (in_addr_t)-1)
problem = _("bad dhcp-proxy address");
new->next = daemon->override_relays;
daemon->override_relays = new;
arg = comma;
}
break;
#endif
case 'V': /* --alias */
@@ -2547,9 +2710,8 @@ static char *one_opt(int option, char *arg, char *gen_prob, int nest)
static void one_file(char *file, int nest, int hard_opt)
{
volatile int lineno = 0;
int i, option;
FILE *f;
char *p, *arg, *start, *buff = daemon->namebuff;
char *buff = daemon->namebuff;
static struct fileread {
dev_t dev;
ino_t ino;
@@ -2595,9 +2757,9 @@ static void one_file(char *file, int nest, int hard_opt)
while (fgets(buff, MAXDNAME, f))
{
int white;
unsigned int lastquote;
char *errmess;
int white, i, option; ;
char *errmess, *p, *arg, *start;
size_t len;
/* Memory allocation failure longjmps here if mem_recover == 1 */
if (hard_opt)
@@ -2612,12 +2774,12 @@ static void one_file(char *file, int nest, int hard_opt)
/* Implement quotes, inside quotes we allow \\ \" \n and \t
metacharacters get hidden also strip comments */
for (white = 1, lastquote = 0, p = buff; *p; p++)
for (white = 1, p = buff; *p; p++)
{
if (*p == '"')
{
memmove(p, p+1, strlen(p+1)+1);
for(; *p && *p != '"'; p++)
{
if (*p == '\\' && strchr("\"tnebr\\", p[1]))
@@ -2636,40 +2798,51 @@ static void one_file(char *file, int nest, int hard_opt)
}
*p = hide_meta(*p);
}
if (*p == '"')
{
memmove(p, p+1, strlen(p+1)+1);
lastquote = p - buff;
}
else
if (*p == 0)
{
errmess = _("missing \"");
goto oops;
}
memmove(p, p+1, strlen(p+1)+1);
}
if (white && *p == '#')
{
*p = 0;
break;
if (isspace(*p))
{
*p = ' ';
white = 1;
}
white = isspace((int)unhide_meta(*p));
else
{
if (white && *p == '#')
{
*p = 0;
break;
}
white = 0;
}
}
/* fgets gets end of line char too. */
while (strlen(buff) > lastquote && isspace((int)unhide_meta(buff[strlen(buff)-1])))
buff[strlen(buff)-1] = 0;
if (*buff == 0)
/* strip leading spaces */
for (start = buff; *start && *start == ' '; start++);
/* strip trailing spaces */
for (len = strlen(start); (len != 0) && (start[len-1] == ' '); len--);
if (len == 0)
continue;
else
start[len] = 0;
if (hard_opt != 0)
arg = buff;
else if ((p=strchr(buff, '=')))
arg = start;
else if ((p=strchr(start, '=')))
{
/* allow spaces around "=" */
arg = p+1;
for (; p >= buff && (isspace((int)*p) || *p == '='); p--)
for (arg = p+1; *arg == ' '; arg++);
for (; p >= start && (*p == ' ' || *p == '='); p--)
*p = 0;
}
else
@@ -2679,9 +2852,6 @@ static void one_file(char *file, int nest, int hard_opt)
option = hard_opt;
else
{
/* skip leading space */
for (start = buff; *start && isspace((int)*start); start++);
for (option = 0, i = 0; opts[i].name; i++)
if (strcmp(opts[i].name, start) == 0)
{
@@ -2698,12 +2868,7 @@ static void one_file(char *file, int nest, int hard_opt)
}
if (!errmess)
{
if (arg)
for (; isspace((int)*arg); arg++);
errmess = one_opt(option, arg, _("error"), nest + 1);
}
errmess = one_opt(option, arg, _("error"), nest + 1);
if (errmess)
{
@@ -2716,7 +2881,7 @@ static void one_file(char *file, int nest, int hard_opt)
}
}
mem_recover = 1;
mem_recover = 0;
fclose(f);
}
@@ -2735,16 +2900,24 @@ void reread_dhcp(void)
if (configs->flags & CONFIG_BANK)
{
struct hwaddr_config *mac, *tmp;
struct dhcp_netid_list *list, *tmplist;
for (mac = configs->hwaddr; mac; mac = tmp)
{
tmp = mac->next;
free(mac);
}
if (configs->flags & CONFIG_CLID)
free(configs->clid);
if (configs->flags & CONFIG_NETID)
free(configs->netid.net);
for (list = configs->netid; list; list = tmplist)
{
free(list->list);
tmplist = list->next;
free(list);
}
if (configs->flags & CONFIG_NAME)
free(configs->hostname);
+65 -22
View File
@@ -513,26 +513,31 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
/* is addr in the non-globally-routed IP space? */
static int private_net(struct in_addr addr)
static int private_net(struct in_addr addr, int ban_localhost)
{
in_addr_t ip_addr = ntohl(addr.s_addr);
return
((ip_addr & 0xFF000000) == 0x7F000000) /* 127.0.0.0/8 (loopback) */ ||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen)
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen, char *name)
{
int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--)
{
if (!(p = skip_name(p, header, qlen, 10)))
if (name && (daemon->options & OPT_LOG))
{
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0;
}
else if (!(p = skip_name(p, header, qlen, 10)))
return 0; /* bad packet */
GETSHORT(qtype, p);
@@ -540,15 +545,15 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
GETLONG(ttl, p);
GETSHORT(rdlen, p);
if ((qclass == C_IN) && (qtype == T_A))
if (qclass == C_IN && qtype == T_A)
{
struct doctor *doctor;
struct in_addr addr;
if (!CHECK_LEN(header, p, qlen, INADDRSZ))
return 0;
/* alignment */
/* alignment */
memcpy(&addr, p, INADDRSZ);
for (doctor = daemon->doctors; doctor; doctor = doctor->next)
@@ -561,7 +566,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
else if (ntohl(doctor->in.s_addr) > ntohl(addr.s_addr) ||
ntohl(doctor->end.s_addr) < ntohl(addr.s_addr))
continue;
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
@@ -570,6 +575,30 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
break;
}
}
else if (qtype == T_TXT && name && (daemon->options & OPT_LOG))
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, rdlen))
return 0;
while ((p1 - p) < rdlen)
{
unsigned int i, len = *p1;
unsigned char *p2 = p1;
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
if (isprint(*(p2+1)))
{
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_DEBUG, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, len);
*p1 = len;
p1 += len+1;
}
}
if (!ADD_RDLEN(header, p, qlen, rdlen))
return 0; /* bad packet */
@@ -578,7 +607,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
return p;
}
static int find_soa(HEADER *header, size_t qlen)
static int find_soa(HEADER *header, size_t qlen, char *name)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -587,7 +616,7 @@ static int find_soa(HEADER *header, size_t qlen)
/* first move to NS section and find TTL from any SOA section */
if (!(p = skip_questions(header, qlen)) ||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen)))
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name)))
return 0; /* bad packet */
for (i = ntohs(header->nscount); i != 0; i--)
@@ -623,7 +652,7 @@ static int find_soa(HEADER *header, size_t qlen)
}
/* rewrite addresses in additioal section too */
if (!do_doctor(p, ntohs(header->arcount), header, qlen))
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL))
return 0;
if (!found_soa)
@@ -635,8 +664,8 @@ static int find_soa(HEADER *header, size_t qlen)
/* Note that the following code can create CNAME chains that don't point to a real record,
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like parct of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int is_sign, int check_rebind)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -645,11 +674,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
cache_start_insert();
/* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
if (daemon->doctors)
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || (daemon->options & OPT_LOG))
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, name);
}
/* go through the questions. */
@@ -698,6 +727,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -732,7 +766,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
if (ttl)
cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags);
@@ -773,6 +807,11 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -807,9 +846,9 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
memcpy(&addr, p1, addrlen);
/* check for returned address in private space */
if ((daemon->options & OPT_NO_REBIND) &&
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4))
private_net(addr.addr.addr4, !(daemon->options & OPT_LOCAL_REBIND)))
return 1;
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
@@ -833,7 +872,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now)
if (!searched_soa)
{
searched_soa = 1;
ttl = find_soa(header, qlen);
ttl = find_soa(header, qlen, NULL);
}
/* If there's no SOA to get the TTL from, but there is a CNAME
pointing at this, inherit its TTL */
@@ -1120,7 +1159,11 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
if (crecp->flags & (F_IMMORTAL | F_DHCP))
return daemon->local_ttl;
return crecp->ttd - now;
/* Return the Max TTL value if it is lower then the actual TTL */
if (daemon->max_ttl == 0 || ((unsigned)(crecp->ttd - now) < daemon->max_ttl))
return crecp->ttd - now;
else
return daemon->max_ttl;
}
@@ -1302,7 +1345,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
else if (is_arpa == F_IPV4 &&
(daemon->options & OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4))
private_net(addr.addr.addr4, 1))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
ans = 1;
+210 -130
View File
@@ -116,11 +116,12 @@ static void do_options(struct dhcp_context *context,
struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxearch,
unsigned char *uuid);
unsigned char *uuid,
int vendor_class_len);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
static void do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
static int do_encap_opts(struct dhcp_opt *opts, int encap, int flag, struct dhcp_packet *mess, unsigned char *end, int null_term);
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid);
static int prune_vendor_opts(struct dhcp_netid *netid);
static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local);
@@ -145,14 +146,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
char *message = NULL;
unsigned int time;
struct dhcp_config *config;
struct dhcp_netid *netid;
struct dhcp_netid *netid, *tagif_netid;
struct in_addr subnet_addr, fallback, override;
unsigned short fuzz = 0;
unsigned int mess_type = 0;
unsigned char fqdn_flags = 0;
unsigned char *agent_id = NULL, *uuid = NULL;
unsigned char *emac = NULL;
int emac_len = 0;
int vendor_class_len = 0, emac_len = 0;
struct dhcp_netid known_id, iface_id, cpewan_id;
struct dhcp_opt *o;
unsigned char pxe_uuid[17];
@@ -174,12 +175,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* check for DHCP rather than BOOTP */
if ((opt = option_find(mess, sz, OPTION_MESSAGE_TYPE, 1)))
{
mess_type = option_uint(opt, 0, 1);
u32 cookie = htonl(DHCP_COOKIE);
/* only insist on a cookie for DHCP. */
if (*((u32 *)&mess->options) != htonl(DHCP_COOKIE))
if (memcmp(mess->options, &cookie, sizeof(u32)) != 0)
return 0;
mess_type = option_uint(opt, 0, 1);
/* two things to note here: expand_buf may move the packet,
so reassign mess from daemon->packet. Also, the size
sent includes the IP and UDP headers, hence the magic "-28" */
@@ -278,7 +281,6 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
vendor->netid.next = netid;
netid = &vendor->netid;
break;
}
}
}
@@ -388,10 +390,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
strcpy(daemon->namebuff, inet_ntoa(context_tmp->start));
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP subnet: %s/%s"),
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->netmask));
else
my_syslog(MS_DHCP | LOG_INFO, _("%u Available DHCP range: %s -- %s"),
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
}
}
@@ -430,10 +432,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
domain = config->domain;
}
if (have_config(config, CONFIG_NETID))
if (config)
{
config->netid.next = netid;
netid = &config->netid;
struct dhcp_netid_list *list;
for (list = config->netid; list; list = list->next)
{
list->list->next = netid;
netid = list->list;
}
}
/* Match incoming filename field as a netid. */
@@ -452,8 +459,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
bootp_id.next = netid;
netid = &bootp_id;
tagif_netid = run_tag_if(netid);
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0))
if (match_netid(id_list->list, tagif_netid, 0))
message = _("ignored");
if (!message)
@@ -474,7 +483,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else
{
if (!(lease = lease_find_by_client(mess->chaddr, mess->hlen, mess->htype, NULL, 0)) ||
!address_available(context, lease->addr, netid))
!address_available(context, lease->addr, tagif_netid))
{
if (lease)
{
@@ -482,7 +491,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease_prune(lease, now);
lease = NULL;
}
if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, netid, now))
if (!address_allocate(context, &mess->yiaddr, mess->chaddr, mess->hlen, tagif_netid, now))
message = _("no address available");
}
else
@@ -494,13 +503,14 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
else if (context->netid.net)
{
context->netid.next = netid;
netid = &context->netid;
}
netid = &context->netid;
tagif_netid = run_tag_if(netid);
}
if (!message && !nailed)
{
for (id_list = daemon->bootp_dynamic; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
break;
if (!id_list)
message = _("no address configured");
@@ -526,13 +536,13 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
domain, netid, subnet_addr, 0, 0, 0, NULL);
domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0);
}
}
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
return message ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
return message ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -632,10 +642,15 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
if (have_config(config, CONFIG_NETID))
if (config)
{
config->netid.next = netid;
netid = &config->netid;
struct dhcp_netid_list *list;
for (list = config->netid; list; list = list->next)
{
list->list->next = netid;
netid = list->list;
}
}
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
@@ -732,22 +747,48 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
}
/* mark vendor-encapsulated options which match the client-supplied vendor class */
match_vendor_opts(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->dhcp_opts);
/* mark vendor-encapsulated options which match the client-supplied vendor class,
save client-supplied vendor class */
if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
{
memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
vendor_class_len = option_len(opt);
}
match_vendor_opts(opt, daemon->dhcp_opts);
if (daemon->options & OPT_LOG_OPTS)
{
if (sanitise(option_find(mess, sz, OPTION_VENDOR_ID, 1), daemon->namebuff))
my_syslog(MS_DHCP | LOG_INFO, _("%u Vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
if (sanitise(opt, daemon->namebuff))
my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
my_syslog(MS_DHCP | LOG_INFO, _("%u User class: %s"), ntohl(mess->xid), daemon->namebuff);
my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
}
tagif_netid = run_tag_if(netid);
/* if all the netids in the ignore list are present, ignore this client */
for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0))
if (match_netid(id_list->list, tagif_netid, 0))
ignore = 1;
/* If configured, we can override the server-id to be the address of the relay,
so that all traffic goes via the relay and can pick up agent-id info. This can be
configured for all relays, or by address. */
if (daemon->override && mess->giaddr.s_addr != 0 && override.s_addr == 0)
{
if (!daemon->override_relays)
override = mess->giaddr;
else
{
struct addr_list *l;
for (l = daemon->override_relays; l; l = l->next)
if (l->addr.s_addr == mess->giaddr.s_addr)
break;
if (l)
override = mess->giaddr;
}
}
/* Can have setting to ignore the client ID for a particular MAC address or hostname */
if (have_config(config, CONFIG_NOCLID))
clid = NULL;
@@ -806,7 +847,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
pxe_misc(mess, end, uuid);
prune_vendor_opts(netid);
prune_vendor_opts(tagif_netid);
opt71.val = save71;
opt71.opt = SUBOPT_PXE_BOOT_ITEM;
opt71.len = 4;
@@ -816,7 +857,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);
return dhcp_packet_size(mess, netid, agent_id, real_end);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -827,7 +868,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)) &&
(context->flags & CONTEXT_PROXY))
{
struct dhcp_boot *boot = find_boot(netid);
struct dhcp_boot *boot = find_boot(tagif_netid);
mess->yiaddr.s_addr = 0;
if (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
@@ -853,11 +894,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
pxe_misc(mess, end, uuid);
prune_vendor_opts(netid);
do_encap_opts(pxe_opts(pxearch, netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
prune_vendor_opts(tagif_netid);
do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);
return ignore ? 0 : dhcp_packet_size(mess, netid, agent_id, real_end);
return ignore ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
}
}
}
@@ -908,7 +949,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return 0;
case DHCPRELEASE:
if (!(context = narrow_context(context, mess->ciaddr, netid)) ||
if (!(context = narrow_context(context, mess->ciaddr, tagif_netid)) ||
!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
return 0;
@@ -970,21 +1011,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (conf.s_addr)
mess->yiaddr = conf;
else if (lease &&
address_available(context, lease->addr, netid) &&
address_available(context, lease->addr, tagif_netid) &&
!config_find_by_address(daemon->dhcp_conf, lease->addr))
mess->yiaddr = lease->addr;
else if (opt && address_available(context, addr, netid) && !lease_find_by_addr(addr) &&
else if (opt && address_available(context, addr, tagif_netid) && !lease_find_by_addr(addr) &&
!config_find_by_address(daemon->dhcp_conf, addr))
mess->yiaddr = addr;
else if (emac_len == 0)
message = _("no unique-id");
else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, netid, now))
else if (!address_allocate(context, &mess->yiaddr, emac, emac_len, tagif_netid, now))
message = _("no address available");
}
log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);
if (message || !(context = narrow_context(context, mess->yiaddr, netid)))
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
return 0;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
@@ -993,6 +1034,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
context->netid.next = netid;
netid = &context->netid;
tagif_netid = run_tag_if(netid);
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
@@ -1007,9 +1049,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_T2, 4, (time*7)/8);
}
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
return dhcp_packet_size(mess, netid, agent_id, real_end);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1063,12 +1105,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return 0;
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
{
message = _("wrong address");
/* avoid loops when client brain-dead */
lease_prune(lease, now);
lease = NULL;
}
message = _("wrong address");
}
}
else
@@ -1085,6 +1122,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
/* ensure we broadcast NAK */
unicast_dest = 0;
}
/* desynchronise renewals */
fuzz = rand16();
mess->yiaddr = mess->ciaddr;
@@ -1102,7 +1140,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context->router.s_addr == config->addr.s_addr)
break;
if (!(context = narrow_context(context, mess->yiaddr, netid)))
if (!(context = narrow_context(context, mess->yiaddr, tagif_netid)))
{
/* If a machine moves networks whilst it has a lease, we catch that here. */
message = _("wrong network");
@@ -1111,7 +1149,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
/* Check for renewal of a lease which is outside the allowed range. */
else if (!address_available(context, mess->yiaddr, netid) &&
else if (!address_available(context, mess->yiaddr, tagif_netid) &&
(!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
message = _("address not available");
@@ -1184,64 +1222,84 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
context->netid.next = netid;
netid = &context->netid;
tagif_netid = run_tag_if(netid);
}
#ifdef HAVE_SCRIPT
if (do_classes && daemon->lease_change_command)
{
struct dhcp_netid *n;
if (mess->giaddr.s_addr)
lease->giaddr = mess->giaddr;
lease->changed = 1;
free(lease->extradata);
lease->extradata_size = lease->extradata_len = 0;
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
add_extradata_opt(lease, oui);
add_extradata_opt(lease, serial);
add_extradata_opt(lease, class);
/* space-concat tag set */
if (!netid)
add_extradata_opt(lease, NULL);
else
for (n = netid; n; n = n->next)
add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt, 0);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
add_extradata_data(lease, ucp, len, 0);
}
}
if (do_classes && daemon->lease_change_command)
{
struct dhcp_netid *n;
if (mess->giaddr.s_addr)
lease->giaddr = mess->giaddr;
lease->changed = 1;
free(lease->extradata);
lease->extradata_size = lease->extradata_len = 0;
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
add_extradata_opt(lease, oui);
add_extradata_opt(lease, serial);
add_extradata_opt(lease, class);
/* space-concat tag set */
if (!tagif_netid)
add_extradata_opt(lease, NULL);
else
for (n = tagif_netid; n; n = n->next)
add_extradata_data(lease, (unsigned char *)n->net, strlen(n->net), n->next ? ' ' : 0);
if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
{
int len = option_len(opt);
unsigned char *ucp = option_ptr(opt, 0);
/* If the user-class option started as counted strings, the first byte will be zero. */
if (len != 0 && ucp[0] == 0)
ucp++, len--;
add_extradata_data(lease, ucp, len, 0);
}
}
#endif
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
{
if (!hostname_auth && (client_hostname = host_from_dns(mess->yiaddr)))
{
domain = get_domain(mess->yiaddr);
hostname = client_hostname;
hostname_auth = 1;
}
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
lease_set_hwaddr(lease, mess->chaddr, clid, mess->hlen, mess->htype, clid_len);
/* if all the netids in the ignore_name list are present, ignore client-supplied name */
if (!hostname_auth)
{
for (id_list = daemon->dhcp_ignore_names; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
break;
if (id_list)
hostname = NULL;
}
/* Last ditch, if configured, generate hostname from mac address */
if (!hostname && emac_len != 0)
{
for (id_list = daemon->dhcp_gen_names; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, tagif_netid, 0))
break;
if (id_list)
{
int i;
hostname = daemon->dhcp_buff;
/* buffer is 256 bytes, 3 bytes per octet */
for (i = 0; (i < emac_len) && (i < 80); i++)
hostname += sprintf(hostname, "%.2x%s", emac[i], (i == emac_len - 1) ? "" : "-");
hostname = daemon->dhcp_buff;
}
}
if (hostname)
lease_set_hostname(lease, hostname, hostname_auth);
@@ -1267,10 +1325,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
}
return dhcp_packet_size(mess, netid, agent_id, real_end);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1282,7 +1340,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
return 0;
/* For DHCPINFORM only, cope without a valid context */
context = narrow_context(context, mess->ciaddr, netid);
context = narrow_context(context, mess->ciaddr, tagif_netid);
/* Find a least based on IP address if we didn't
get one from MAC address/client-d */
@@ -1291,8 +1349,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
lease->hostname)
hostname = lease->hostname;
if (!hostname)
hostname = host_from_dns(mess->ciaddr);
if (!hostname && (hostname = host_from_dns(mess->ciaddr)))
domain = get_domain(mess->ciaddr);
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
@@ -1300,6 +1358,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
context->netid.next = netid;
netid = &context->netid;
tagif_netid = run_tag_if(netid);
}
if (lease)
@@ -1325,10 +1384,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid);
domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
*is_inform = 1; /* handle reply differently */
return dhcp_packet_size(mess, netid, agent_id, real_end);
return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
}
return 0;
@@ -1722,23 +1781,28 @@ static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *neti
*p++ = OPTION_END;
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
break;
if (id_list)
mess->flags |= htons(0x8000); /* force broadcast */
if (daemon->options & OPT_LOG_OPTS)
{
if (mess->siaddr.s_addr != 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u next server: %s"), ntohl(mess->xid), inet_ntoa(mess->siaddr));
if ((mess->flags & htons(0x8000)) && mess->ciaddr.s_addr == 0)
my_syslog(MS_DHCP | LOG_INFO, _("%u broadcast response"), ntohl(mess->xid));
log_options(&mess->options[0] + sizeof(u32), mess->xid);
}
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
if (match_netid(id_list->list, netid, 0))
mess->flags |= htons(0x8000); /* force broadcast */
ret = (size_t)(p - (unsigned char *)mess);
if (ret < MIN_PACKETSZ)
ret = MIN_PACKETSZ;
return ret;
}
@@ -1877,10 +1941,16 @@ static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *
struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
if (match_netid(tmp->netid, netid, netid ? 0 : 1))
if (match_netid(tmp->netid, netid, 0))
return tmp;
return netid ? option_find2(NULL, opts, opt) : NULL;
/* No match, look for one without a netid */
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt && !(tmp->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)))
if (match_netid(tmp->netid, netid, 1))
return tmp;
return NULL;
}
/* mark vendor-encapsulated options which match the client-supplied or
@@ -1905,10 +1975,10 @@ static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt)
}
}
static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
struct dhcp_packet *mess, unsigned char *end, int null_term)
static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
struct dhcp_packet *mess, unsigned char *end, int null_term)
{
int len, enc_len;
int len, enc_len, ret = 0;
struct dhcp_opt *start;
unsigned char *p;
@@ -1917,6 +1987,7 @@ static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
if (opt->flags & flag)
{
int new = do_opt(opt, NULL, NULL, null_term) + 2;
ret = 1;
if (enc_len + new <= 255)
enc_len += new;
else
@@ -1948,6 +2019,8 @@ static void do_encap_opts(struct dhcp_opt *opt, int encap, int flag,
}
*p = OPTION_END;
}
return ret;
}
static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char *uuid)
@@ -2009,7 +2082,7 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
/* create the data for the PXE_MENU and PXE_SERVERS options. */
p = (unsigned char *)daemon->dhcp_buff;
q = (unsigned char *)daemon->dhcp_buff2;
q = (unsigned char *)daemon->dhcp_buff3;
for (i = 0, service = daemon->pxe_services; service; service = service->next)
if (pxe_arch == service->CSA && match_netid(service->netid, netid, 1))
@@ -2037,7 +2110,7 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
if (boot_server.s_addr != 0)
{
if (q - (unsigned char *)daemon->dhcp_buff2 + 3 + INADDRSZ >= 253)
if (q - (unsigned char *)daemon->dhcp_buff3 + 3 + INADDRSZ >= 253)
goto toobig;
/* Boot service with known address - give it */
@@ -2066,11 +2139,11 @@ static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct
ret->val = (unsigned char *)daemon->dhcp_buff;
ret->opt = SUBOPT_PXE_MENU;
if (q - (unsigned char *)daemon->dhcp_buff2 != 0)
if (q - (unsigned char *)daemon->dhcp_buff3 != 0)
{
ret = &fake_opts[j--];
ret->len = q - (unsigned char *)daemon->dhcp_buff2;
ret->val = (unsigned char *)daemon->dhcp_buff2;
ret->len = q - (unsigned char *)daemon->dhcp_buff3;
ret->val = (unsigned char *)daemon->dhcp_buff3;
ret->opt = SUBOPT_PXE_SERVERS;
}
}
@@ -2130,7 +2203,8 @@ static void do_options(struct dhcp_context *context,
struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxe_arch,
unsigned char *uuid)
unsigned char *uuid,
int vendor_class_len)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot;
@@ -2138,6 +2212,7 @@ static void do_options(struct dhcp_context *context,
int i, len, force_encap = 0;
unsigned char f0 = 0, s0 = 0;
int done_file = 0, done_server = 0;
int done_vendor_class = 0;
if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, hostname);
@@ -2203,7 +2278,7 @@ static void do_options(struct dhcp_context *context,
as an internal way to specify siaddr without using dhcp-boot, for use in
dhcp-optsfile. */
{
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) && mess->file[0] == 0 &&
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
(opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
{
strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
@@ -2373,7 +2448,10 @@ static void do_options(struct dhcp_context *context,
/* If we send a vendor-id, revisit which vendor-ops we consider
it appropriate to send. */
if (optno == OPTION_VENDOR_ID)
match_vendor_opts(p - 2, config_opts);
{
match_vendor_opts(p - 2, config_opts);
done_vendor_class = 1;
}
}
}
@@ -2442,20 +2520,22 @@ static void do_options(struct dhcp_context *context,
}
}
/* Must precede pxe_opts, since it overwrites req_options */
force_encap = prune_vendor_opts(netid);
if (in_list(req_options, OPTION_VENDOR_CLASS_OPT))
force_encap = 1;
if (context && pxe_arch != -1)
{
pxe_misc(mess, end, uuid);
config_opts = pxe_opts(pxe_arch, netid, context->local);
}
if (force_encap)
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term);
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
/* If we send vendor encapsulated options, and haven't already sent option 60,
echo back the value we got from the client. */
memcpy(p, daemon->dhcp_buff3, vendor_class_len);
/* restore BOOTP anti-overload hack */
if (!req_options || (daemon->options & OPT_NO_OVERRIDE))
{
+90 -36
View File
@@ -18,7 +18,7 @@
#ifdef HAVE_TFTP
static struct tftp_file *check_tftp_fileperm(ssize_t *len);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err_oops(char *packet, char *file);
@@ -47,14 +47,21 @@ void tftp_request(struct listener *listen, time_t now)
struct msghdr msg;
struct iovec iov;
struct ifreq ifr;
int is_err = 1, if_index = 0, mtu = 0;
int is_err = 1, if_index = 0, mtu = 0, special = 0;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
struct tftp_transfer *transfer;
int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtuflag = IP_PMTUDISC_DONT;
#endif
char namebuff[IF_NAMESIZE];
char *name;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
union {
struct cmsghdr align; /* this ensures alignment */
#if defined(HAVE_LINUX_NETWORK)
@@ -87,54 +94,101 @@ void tftp_request(struct listener *listen, time_t now)
{
addr = listen->iface->addr.in;
mtu = listen->iface->mtu;
name = listen->iface->name;
}
else
{
char name[IF_NAMESIZE];
struct cmsghdr *cmptr;
int check;
struct interface_list *ir;
addr.sin_addr.s_addr = 0;
#if defined(HAVE_LINUX_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
{
addr.sin_addr = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
#elif defined(HAVE_SOLARIS_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *((struct in_addr *)CMSG_DATA(cmptr));
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *((unsigned int *)CMSG_DATA(cmptr));
{
union {
unsigned char *c;
struct in_addr *a;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *(p.i);
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *((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;
{
union {
unsigned char *c;
struct in_addr *a;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = p.s->sdl_index;
}
#endif
if (!indextoname(listen->tftpfd, if_index, name) ||
addr.sin_addr.s_addr == 0 ||
!iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index))
if (!indextoname(listen->tftpfd, if_index, namebuff) ||
addr.sin_addr.s_addr == 0)
return;
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return;
name = namebuff;
check = iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index);
/* wierd TFTP service override */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
break;
if (!ir)
{
if (!daemon->tftp_unlimited || !check)
return;
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return;
#endif
}
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
}
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
/* wierd TFTP interfaces disable special options. */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
special = 1;
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -202,7 +256,7 @@ void tftp_request(struct listener *listen, time_t now)
if (strcasecmp(opt, "blksize") == 0)
{
if ((opt = next(&p, end)) &&
!(daemon->options & OPT_TFTP_NOBLOCK))
(special || !(daemon->options & OPT_TFTP_NOBLOCK)))
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
@@ -228,15 +282,15 @@ void tftp_request(struct listener *listen, time_t now)
*p = '/';
strcpy(daemon->namebuff, "/");
if (daemon->tftp_prefix)
if (prefix)
{
if (daemon->tftp_prefix[0] == '/')
if (prefix[0] == '/')
daemon->namebuff[0] = 0;
strncat(daemon->namebuff, daemon->tftp_prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
if (daemon->tftp_prefix[strlen(daemon->tftp_prefix)-1] != '/')
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
if (prefix[strlen(prefix)-1] != '/')
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
if (daemon->options & OPT_TFTP_APREF)
if (!special && (daemon->options & OPT_TFTP_APREF))
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
@@ -263,7 +317,7 @@ void tftp_request(struct listener *listen, time_t now)
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len)))
if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
{
if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
@@ -285,7 +339,7 @@ void tftp_request(struct listener *listen, time_t now)
}
}
static struct tftp_file *check_tftp_fileperm(ssize_t *len)
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file;
@@ -295,7 +349,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len)
int fd = -1;
/* trick to ban moving out of the subtree */
if (daemon->tftp_prefix && strstr(namebuff, "/../"))
if (prefix && strstr(namebuff, "/../"))
goto perm;
if ((fd = open(namebuff, O_RDONLY)) == -1)
@@ -322,7 +376,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len)
goto perm;
}
/* in secure mode, must be owned by user running dnsmasq */
else if ((daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
else if (!special && (daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
goto perm;
/* If we're doing many tranfers from the same file, only