import of dnsmasq-2.58.tar.gz
diff --git a/src/bpf.c b/src/bpf.c
index 9a77426..cffcf67 100644
--- a/src/bpf.c
+++ b/src/bpf.c
@@ -35,6 +35,13 @@
#include <net/if_dl.h>
#include <netinet/if_ether.h>
+#ifndef SA_SIZE
+#define SA_SIZE(sa) \
+ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
+ sizeof(long) : \
+ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
+#endif
+
int arp_enumerate(void *parm, int (*callback)())
{
int mib[6];
diff --git a/src/cache.c b/src/cache.c
index 77c1972..11ccc0d 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -922,6 +922,21 @@
}
#ifdef HAVE_DHCP
+struct in_addr a_record_from_hosts(char *name, time_t now)
+{
+ struct crec *crecp = NULL;
+ struct in_addr ret;
+
+ while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
+ if (crecp->flags & F_HOSTS)
+ return *(struct in_addr *)&crecp->addr;
+
+ my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
+
+ ret.s_addr = 0;
+ return ret;
+}
+
void cache_unhash_dhcp(void)
{
struct crec *cache, **up;
diff --git a/src/config.h b/src/config.h
index 7394253..a988770 100644
--- a/src/config.h
+++ b/src/config.h
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define VERSION "2.57"
+#define VERSION "2.58"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
@@ -134,6 +134,12 @@
included when internationalisation support is built, using the
*-i18n makefile targets, even if HAVE_IDN is not explicitly set.
+HAVE_CONNTRACK
+ define this to include code which propogates conntrack marks from
+ incoming DNS queries to the corresponding upstream queries. This adds
+ a build-dependency on libnetfilter_conntrack, but the resulting binary will
+ still run happily on a kernel without conntrack support.
+
NOTES:
For Linux you should define
HAVE_LINUX_NETWORK
@@ -159,6 +165,7 @@
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
+/* #define HAVE_CONNTRACK */
/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
#ifdef NO_TFTP
diff --git a/src/conntrack.c b/src/conntrack.c
new file mode 100644
index 0000000..4142c51
--- /dev/null
+++ b/src/conntrack.c
@@ -0,0 +1,90 @@
+/* dnsmasq is Copyright (c) 2000-2011 Simon Kelley
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_CONNTRACK
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static int gotit = 0; /* yuck */
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
+
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
+{
+ struct nf_conntrack *ct;
+ struct nfct_handle *h;
+
+ gotit = 0;
+
+ if ((ct = nfct_new()))
+ {
+ nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
+ nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
+
+#ifdef HAVE_IPV6
+ if (peer_addr->sa.sa_family == AF_INET6)
+ {
+ nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
+ nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
+ nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
+ nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
+ }
+ else
+#endif
+ {
+ nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
+ nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
+ nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
+ nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
+ }
+
+
+ if ((h = nfct_open(CONNTRACK, 0)))
+ {
+ nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);
+ if (nfct_query(h, NFCT_Q_GET, ct) == -1)
+ {
+ static int warned = 0;
+ if (!warned)
+ {
+ my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
+ warned = 1;
+ }
+ }
+ nfct_close(h);
+ }
+ nfct_destroy(ct);
+ }
+
+ return gotit;
+}
+
+static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
+{
+ unsigned int *ret = (unsigned int *)data;
+ *ret = nfct_get_attr_u32(ct, ATTR_MARK);
+ (void)type; /* eliminate warning */
+ gotit = 1;
+
+ return NFCT_CB_CONTINUE;
+}
+
+#endif
+
+
+
diff --git a/src/dhcp.c b/src/dhcp.c
index 29ddf24..28a3dac 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -19,7 +19,6 @@
#ifdef HAVE_DHCP
struct iface_param {
- struct in_addr relay, primary;
struct dhcp_context *current;
int ind;
};
@@ -269,8 +268,6 @@
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
- parm.relay = mess->giaddr;
- parm.primary = iface_addr;
parm.current = NULL;
parm.ind = iface_index;
@@ -299,7 +296,7 @@
return;
lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
- now, unicast_dest, &is_inform, pxe_fd);
+ now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now);
lease_update_dns();
@@ -448,38 +445,27 @@
context->netmask = netmask;
}
- if (context->netmask.s_addr)
+ if (context->netmask.s_addr != 0 &&
+ is_same_net(local, context->start, context->netmask) &&
+ is_same_net(local, context->end, context->netmask))
{
- if (is_same_net(local, context->start, context->netmask) &&
- is_same_net(local, context->end, context->netmask))
+ /* link it onto the current chain if we've not seen it before */
+ if (if_index == param->ind && context->current == context)
{
- /* link it onto the current chain if we've not seen it before */
- if (if_index == param->ind && context->current == context)
- {
- context->router = local;
- context->local = local;
- context->current = param->current;
- param->current = context;
- }
-
- if (!(context->flags & CONTEXT_BRDCAST))
- {
- if (is_same_net(broadcast, context->start, context->netmask))
- context->broadcast = broadcast;
- else
- context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
- }
- }
- else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
+ context->router = local;
+ context->local = local;
+ context->current = param->current;
+ param->current = context;
+ }
+
+ if (!(context->flags & CONTEXT_BRDCAST))
{
- context->router = param->relay;
- context->local = param->primary;
- /* fill in missing broadcast addresses for relayed ranges */
- if (!(context->flags & CONTEXT_BRDCAST))
+ if (is_same_net(broadcast, context->start, context->netmask))
+ context->broadcast = broadcast;
+ else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
-
- }
+ }
}
return 1;
@@ -505,7 +491,7 @@
start = ntohl(tmp->start.s_addr);
end = ntohl(tmp->end.s_addr);
- if (!(tmp->flags & CONTEXT_STATIC) &&
+ if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
addr >= start &&
addr <= end &&
match_netid(tmp->filter, netids, 1))
@@ -540,7 +526,8 @@
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
- is_same_net(taddr, tmp->start, tmp->netmask))
+ is_same_net(taddr, tmp->start, tmp->netmask) &&
+ !(tmp->flags & CONTEXT_PROXY))
break;
}
@@ -626,16 +613,22 @@
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
- if (c->flags & CONTEXT_STATIC)
+ if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
else
{
- /* pick a seed based on hwaddr then iterate until we find a free address. */
- start.s_addr = addr.s_addr =
- htonl(ntohl(c->start.s_addr) +
- ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+ if (option_bool(OPT_CONSEC_ADDR))
+ /* seed is largest extant lease addr in this context */
+ start = lease_find_max_addr(c);
+ else
+ /* pick a seed based on hwaddr */
+ start.s_addr = htonl(ntohl(c->start.s_addr) +
+ ((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
+
+ /* iterate until we find a free address. */
+ addr = start;
do {
/* eliminate addresses in use by the server. */
@@ -660,9 +653,6 @@
*addrp = addr;
- if (option_bool(OPT_NO_PING))
- return 1;
-
/* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
@@ -672,33 +662,51 @@
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */
- else if (++count == max || r->addr.s_addr == addr.s_addr)
- return 1;
-
- if (icmp_ping(addr))
- /* address in use: perturb address selection so that we are
- less likely to try this address again. */
- c->addr_epoch++;
- else
+ else
+ {
+ count++;
+ if (r->addr.s_addr == addr.s_addr)
+ {
+ /* consec-ip mode: we offered this address for another client
+ (different hash) recently, don't offer it to this one. */
+ if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
+ break;
+
+ return 1;
+ }
+ }
+
+ if (!r)
{
- /* at this point victim may hold an expired record */
- if (!victim)
+ if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
{
- if ((victim = whine_malloc(sizeof(struct ping_result))))
+ /* address in use: perturb address selection so that we are
+ less likely to try this address again. */
+ if (!option_bool(OPT_CONSEC_ADDR))
+ c->addr_epoch++;
+ }
+ else
+ {
+ /* at this point victim may hold an expired record */
+ if (!victim)
{
- victim->next = daemon->ping_results;
- daemon->ping_results = victim;
+ if ((victim = whine_malloc(sizeof(struct ping_result))))
+ {
+ victim->next = daemon->ping_results;
+ daemon->ping_results = victim;
+ }
}
+
+ /* record that this address is OK for 30s
+ without more ping checks */
+ if (victim)
+ {
+ victim->addr = addr;
+ victim->time = now;
+ victim->hash = j;
+ }
+ return 1;
}
-
- /* record that this address is OK for 30s
- without more ping checks */
- if (victim)
- {
- victim->addr = addr;
- victim->time = now;
- }
- return 1;
}
}
@@ -709,6 +717,7 @@
} while (addr.s_addr != start.s_addr);
}
+
return 0;
}
diff --git a/src/dns_protocol.h b/src/dns_protocol.h
index bc18e79..456a997 100644
--- a/src/dns_protocol.h
+++ b/src/dns_protocol.h
@@ -56,7 +56,7 @@
u16 id;
u8 hb3,hb4;
u16 qdcount,ancount,nscount,arcount;
-} ;
+};
#define HB3_QR 0x80
#define HB3_OPCODE 0x78
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index 827b0dc..821b7f0 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -40,7 +40,7 @@
#ifndef LOCALEDIR
"no-"
#endif
-"I18N "
+"i18n "
#ifndef HAVE_DHCP
"no-"
#endif
@@ -52,6 +52,10 @@
"no-"
#endif
"TFTP "
+#ifndef HAVE_CONNTRACK
+"no-"
+#endif
+"conntrack "
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
"no-"
#endif
@@ -148,6 +152,14 @@
die(_("TFTP server not available: set HAVE_TFTP in src/config.h"), NULL, EC_BADCONF);
#endif
+#ifdef HAVE_CONNTRACK
+ if (option_bool(OPT_CONNTRACK) && (daemon->query_port != 0 || daemon->osport))
+ die (_("Cannot use --conntrack AND --query-port"), NULL, EC_BADCONF);
+#else
+ if (option_bool(OPT_CONNTRACK))
+ die(_("Conntrack support not available: set HAVE_CONNTRACK in src/config.h"), NULL, EC_BADCONF);
+#endif
+
#ifdef HAVE_SOLARIS_NETWORK
if (daemon->max_logs != 0)
die(_("asychronous logging is not available under Solaris"), NULL, EC_BADCONF);
@@ -1147,9 +1159,6 @@
unsigned char *buff;
struct server *s;
int flags;
- struct in_addr dst_addr_4;
-
- dst_addr_4.s_addr = 0;
#ifndef NO_FORK
/* Arrange for SIGALARM after CHILD_LIFETIME seconds to
@@ -1168,10 +1177,7 @@
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
- if (listener->family == AF_INET)
- dst_addr_4 = iface->addr.in.sin_addr;
-
- buff = tcp_request(confd, now, dst_addr_4, iface->netmask);
+ buff = tcp_request(confd, now, &iface->addr, iface->netmask);
shutdown(confd, SHUT_RDWR);
close(confd);
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index a386a31..3bf7a44 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -202,7 +202,9 @@
#define OPT_NO_REBIND 31
#define OPT_ADD_MAC 32
#define OPT_DNSSEC 33
-#define OPT_LAST 34
+#define OPT_CONSEC_ADDR 34
+#define OPT_CONNTRACK 35
+#define OPT_LAST 36
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -309,7 +311,6 @@
#define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
-#define F_NSRR (1u<<20)
/* struct sockaddr is not large enough to hold any address,
@@ -517,9 +518,10 @@
#define DHOPT_HEX 512
#define DHOPT_VENDOR_MATCH 1024
#define DHOPT_RFC3925 2048
+#define DHOPT_TAGOK 4096
struct dhcp_boot {
- char *file, *sname;
+ char *file, *sname, *tftp_sname;
struct in_addr next_server;
struct dhcp_netid *netid;
struct dhcp_boot *next;
@@ -585,6 +587,7 @@
struct ping_result {
struct in_addr addr;
time_t time;
+ unsigned int hash;
struct ping_result *next;
};
@@ -742,6 +745,7 @@
time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(void);
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
+struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
@@ -813,7 +817,7 @@
void reply_query(int fd, int family, time_t now);
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);
+ union mysockaddr *local_addr, struct in_addr netmask);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
@@ -875,6 +879,7 @@
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
+struct in_addr lease_find_max_addr(struct dhcp_context *context);
void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(void);
int do_script_run(time_t now);
@@ -884,7 +889,7 @@
/* rfc2131.c */
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
- size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd);
+ size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd, struct in_addr fallback);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
#endif
@@ -938,3 +943,9 @@
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
#endif
+
+/* conntrack.c */
+#ifdef HAVE_CONNTRACK
+int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
+ int istcp, unsigned int *markp);
+#endif
diff --git a/src/forward.c b/src/forward.c
index 92bc6b0..bdf6bfb 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -207,10 +207,10 @@
}
}
- if (flags == 0 && !(qtype & F_NSRR) &&
+ if (flags == 0 && !(qtype & F_QUERY) &&
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
- /* don't forward simple names, make exception for NS queries and empty name. */
- flags = F_NXDOMAIN;
+ /* don't forward A or AAAA queries for simple names, except the empty name */
+ flags = F_NOERR;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR;
@@ -243,7 +243,7 @@
unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL;
-
+
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
@@ -366,6 +366,16 @@
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
+
+#ifdef HAVE_CONNTRACK
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (option_bool(OPT_CONNTRACK))
+ {
+ unsigned int mark;
+ if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
+ setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+ }
+#endif
}
if (sendto(fd, (char *)header, plen, 0,
@@ -797,7 +807,7 @@
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
unsigned char *tcp_request(int confd, time_t now,
- struct in_addr local_addr, struct in_addr netmask)
+ union mysockaddr *local_addr, struct in_addr netmask)
{
size_t size = 0;
int norebind = 0;
@@ -809,7 +819,13 @@
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
struct dns_header *header;
struct server *last_server;
+ struct in_addr dst_addr_4;
+ union mysockaddr peer_addr;
+ socklen_t peer_len = sizeof(union mysockaddr);
+ if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
+ return packet;
+
while (1)
{
if (!packet ||
@@ -831,29 +847,28 @@
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{
- union mysockaddr peer_addr;
- socklen_t peer_len = sizeof(union mysockaddr);
+ char types[20];
- if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
- {
- char types[20];
-
- querystr(types, qtype);
-
- if (peer_addr.sa.sa_family == AF_INET)
- log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&peer_addr.in.sin_addr, types);
+ querystr(types, qtype);
+
+ if (peer_addr.sa.sa_family == AF_INET)
+ log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
- else
- log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&peer_addr.in6.sin6_addr, types);
+ else
+ log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif
- }
}
+ if (local_addr->sa.sa_family == AF_INET)
+ dst_addr_4 = local_addr->in.sin_addr;
+ else
+ dst_addr_4.s_addr = 0;
+
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
- local_addr, netmask, now);
+ dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
@@ -866,14 +881,8 @@
char *domain = NULL;
if (option_bool(OPT_ADD_MAC))
- {
- union mysockaddr peer_addr;
- socklen_t peer_len = sizeof(union mysockaddr);
-
- if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
- size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
- }
-
+ size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
+
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
@@ -907,18 +916,38 @@
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
-
- if ((last_server->tcpfd == -1) &&
- (last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
- (!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
- connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+
+ if (last_server->tcpfd == -1)
{
- close(last_server->tcpfd);
- last_server->tcpfd = -1;
+ if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
+ continue;
+
+ if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
+ connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
+ {
+ close(last_server->tcpfd);
+ last_server->tcpfd = -1;
+ continue;
+ }
+
+#ifdef HAVE_CONNTRACK
+ /* Copy connection mark of incoming query to outgoing connection. */
+ if (option_bool(OPT_CONNTRACK))
+ {
+ unsigned int mark;
+ struct all_addr local;
+#ifdef HAVE_IPV6
+ if (local_addr->sa.sa_family == AF_INET6)
+ local.addr.addr6 = local_addr->in6.sin6_addr;
+ else
+#endif
+ local.addr.addr4 = local_addr->in.sin_addr;
+
+ if (get_incoming_mark(&peer_addr, &local, 1, &mark))
+ setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
+ }
+#endif
}
-
- if (last_server->tcpfd == -1)
- continue;
c1 = size >> 8;
c2 = size;
diff --git a/src/lease.c b/src/lease.c
index cfa7543..0945495 100644
--- a/src/lease.c
+++ b/src/lease.c
@@ -323,6 +323,21 @@
return NULL;
}
+/* Find largest assigned address in context */
+struct in_addr lease_find_max_addr(struct dhcp_context *context)
+{
+ struct dhcp_lease *lease;
+ struct in_addr addr = context->start;
+
+ if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
+ for (lease = leases; lease; lease = lease->next)
+ if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
+ ((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
+ ((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
+ addr = lease->addr;
+
+ return addr;
+}
struct dhcp_lease *lease_allocate(struct in_addr addr)
{
diff --git a/src/log.c b/src/log.c
index baaae7c..08ce62b 100644
--- a/src/log.c
+++ b/src/log.c
@@ -154,6 +154,19 @@
while (entries)
{
+ /* The data in the payoad is written with a terminating zero character
+ and the length reflects this. For a stream connection we need to
+ send the zero as a record terminator, but this isn't done for a
+ datagram connection, so treat the length as one less than reality
+ to elide the zero. If we're logging to a file, turn the zero into
+ a newline, and leave the length alone. */
+ int len_adjust = 0;
+
+ if (log_to_file)
+ entries->payload[entries->offset + entries->length - 1] = '\n';
+ else if (connection_type == SOCK_DGRAM)
+ len_adjust = 1;
+
/* Avoid duplicates over a fork() */
if (entries->pid != getpid())
{
@@ -163,11 +176,11 @@
connection_good = 1;
- if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
+ if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
{
entries->length -= rc;
entries->offset += rc;
- if (entries->length == 0)
+ if (entries->length == len_adjust)
{
free_entry();
if (entries_lost != 0)
@@ -366,10 +379,6 @@
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0;
entry->pid = pid;
-
- /* replace terminator with \n */
- if (log_to_file)
- entry->payload[entry->length - 1] = '\n';
}
/* almost always, logging won't block, so try and write this now,
diff --git a/src/netlink.c b/src/netlink.c
index f6da7db..df3585f 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -222,7 +222,7 @@
}
if (addrp)
- if (!((*callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
+ if (!((*callback)(addrp, ifa->ifa_scope, ifa->ifa_index, parm)))
return 0;
}
#endif
diff --git a/src/network.c b/src/network.c
index 7b2e905..fe5557a 100644
--- a/src/network.c
+++ b/src/network.c
@@ -896,20 +896,38 @@
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
- else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
- {
+ else
+ {
+ int scope_index = 0;
+ char *scope_id = strchr(token, '%');
+
+ if (scope_id)
+ {
+ *(scope_id++) = 0;
+ scope_index = if_nametoindex(scope_id);
+ }
+
+ if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
+ {
#ifdef HAVE_SOCKADDR_SA_LEN
- source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
+ source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
#endif
- source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
- addr.in6.sin6_port = htons(NAMESERVER_PORT);
- source_addr.in6.sin6_addr = in6addr_any;
- source_addr.in6.sin6_port = htons(daemon->query_port);
+ source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
+ source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
+ addr.in6.sin6_port = htons(NAMESERVER_PORT);
+ addr.in6.sin6_scope_id = scope_index;
+ source_addr.in6.sin6_addr = in6addr_any;
+ source_addr.in6.sin6_port = htons(daemon->query_port);
+ source_addr.in6.sin6_scope_id = 0;
+ }
+ else
+ continue;
}
-#endif /* IPV6 */
+#else /* IPV6 */
else
continue;
-
+#endif
+
if (old_servers)
{
serv = old_servers;
diff --git a/src/option.c b/src/option.c
index 4cee0a2..7e8f47a 100644
--- a/src/option.c
+++ b/src/option.c
@@ -110,6 +110,8 @@
#define LOPT_LOC_REBND 299
#define LOPT_ADD_MAC 300
#define LOPT_DNSSEC 301
+#define LOPT_INCR_ADDR 302
+#define LOPT_CONNTRACK 303
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -225,6 +227,8 @@
{ "rebind-localhost-ok", 0, 0, LOPT_LOC_REBND },
{ "add-mac", 0, 0, LOPT_ADD_MAC },
{ "proxy-dnssec", 0, 0, LOPT_DNSSEC },
+ { "dhcp-sequential-ip", 0, 0, LOPT_INCR_ADDR },
+ { "conntrack", 0, 0, LOPT_CONNTRACK },
{ NULL, 0, 0, 0 }
};
@@ -345,8 +349,10 @@
{ 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 },
{ LOPT_TEST, 0, NULL, gettext_noop("Check configuration syntax."), NULL },
- { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries"), NULL },
- { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers"), NULL },
+ { LOPT_ADD_MAC, OPT_ADD_MAC, NULL, gettext_noop("Add requestor's MAC address to forwarded DNS queries."), NULL },
+ { LOPT_DNSSEC, OPT_DNSSEC, NULL, gettext_noop("Proxy DNSSEC validation results from upstream nameservers."), NULL },
+ { LOPT_INCR_ADDR, OPT_CONSEC_ADDR, NULL, gettext_noop("Attempt to allocate sequential IP addresses to DHCP clients."), NULL },
+ { LOPT_CONNTRACK, OPT_CONNTRACK, NULL, gettext_noop("Copy connection-track mark from queries to upstream connections."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -1590,6 +1596,10 @@
{
int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source;
+#ifdef HAVE_IPV6
+ int scope_index = 0;
+ char *scope_id;
+#endif
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) &&
@@ -1600,6 +1610,10 @@
!atoi_check16(portno, &serv_port))
problem = _("bad port");
+#ifdef HAVE_IPV6
+ scope_id = split_chr(arg, '%');
+#endif
+
if ((newlist->addr.in.sin_addr.s_addr = inet_addr(arg)) != (in_addr_t) -1)
{
newlist->addr.in.sin_port = htons(serv_port);
@@ -1627,16 +1641,22 @@
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &newlist->addr.in6.sin6_addr) > 0)
{
+ if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
+ problem = _("bad interface name");
+
newlist->addr.in6.sin6_port = htons(serv_port);
+ newlist->addr.in6.sin6_scope_id = scope_index;
newlist->source_addr.in6.sin6_port = htons(source_port);
+ newlist->source_addr.in6.sin6_scope_id = 0;
newlist->addr.sa.sa_family = newlist->source_addr.sa.sa_family = AF_INET6;
+ newlist->addr.in6.sin6_flowinfo = newlist->source_addr.in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
newlist->addr.in6.sin6_len = newlist->source_addr.in6.sin6_len = sizeof(newlist->addr.in6);
#endif
if (source)
{
- newlist->flags |= SERV_HAS_SOURCE;
- if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
+ newlist->flags |= SERV_HAS_SOURCE;
+ if (inet_pton(AF_INET6, source, &newlist->source_addr.in6.sin6_addr) == 0)
{
#if defined(SO_BINDTODEVICE)
newlist->source_addr.in6.sin6_addr = in6addr_any;
@@ -1652,7 +1672,6 @@
#endif
else
option = '?'; /* error */
-
}
serv = newlist;
@@ -1842,6 +1861,7 @@
new->netmask.s_addr = 0;
new->broadcast.s_addr = 0;
new->router.s_addr = 0;
+ new->local.s_addr = 0;
new->netid.net = NULL;
new->filter = NULL;
new->flags = 0;
@@ -2037,7 +2057,9 @@
strcpy(newtag->net, arg+4);
unhide_metas(newtag->net);
}
- else
+ else if (strstr(arg, "tag:") == arg)
+ problem = _("cannot match tags in --dhcp-host");
+ else
{
struct hwaddr_config *newhw = opt_malloc(sizeof(struct hwaddr_config));
if ((newhw->hwaddr_len = parse_hex(a[j], newhw->hwaddr, DHCP_CHADDR_MAX,
@@ -2218,7 +2240,7 @@
option = '?';
else
{
- char *dhcp_file, *dhcp_sname = NULL;
+ char *dhcp_file, *dhcp_sname = NULL, *tftp_sname = NULL;
struct in_addr dhcp_next_server;
comma = split(arg);
dhcp_file = opt_string_alloc(arg);
@@ -2231,8 +2253,17 @@
if (comma)
{
unhide_metas(comma);
- if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
- option = '?';
+ if ((dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1) {
+
+ /*
+ * The user may have specified the tftp hostname here.
+ * save it so that it can be resolved/looked up during
+ * actual dhcp_reply().
+ */
+
+ tftp_sname = opt_string_alloc(comma);
+ dhcp_next_server.s_addr = 0;
+ }
}
}
if (option != '?')
@@ -2240,6 +2271,7 @@
struct dhcp_boot *new = opt_malloc(sizeof(struct dhcp_boot));
new->file = dhcp_file;
new->sname = dhcp_sname;
+ new->tftp_sname = tftp_sname;
new->next_server = dhcp_next_server;
new->netid = id;
new->next = daemon->boot_config;
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 889c1f0..25cbeec 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -642,7 +642,6 @@
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
{
int i, qtype, qclass, rdlen;
- unsigned long ttl;
for (i = count; i != 0; i--)
{
@@ -656,7 +655,7 @@
GETSHORT(qtype, p);
GETSHORT(qclass, p);
- GETLONG(ttl, p);
+ p += 4; /* ttl */
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A)
@@ -1044,8 +1043,6 @@
return F_IPV6;
if (qtype == T_ANY)
return F_IPV4 | F_IPV6;
- if (qtype == T_NS || qtype == T_SOA)
- return F_QUERY | F_NSRR;
}
return F_QUERY;
@@ -1101,12 +1098,17 @@
struct txt_record *txt;
struct interface_name *intr;
struct ptr_record *ptr;
-
- if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
+ struct naptr *naptr;
+
+ if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
- for (mx = daemon->mxnames; mx; mx = mx->next)
+ for (naptr = daemon->naptr; naptr; naptr = naptr->next)
+ if (hostname_isequal(name, naptr->name))
+ return 1;
+
+ for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name))
return 1;
@@ -1309,11 +1311,11 @@
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
- unsigned short udpsz, ext_rcode, flags;
+ unsigned short udpsz, flags;
unsigned char *psave = pheader;
GETSHORT(udpsz, pheader);
- GETSHORT(ext_rcode, pheader);
+ pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
diff --git a/src/rfc2131.c b/src/rfc2131.c
index ed9af7f..b96b197 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -41,8 +41,8 @@
int mac_len, char *interface, char *string, u32 xid);
static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
- unsigned char *agent_id, unsigned char *real_end);
+static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess);
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
static void do_options(struct dhcp_context *context,
struct dhcp_packet *mess,
@@ -51,11 +51,12 @@
char *hostname,
char *domain, char *config_domain,
struct dhcp_netid *netid,
- struct in_addr subnet_addr,
+ struct in_addr subnet_addr,
unsigned char fqdn_flags,
int null_term, int pxearch,
unsigned char *uuid,
- int vendor_class_len);
+ int vendor_class_len,
+ time_t now);
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
@@ -67,7 +68,7 @@
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
- size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe)
+ size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
{
unsigned char *opt, *clid = NULL;
struct dhcp_lease *ltmp, *lease = NULL;
@@ -85,7 +86,7 @@
unsigned int time;
struct dhcp_config *config;
struct dhcp_netid *netid, *tagif_netid;
- struct in_addr subnet_addr, fallback, override;
+ struct in_addr subnet_addr, override;
unsigned short fuzz = 0;
unsigned int mess_type = 0;
unsigned char fqdn_flags = 0;
@@ -297,17 +298,43 @@
if (!context_new)
for (context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
- if (context_tmp->netmask.s_addr &&
- is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
- is_same_net(addr, context_tmp->end, context_tmp->netmask))
- {
- context_tmp->current = context_new;
- context_new = context_tmp;
- }
+ {
+ struct in_addr netmask = context_tmp->netmask;
+
+ /* guess the netmask for relayed networks */
+ if (!(context_tmp->flags & CONTEXT_NETMASK) && context_tmp->netmask.s_addr == 0)
+ {
+ if (IN_CLASSA(ntohl(context_tmp->start.s_addr)) && IN_CLASSA(ntohl(context_tmp->end.s_addr)))
+ netmask.s_addr = htonl(0xff000000);
+ else if (IN_CLASSB(ntohl(context_tmp->start.s_addr)) && IN_CLASSB(ntohl(context_tmp->end.s_addr)))
+ netmask.s_addr = htonl(0xffff0000);
+ else if (IN_CLASSC(ntohl(context_tmp->start.s_addr)) && IN_CLASSC(ntohl(context_tmp->end.s_addr)))
+ netmask.s_addr = htonl(0xffffff00);
+ }
+
+ /* This section fills in context mainly when a client which is on a remote (relayed)
+ network renews a lease without using the relay, after dnsmasq has restarted. */
+ if (netmask.s_addr != 0 &&
+ is_same_net(addr, context_tmp->start, netmask) &&
+ is_same_net(addr, context_tmp->end, netmask))
+ {
+ context_tmp->netmask = netmask;
+ if (context_tmp->local.s_addr == 0)
+ context_tmp->local = fallback;
+ if (context_tmp->router.s_addr == 0)
+ context_tmp->router = mess->giaddr;
+
+ /* fill in missing broadcast addresses for relayed ranges */
+ if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
+ context_tmp->broadcast.s_addr = context_tmp->start.s_addr | ~context_tmp->netmask.s_addr;
+
+ context_tmp->current = context_new;
+ context_new = context_tmp;
+ }
+ }
if (context_new || force)
- context = context_new;
-
+ context = context_new;
}
if (!context)
@@ -318,9 +345,6 @@
return 0;
}
- /* keep _a_ local address available. */
- fallback = context->local;
-
if (option_bool(OPT_LOG_OPTS))
{
struct dhcp_context *context_tmp;
@@ -441,9 +465,10 @@
else if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if(&context->netid);
}
+
+ log_tags(tagif_netid, mess);
if (!message && !nailed)
{
@@ -474,13 +499,13 @@
clear_packet(mess, end);
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
- domain, tagif_netid, subnet_addr, 0, 0, 0, NULL, 0);
+ domain, netid, subnet_addr, 0, 0, 0, NULL, 0, now);
}
}
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);
- return message ? 0 : dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))
@@ -795,7 +820,8 @@
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, tagif_netid, agent_id, real_end);
+ log_tags(tagif_netid, mess);
+ return dhcp_packet_size(mess, agent_id, real_end);
}
if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
@@ -829,8 +855,10 @@
and set discovery_control = 8 */
if (boot)
{
- if (boot->next_server.s_addr)
+ if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
+ else if (boot->tftp_sname)
+ mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
if (boot->file)
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
@@ -844,7 +872,8 @@
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, tagif_netid, agent_id, real_end);
+ log_tags(tagif_netid, mess);
+ return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
}
}
}
@@ -975,15 +1004,16 @@
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);
-
if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if(&context->netid);
}
-
+
+ log_tags(tagif_netid, mess);
+
+ log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);
+
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
clear_packet(mess, end);
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPOFFER);
@@ -996,9 +1026,9 @@
option_put(mess, end, OPTION_T2, 4, (time*7)/8);
}
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
case DHCPREQUEST:
if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1029,12 +1059,30 @@
if (!context)
{
- /* In auth mode, a REQUEST sent to the wrong server
- should be faulted, so that the client establishes
- communication with us, otherwise, silently ignore. */
- if (!option_bool(OPT_AUTHORITATIVE))
- return 0;
- message = _("wrong server-ID");
+ /* Handle very strange configs where clients have more than one route to the server.
+ If a clients idea of its server-id matches any of our DHCP interfaces, we let it pass.
+ Have to set override to make sure we echo back the correct server-id */
+ struct irec *intr;
+
+ enumerate_interfaces();
+
+ for (intr = daemon->interfaces; intr; intr = intr->next)
+ if (intr->addr.sa.sa_family == AF_INET &&
+ intr->addr.in.sin_addr.s_addr == option_addr(opt).s_addr &&
+ intr->tftp_ok)
+ break;
+
+ if (intr)
+ override = intr->addr.in.sin_addr;
+ else
+ {
+ /* In auth mode, a REQUEST sent to the wrong server
+ should be faulted, so that the client establishes
+ communication with us, otherwise, silently ignore. */
+ if (!option_bool(OPT_AUTHORITATIVE))
+ return 0;
+ message = _("wrong server-ID");
+ }
}
}
@@ -1173,9 +1221,10 @@
if (context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if( &context->netid);
}
+
+ log_tags(tagif_netid, mess);
#ifdef HAVE_SCRIPT
if (do_classes && daemon->lease_change_command)
@@ -1278,10 +1327,10 @@
option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
}
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
case DHCPINFORM:
if (ignore || have_config(config, CONFIG_DISABLE))
@@ -1304,15 +1353,16 @@
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);
if (context && context->netid.net)
{
context->netid.next = netid;
- netid = &context->netid;
- tagif_netid = run_tag_if(netid);
+ tagif_netid = run_tag_if( &context->netid);
}
+
+ log_tags(tagif_netid, mess);
+
+ log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);
if (lease)
{
@@ -1337,10 +1387,10 @@
}
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
- domain, tagif_netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len);
+ domain, netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);
*is_inform = 1; /* handle reply differently */
- return dhcp_packet_size(mess, tagif_netid, agent_id, real_end);
+ return dhcp_packet_size(mess, agent_id, real_end);
}
return 0;
@@ -1432,7 +1482,7 @@
{
if (override.s_addr != 0)
return override;
- else if (context)
+ else if (context && context->local.s_addr != 0)
return context->local;
else
return fallback;
@@ -1672,30 +1722,16 @@
return NULL;
}
-static size_t dhcp_packet_size(struct dhcp_packet *mess, struct dhcp_netid *netid,
- unsigned char *agent_id, unsigned char *real_end)
+static void log_tags(struct dhcp_netid *netid, struct dhcp_packet *mess)
{
- unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
- unsigned char *overload;
- size_t ret;
- struct dhcp_netid_list *id_list;
- struct dhcp_netid *n;
-
- /* move agent_id back down to the end of the packet */
- if (agent_id)
- {
- memmove(p, agent_id, real_end - agent_id);
- p += real_end - agent_id;
- memset(p, 0, real_end - p); /* in case of overlap */
- }
-
- /* We do logging too */
if (netid && option_bool(OPT_LOG_OPTS))
{
char *s = daemon->namebuff;
for (*s = 0; netid; netid = netid->next)
{
/* kill dupes. */
+ struct dhcp_netid *n;
+
for (n = netid->next; n; n = n->next)
if (strcmp(netid->net, n->net) == 0)
break;
@@ -1709,7 +1745,22 @@
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), ntohl(mess->xid), s);
}
-
+}
+
+static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end)
+{
+ unsigned char *p = dhcp_skip_opts(&mess->options[0] + sizeof(u32));
+ unsigned char *overload;
+ size_t ret;
+
+ /* move agent_id back down to the end of the packet */
+ if (agent_id)
+ {
+ memmove(p, agent_id, real_end - agent_id);
+ p += real_end - agent_id;
+ memset(p, 0, real_end - p); /* in case of overlap */
+ }
+
/* add END options to the regions. */
overload = find_overload(mess);
@@ -1734,12 +1785,6 @@
*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 (option_bool(OPT_LOG_OPTS))
{
if (mess->siaddr.s_addr != 0)
@@ -1889,20 +1934,14 @@
return 0;
}
-static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
+static struct dhcp_opt *option_find2(int 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, 0))
- return tmp;
-
- /* 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;
-
+ struct dhcp_opt *opts;
+
+ for (opts = daemon->dhcp_opts; opts; opts = opts->next)
+ if (opts->opt == opt && (opts->flags & DHOPT_TAGOK))
+ return opts;
+
return NULL;
}
@@ -2157,7 +2196,8 @@
unsigned char fqdn_flags,
int null_term, int pxe_arch,
unsigned char *uuid,
- int vendor_class_len)
+ int vendor_class_len,
+ time_t now)
{
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
struct dhcp_boot *boot;
@@ -2166,7 +2206,53 @@
unsigned char f0 = 0, s0 = 0;
int done_file = 0, done_server = 0;
int done_vendor_class = 0;
+ struct dhcp_netid *tagif;
+ struct dhcp_netid_list *id_list;
+ /* flag options which are valid with the current tag set (sans context tags) */
+ tagif = run_tag_if(netid);
+ for (opt = config_opts; opt; opt = opt->next)
+ {
+ opt->flags &= ~DHOPT_TAGOK;
+ if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
+ match_netid(opt->netid, tagif, 0))
+ opt->flags |= DHOPT_TAGOK;
+ }
+
+ /* now flag options which are valid, including the context tags,
+ otherwise valid options are inhibited if we found a higher priotity one above */
+ if (context && context->netid.net)
+ {
+ context->netid.next = netid;
+ tagif = run_tag_if(&context->netid);
+
+ for (opt = config_opts; opt; opt = opt->next)
+ if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
+ match_netid(opt->netid, tagif, 0))
+ {
+ struct dhcp_opt *tmp;
+ for (tmp = config_opts; tmp; tmp = tmp->next)
+ if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
+ break;
+ if (!tmp)
+ opt->flags |= DHOPT_TAGOK;
+ }
+ }
+
+ /* now flag untagged options which are not overridden by tagged ones */
+ for (opt = config_opts; opt; opt = opt->next)
+ if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
+ {
+ struct dhcp_opt *tmp;
+ for (tmp = config_opts; tmp; tmp = tmp->next)
+ if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
+ break;
+ if (!tmp)
+ opt->flags |= DHOPT_TAGOK;
+ else if (!tmp->netid)
+ my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
+ }
+
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);
@@ -2191,6 +2277,12 @@
}
}
+ 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 (context)
mess->siaddr = context->local;
@@ -2200,7 +2292,7 @@
provide an manual option to disable it.
Some PXE ROMs have bugs (surprise!) and need zero-terminated
names, so we always send those. */
- if ((boot = find_boot(netid)))
+ if ((boot = find_boot(tagif)))
{
if (boot->sname)
{
@@ -2222,8 +2314,10 @@
strncpy((char *)mess->file, boot->file, sizeof(mess->file)-1);
}
- if (boot->next_server.s_addr)
+ if (boot->next_server.s_addr)
mess->siaddr = boot->next_server;
+ else if (boot->tftp_sname)
+ mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
}
else
/* Use the values of the relevant options if no dhcp-boot given and
@@ -2232,20 +2326,20 @@
dhcp-optsfile. */
{
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
- (opt = option_find2(netid, config_opts, OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
+ (opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
{
strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file)-1);
done_file = 1;
}
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
- (opt = option_find2(netid, config_opts, OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
+ (opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
{
strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname)-1);
done_server = 1;
}
- if ((opt = option_find2(netid, config_opts, OPTION_END)))
+ if ((opt = option_find2(OPTION_END)))
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
}
@@ -2273,36 +2367,36 @@
/* replies to DHCPINFORM may not have a valid context */
if (context)
{
- if (!option_find2(netid, config_opts, OPTION_NETMASK))
+ if (!option_find2(OPTION_NETMASK))
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
/* May not have a "guessed" broadcast address if we got no packets via a relay
from this net yet (ie just unicast renewals after a restart */
if (context->broadcast.s_addr &&
- !option_find2(netid, config_opts, OPTION_BROADCAST))
+ !option_find2(OPTION_BROADCAST))
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
/* Same comments as broadcast apply, and also may not be able to get a sensible
default when using subnet select. User must configure by steam in that case. */
if (context->router.s_addr &&
in_list(req_options, OPTION_ROUTER) &&
- !option_find2(netid, config_opts, OPTION_ROUTER))
+ !option_find2(OPTION_ROUTER))
option_put(mess, end, OPTION_ROUTER, INADDRSZ, ntohl(context->router.s_addr));
if (in_list(req_options, OPTION_DNSSERVER) &&
- !option_find2(netid, config_opts, OPTION_DNSSERVER))
+ !option_find2(OPTION_DNSSERVER))
option_put(mess, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
}
if (domain && in_list(req_options, OPTION_DOMAINNAME) &&
- !option_find2(netid, config_opts, OPTION_DOMAINNAME))
+ !option_find2(OPTION_DOMAINNAME))
option_put_string(mess, end, OPTION_DOMAINNAME, domain, null_term);
/* Note that we ignore attempts to set the fqdn using --dhc-option=81,<name> */
if (hostname)
{
if (in_list(req_options, OPTION_HOSTNAME) &&
- !option_find2(netid, config_opts, OPTION_HOSTNAME))
+ !option_find2(OPTION_HOSTNAME))
option_put_string(mess, end, OPTION_HOSTNAME, hostname, null_term);
if (fqdn_flags != 0)
@@ -2351,6 +2445,10 @@
{
int optno = opt->opt;
+ /* netids match and not encapsulated? */
+ if (!(opt->flags & DHOPT_TAGOK))
+ continue;
+
/* was it asked for, or are we sending it anyway? */
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
continue;
@@ -2369,10 +2467,6 @@
if (optno == OPTION_FILENAME && done_file)
continue;
- /* netids match and not encapsulated? */
- if (opt != option_find2(netid, config_opts, optno))
- continue;
-
/* For the options we have default values on
dhc-option=<optionno> means "don't include this option"
not "include a zero-length option" */
@@ -2439,7 +2533,7 @@
continue;
o->flags |= DHOPT_ENCAP_DONE;
- if (match_netid(o->netid, netid, 1) &&
+ if (match_netid(o->netid, tagif, 1) &&
((o->flags & DHOPT_FORCE) || in_list(req_options, outer)))
{
o->flags |= DHOPT_ENCAP_MATCH;
@@ -2473,12 +2567,12 @@
}
}
- force_encap = prune_vendor_opts(netid);
+ force_encap = prune_vendor_opts(tagif);
if (context && pxe_arch != -1)
{
pxe_misc(mess, end, uuid);
- config_opts = pxe_opts(pxe_arch, netid, context->local);
+ config_opts = pxe_opts(pxe_arch, tagif, context->local);
}
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
diff --git a/src/tftp.c b/src/tftp.c
index 789c444..9cecdd9 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -222,17 +222,22 @@
if (strcmp(ir->interface, name) == 0)
special = 1;
-#ifdef HAVE_SOCKADDR_SA_LEN
- addr.sa.sa_len = sa_len(&addr);
-#endif
-
if (listen->family == AF_INET)
- addr.in.sin_port = htons(port);
+ {
+ addr.in.sin_port = htons(port);
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr.in.sin_len = sizeof(addr.in);
+#endif
+ }
#ifdef HAVE_IPV6
else
{
addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = 0;
+ addr.in6.sin6_scope_id = 0;
+#ifdef HAVE_SOCKADDR_SA_LEN
+ addr.in6.sin6_len = sizeof(addr.in6);
+#endif
}
#endif
@@ -260,7 +265,7 @@
/* if we have a nailed-down range, iterate until we find a free one. */
while (1)
{
- if (bind(transfer->sockfd, &addr.sa, sizeof(addr)) == -1 ||
+ if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
@@ -685,15 +690,13 @@
for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
{
- if (size == transfer->blocksize)
- {
- transfer->expansion++;
- if (i == size - 1)
- newcarrylf = 1; /* don't expand LF again if it moves to the next block */
- }
- else
+ transfer->expansion++;
+
+ if (size != transfer->blocksize)
size++; /* room in this block */
-
+ else if (i == size - 1)
+ newcarrylf = 1; /* don't expand LF again if it moves to the next block */
+
/* make space and insert CR */
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
mess->data[i] = '\r';
diff --git a/src/util.c b/src/util.c
index e64f1a6..e11360b 100644
--- a/src/util.c
+++ b/src/util.c
@@ -333,7 +333,15 @@
}
else if (addr->sa.sa_family == AF_INET6)
{
+ char name[IF_NAMESIZE];
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
+ if (addr->in6.sin6_scope_id != 0 &&
+ if_indextoname(addr->in6.sin6_scope_id, name) &&
+ strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
+ {
+ strcat(buf, "%");
+ strcat(buf, name);
+ }
port = ntohs(addr->in6.sin6_port);
}
#else