mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb60168382 | ||
|
|
57a2f5778c | ||
|
|
7d5fbe7da3 | ||
|
|
ded935be37 | ||
|
|
aa9b71c681 | ||
|
|
e497f3f2c8 | ||
|
|
ee09f0655c | ||
|
|
1e83316e06 | ||
|
|
9a566c0913 | ||
|
|
06e2d479c9 | ||
|
|
b52d1d2017 | ||
|
|
18195e7bb2 | ||
|
|
d81b1d76a0 | ||
|
|
1677c6e10b | ||
|
|
ff30fa4b91 |
No files matched your search
@@ -70,6 +70,12 @@ version 2.92
|
||||
is functional when client and server networks aren't mutually
|
||||
route-able.
|
||||
|
||||
Fix failure to add client MAC address to queries in TCP mode.
|
||||
The options which cause dnsmasq to decorate a DNS query with the MAC
|
||||
address on the originating client can fail when the query is sent
|
||||
using TCP. Thanks to Bruno Ravara for spotting and
|
||||
characterising this bug.
|
||||
|
||||
|
||||
version 2.91
|
||||
Fix spurious "resource limit exceeded messages". Thanks to
|
||||
|
||||
+6
-3
@@ -1489,19 +1489,22 @@ prefix-delegation from relayed DHCP transactions. See
|
||||
.B --dhcp-script
|
||||
for details.
|
||||
.TP
|
||||
.B --dhcp-split-relay=<local address>,[<server address>[#<server port>]],<server-facing-interface>
|
||||
.B --dhcp-split-relay=<local address>,[<server address>[#<server port>]],<server-facing-interface>|<server-facing-address>
|
||||
A usefully enchanced version of DHCPv4 relay. IPv4 DHCP normally uses a single address
|
||||
for two functions; it is used by the DHCP server to determine which network to allocate
|
||||
an address on, and it is used as the address of the relay to which the server sends packets.
|
||||
|
||||
This version of DHCP relay splits these functions. It uses the address of the server-facing relay
|
||||
interface as the address that the server talks to. The address of the client-facing interface
|
||||
interface or a directly-specified address as the address that the server talks to. The address of the client-facing interface
|
||||
(the first item in the config) is used as to determine the client's subnet. The
|
||||
local address is also used as server-ID override so that the client always sends requests
|
||||
via the relay. The effect of this is that server doesn't require
|
||||
a route to the client network and the clients don't require a route to the server.
|
||||
|
||||
The interface parameter is mandatory and a cannot be a wildcard.
|
||||
The third parameter is mandatory. If it is an interface name it cannot be a wildcard and the same filtering as descibed in
|
||||
--dhcp-relay applies; answers from the server must arrve via the specified interface. If the third parameter
|
||||
is an IP address it must be an address of a local interface which is routable from the server; In this case no filtering
|
||||
is done, the reply packets can arrive via any route.
|
||||
|
||||
If setting up a network where the client networks have limited routing, be careful
|
||||
about configuring the DHCP server. Dnsmasq, as DHCP server, will set the default route to the
|
||||
|
||||
+2870
File diff suppressed because it is too large.
Load diff
+885
-704
File diff suppressed because it is too large.
Load diff
@@ -111,22 +111,26 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
|
||||
|
||||
again:
|
||||
|
||||
/* If the database is less then INTERVAL old, look in there */
|
||||
if (difftime(now, last) < INTERVAL)
|
||||
/* If the database is less then INTERVAL old, look in there.
|
||||
|
||||
If we're a child process, we always rely on the existing cache we
|
||||
inherited from the parent, since we don't have a netlink socket.
|
||||
*/
|
||||
if (difftime(now, last) < INTERVAL || daemon->pipe_to_parent != -1)
|
||||
{
|
||||
/* addr == NULL -> just make cache up-to-date */
|
||||
if (!addr)
|
||||
return 0;
|
||||
|
||||
|
||||
for (arp = arps; arp; arp = arp->next)
|
||||
{
|
||||
if (addr->sa.sa_family != arp->family)
|
||||
continue;
|
||||
|
||||
|
||||
if (arp->family == AF_INET &&
|
||||
arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
|
||||
continue;
|
||||
|
||||
|
||||
if (arp->family == AF_INET6 &&
|
||||
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
|
||||
continue;
|
||||
@@ -141,6 +145,10 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
/* Not in cache in child, no go. */
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
return 0;
|
||||
|
||||
/* Not found, try the kernel */
|
||||
if (!updated)
|
||||
{
|
||||
|
||||
+3
-4
@@ -938,15 +938,14 @@ int cache_recv_insert(time_t now, int fd)
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
|
||||
if (!crecp)
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
else
|
||||
if (crecp)
|
||||
{
|
||||
next_uid(crecp);
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
newc->addr.cname.uid = crecp->uid;
|
||||
}
|
||||
crecp = newc;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
+16
-3
@@ -530,8 +530,8 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
||||
union all_addr addr;
|
||||
time_t now = dnsmasq_time();
|
||||
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
|
||||
|
||||
DBusMessageIter iter, array_iter;
|
||||
|
||||
if (!dbus_message_iter_init(message, &iter))
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Failed to initialize dbus message iter");
|
||||
@@ -599,6 +599,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
||||
|
||||
if (inet_pton(AF_INET, ipaddr, &addr.addr4))
|
||||
{
|
||||
if (!daemon->dhcp)
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"DHCPv4 not configured");
|
||||
|
||||
if (ia_id != 0 || is_temporary)
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"ia_id and is_temporary must be zero for IPv4 lease");
|
||||
@@ -609,16 +613,25 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6))
|
||||
{
|
||||
if (!daemon->doing_dhcp6)
|
||||
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"DHCPv6 not configured");
|
||||
|
||||
if (!(lease = lease6_find_by_addr(&addr.addr6, 128, 0)))
|
||||
lease = lease6_allocate(&addr.addr6,
|
||||
is_temporary ? LEASE_TA : LEASE_NA);
|
||||
lease_set_iaid(lease, ia_id);
|
||||
if (lease)
|
||||
lease_set_iaid(lease, ia_id);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"Invalid IP address '%s'", ipaddr);
|
||||
|
||||
|
||||
if (!lease)
|
||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
"unable to allocate lease for IP address '%s'", ipaddr);
|
||||
|
||||
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
|
||||
if (hw_len < 0)
|
||||
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
|
||||
|
||||
+3
-1
@@ -709,7 +709,8 @@ static const struct opttab_t {
|
||||
{ "client-machine-id", 97, 0 },
|
||||
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||
{ "captive-portal", 114, OT_NAME }, /* RFC 8910 */
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
@@ -751,6 +752,7 @@ static const struct opttab_t opttab6[] = {
|
||||
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
|
||||
{ "bootfile-url", 59, OT_NAME },
|
||||
{ "bootfile-param", 60, OT_CSTRING },
|
||||
{ "captive-portal", 103, OT_NAME }, /* RFC 8910 */
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
#endif
|
||||
|
||||
+22
-8
@@ -142,7 +142,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
struct iovec iov;
|
||||
ssize_t sz;
|
||||
int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0;
|
||||
int rcvd_iface_index;
|
||||
int rcvd_iface_index, relay_index;
|
||||
struct in_addr iface_addr;
|
||||
struct iface_param parm;
|
||||
time_t recvtime = now;
|
||||
@@ -302,10 +302,10 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
unicast_dest = 1;
|
||||
#endif
|
||||
|
||||
if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
|
||||
if ((relay_index = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, ifr.ifr_name)))
|
||||
{
|
||||
/* Reply from server, using us as relay. */
|
||||
rcvd_iface_index = relay->iface_index;
|
||||
rcvd_iface_index = relay_index;
|
||||
if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
|
||||
return;
|
||||
is_relay_reply = 1;
|
||||
@@ -330,9 +330,12 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
return;
|
||||
|
||||
/* unlinked contexts/relays are marked by context->current == context */
|
||||
/* unlinked contexts are marked by context->current == context */
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
context->current = context;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
relay->matchcount = 0;
|
||||
|
||||
parm.current = NULL;
|
||||
parm.ind = iface_index;
|
||||
@@ -360,7 +363,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if (!iface_enumerate(AF_INET, &parm, (callback_t){.af_inet=complete_context}))
|
||||
return;
|
||||
|
||||
relay_upstream4(iface_index, mess, (size_t)sz, unicast_dest);
|
||||
relay_upstream4(iface_addr, iface_index, mess, (size_t)sz, unicast_dest);
|
||||
|
||||
/* May have configured relay, but not DHCP server */
|
||||
if (!daemon->dhcp)
|
||||
@@ -664,8 +667,19 @@ static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
}
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
if (relay->local.addr4.s_addr == local.s_addr)
|
||||
relay->iface_index = if_index;
|
||||
if (!relay->split_mode && relay->local.addr4.s_addr == local.s_addr)
|
||||
{
|
||||
if (if_index == param->ind)
|
||||
relay->iface_index = if_index;
|
||||
|
||||
/* More than one interface with the relay address breaks things. */
|
||||
if (relay->matchcount++ == 1 && !relay->warned)
|
||||
{
|
||||
relay->warned = 1;
|
||||
inet_ntop(AF_INET, &local, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP relay address %s appears on more than one interface"), daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -682,7 +696,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
|
||||
struct dhcp_context *tmp;
|
||||
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
if (taddr.s_addr == context->router.s_addr)
|
||||
if (taddr.s_addr == tmp->router.s_addr)
|
||||
return NULL;
|
||||
|
||||
for (tmp = context; tmp; tmp = tmp->current)
|
||||
|
||||
+16
-2
@@ -89,6 +89,7 @@ void dhcp6_init(void)
|
||||
void dhcp6_packet(time_t now)
|
||||
{
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
struct iface_param parm;
|
||||
struct cmsghdr *cmptr;
|
||||
struct msghdr msg;
|
||||
@@ -231,7 +232,10 @@ void dhcp6_packet(time_t now)
|
||||
context->current = context;
|
||||
memset(&context->local6, 0, IN6ADDRSZ);
|
||||
}
|
||||
|
||||
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
relay->matchcount = 0;
|
||||
|
||||
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &all_servers);
|
||||
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
|
||||
multicast_dest = 1;
|
||||
@@ -452,7 +456,17 @@ static int complete_context6(struct in6_addr *local, int prefix,
|
||||
if (match)
|
||||
for (relay = daemon->relay6; relay; relay = relay->next)
|
||||
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
|
||||
relay->iface_index = if_index;
|
||||
{
|
||||
relay->iface_index = if_index;
|
||||
|
||||
/* More than one interface with the relay address breaks things. */
|
||||
if (relay->matchcount++ == 1 && !relay->warned)
|
||||
{
|
||||
relay->warned = 1;
|
||||
inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP relay address %s appears on more than one interface"), daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
+16
-1
@@ -2033,9 +2033,24 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
/* The code in edns0.c qthat decorates queries with the source MAC address depends
|
||||
on the code in arp.c, which populates a cache with the contents of the ARP table
|
||||
using netlink. Since the child process can't use netlink, we pre-populate
|
||||
the cache with the ARP table entry for our source here, including a negative entry
|
||||
if there is nothing for our address in the ARP table.
|
||||
|
||||
When the edns0 code calls find_mac() in the child process, it will
|
||||
get the correct answer from the cache inherited from the parent
|
||||
without having to use netlink to consult the kernel ARP table.
|
||||
|
||||
edns0_needs_mac() simply calls find_mac if any EDNS0 options
|
||||
which need a MAC address are enabled. */
|
||||
|
||||
edns0_needs_mac(&tcp_addr, now);
|
||||
|
||||
if (pipe(pipefd) == -1)
|
||||
goto closeconandreturn; /* pipe failed */
|
||||
|
||||
|
||||
if ((p = fork()) == -1)
|
||||
{
|
||||
/* fork failed */
|
||||
|
||||
+10
-6
@@ -1109,7 +1109,7 @@ struct ping_result {
|
||||
|
||||
struct tftp_file {
|
||||
int refcount, fd;
|
||||
off_t size;
|
||||
off_t size, posn;
|
||||
dev_t dev;
|
||||
ino_t inode;
|
||||
char filename[];
|
||||
@@ -1117,13 +1117,14 @@ struct tftp_file {
|
||||
|
||||
struct tftp_transfer {
|
||||
int sockfd;
|
||||
u16 block_hi, ackprev;
|
||||
time_t retransmit, start;
|
||||
unsigned int lastack, block, blocksize, windowsize, timeout, expansion;
|
||||
off_t offset;
|
||||
union mysockaddr peer;
|
||||
union all_addr source;
|
||||
int if_index;
|
||||
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
|
||||
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, lastcarrylf, backoff;
|
||||
struct tftp_file *file;
|
||||
struct tftp_transfer *next;
|
||||
};
|
||||
@@ -1144,11 +1145,12 @@ struct dhcp_relay {
|
||||
union {
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
} local, server;
|
||||
} local, server, uplink;
|
||||
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
|
||||
int iface_index; /* working - interface in which requests arrived, for return */
|
||||
int port; /* Port of relay we forward to. */
|
||||
int split_mode; /* Split address allocation and relay address. */
|
||||
int warned, matchcount;
|
||||
#ifdef HAVE_SCRIPT
|
||||
struct snoop_record {
|
||||
struct in6_addr client, prefix;
|
||||
@@ -1276,7 +1278,7 @@ extern struct daemon {
|
||||
struct serverfd *sfds;
|
||||
struct irec *interfaces;
|
||||
struct listener *listeners;
|
||||
struct server *srv_save; /* Used for resend on DoD */
|
||||
void *srv_save; /* Used for resend on DoD and tftp prefetch */
|
||||
size_t packet_len; /* " " */
|
||||
int fd_save; /* " " */
|
||||
pid_t *tcp_pids;
|
||||
@@ -1667,8 +1669,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
time_t recvtime, struct in_addr leasequery_source);
|
||||
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
|
||||
int clid_len, unsigned char *clid, int *len_out);
|
||||
void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int unicast);
|
||||
struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
|
||||
void relay_upstream4(struct in_addr iface_addr, int iface_index,
|
||||
struct dhcp_packet *mess, size_t sz, int unicast);
|
||||
unsigned int relay_reply4(struct dhcp_packet *mess, size_t sz, char *arrival_interface);
|
||||
#endif
|
||||
|
||||
/* dnsmasq.c */
|
||||
@@ -1915,6 +1918,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
||||
void edns0_needs_mac(union mysockaddr *addr, time_t now);
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
union mysockaddr *source, time_t now, int *cacheable);
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
|
||||
+10
@@ -265,6 +265,15 @@ static void encoder(unsigned char *in, char *out)
|
||||
out[3] = char64(in[2]);
|
||||
}
|
||||
|
||||
/* This function needs to call find_mac if any option which requires a MAC address is enabled
|
||||
and used below. If you add a new MAC consumer, modify this, otherwise your
|
||||
new EDNS0 option won't work in TCP mode. */
|
||||
void edns0_needs_mac(union mysockaddr *addr, time_t now)
|
||||
{
|
||||
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX) || option_bool(OPT_ADD_MAC))
|
||||
find_mac(addr, NULL, 0, now);
|
||||
}
|
||||
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
@@ -562,3 +571,4 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
+41
-30
@@ -17,7 +17,7 @@
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct frec *get_new_frec(time_t now, struct server *serv, int force);
|
||||
static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask);
|
||||
static struct frec *lookup_frec(time_t now, char *target, int class, int rrtype, int id, int flags, int flagmask);
|
||||
#ifdef HAVE_DNSSEC
|
||||
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
|
||||
int class, char *name, char *keyname, struct server *server,
|
||||
@@ -192,7 +192,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
old_reply = 1;
|
||||
fwd_flags = forward->flags;
|
||||
}
|
||||
else if (gotname && (forward = lookup_frec(daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
|
||||
else if (gotname && (forward = lookup_frec(now, daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
|
||||
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
|
||||
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
|
||||
{
|
||||
@@ -980,7 +980,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
unsigned int flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
|
||||
struct frec *old;
|
||||
|
||||
if ((old = lookup_frec(daemon->keyname, forward->class, -1, -1, flags, flags)))
|
||||
if ((old = lookup_frec(now, daemon->keyname, forward->class, -1, -1, flags, flags)))
|
||||
{
|
||||
/* This is tricky; it detects loops in the dependency
|
||||
graph for DNSSEC validation, say validating A requires DS B
|
||||
@@ -1170,7 +1170,7 @@ void reply_query(int fd, time_t now)
|
||||
GETSHORT(rrtype, p);
|
||||
GETSHORT(class, p);
|
||||
|
||||
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
|
||||
if (!(forward = lookup_frec(now, daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
|
||||
return;
|
||||
|
||||
filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last);
|
||||
@@ -3106,7 +3106,7 @@ static void free_frec(struct frec *f)
|
||||
/* Impose an absolute
|
||||
limit of 4*TIMEOUT before we wipe things (for random sockets).
|
||||
If force is set, always return a result, even if we have
|
||||
to allocate above the limit, and don'y free any records.
|
||||
to allocate above the limit, and don't free any records.
|
||||
This is set when allocating for DNSSEC to avoid cutting off
|
||||
the branch we are sitting on. */
|
||||
static struct frec *get_new_frec(time_t now, struct server *master, int force)
|
||||
@@ -3128,36 +3128,40 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
|
||||
/* Don't free DNSSEC sub-queries here, as we may end up with
|
||||
dangling references to them. They'll go when their "real" query
|
||||
is freed. */
|
||||
if (!f->dependent && !force)
|
||||
if (!f->dependent)
|
||||
#endif
|
||||
{
|
||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||
{
|
||||
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
|
||||
free_frec(f);
|
||||
target = f;
|
||||
}
|
||||
else if (!oldest || difftime(f->time, oldest->time) <= 0)
|
||||
oldest = f;
|
||||
}
|
||||
if (!force)
|
||||
{
|
||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||
{
|
||||
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
|
||||
free_frec(f);
|
||||
target = f;
|
||||
}
|
||||
else if (!oldest || difftime(f->time, oldest->time) <= 0)
|
||||
oldest = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (f->sentto && ((int)difftime(now, f->time)) < TIMEOUT && server_samegroup(f->sentto, master))
|
||||
count++;
|
||||
}
|
||||
|
||||
if (!force && count >= daemon->ftabsize)
|
||||
{
|
||||
query_full(now, master->domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
|
||||
{
|
||||
/* can't find empty one, use oldest if there is one and it's older than timeout */
|
||||
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
|
||||
free_frec(oldest);
|
||||
target = oldest;
|
||||
if (!force)
|
||||
{
|
||||
if (count >= daemon->ftabsize)
|
||||
{
|
||||
query_full(now, master->domain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
|
||||
{
|
||||
/* can't find empty one, use oldest if there is one and it's older than timeout */
|
||||
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
|
||||
free_frec(oldest);
|
||||
target = oldest;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target && (target = (struct frec *)whine_malloc(sizeof(struct frec))))
|
||||
@@ -3192,7 +3196,7 @@ static void query_full(time_t now, char *domain)
|
||||
}
|
||||
}
|
||||
|
||||
static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask)
|
||||
static struct frec *lookup_frec(time_t now, char *target, int class, int rrtype, int id, int flags, int flagmask)
|
||||
{
|
||||
struct frec *f;
|
||||
struct dns_header *header;
|
||||
@@ -3242,7 +3246,14 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* frecs older than this will get garbage-collected in
|
||||
get_new_frec(), so don't return them here, so we have
|
||||
consistent behaviour from an idle dnsmasq which
|
||||
is not calling get_new_frec() often. */
|
||||
if (difftime(now, f->time) >= 4*TIMEOUT)
|
||||
return NULL;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
@@ -4756,6 +4756,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
else
|
||||
three = two;
|
||||
}
|
||||
else if (new->split_mode && inet_pton(AF_INET, three, &new->uplink))
|
||||
/* Third arg in split mode can be an address. */
|
||||
three = NULL;
|
||||
|
||||
new->next = daemon->relay4;
|
||||
daemon->relay4 = new;
|
||||
|
||||
+21
-7
@@ -516,7 +516,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
|
||||
Cache said SOA and return the difference in length between name and the name of the
|
||||
SOA RR so we can look it up again.
|
||||
*/
|
||||
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int no_cache, time_t now)
|
||||
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int cache, time_t now)
|
||||
{
|
||||
unsigned char *p, *psave;
|
||||
int qtype, qclass, rdlen;
|
||||
@@ -556,7 +556,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
{
|
||||
int prefix = name_len - soa_len;
|
||||
|
||||
if (!no_cache)
|
||||
if (cache)
|
||||
{
|
||||
if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0)))
|
||||
return 0;
|
||||
@@ -568,12 +568,12 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
if (!no_cache)
|
||||
if (cache)
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!no_cache)
|
||||
if (cache)
|
||||
{
|
||||
len = to_wire(daemon->workspacename);
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len))
|
||||
@@ -588,13 +588,13 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
|
||||
if (!CHECK_LEN(header, p, qlen, 20))
|
||||
{
|
||||
if (!no_cache)
|
||||
if (cache)
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* rest of RR */
|
||||
if (!no_cache)
|
||||
if (cache)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
@@ -1079,7 +1079,17 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (insert && !option_bool(OPT_NO_NEG))
|
||||
{
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
|
||||
/* The order of records going into the cache matters (see cache_recv_insert()).
|
||||
The target of a CNAME must immediately follow the CNAME.
|
||||
(CNAME has already gone into the cache at this point)
|
||||
Here we call find_soa to get the ttl and substring, but
|
||||
we DON'T LET IT INSERT the SOA into the cache if our negative record is a CNAME target
|
||||
so that the SOA doesn't come before the CNAME target.
|
||||
|
||||
We call find_soa() again after inserting the CNAME target to insert the SOA
|
||||
if necessary. */
|
||||
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, cpp == NULL, now);
|
||||
|
||||
if (have_soa || daemon->neg_ttl)
|
||||
{
|
||||
@@ -1101,6 +1111,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
|
||||
/* we didn't insert the SOA before a CNAME target above, do it now. */
|
||||
if (have_soa)
|
||||
find_soa(header, qlen, name, NULL, NULL, 1, now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+168
-144
@@ -3055,7 +3055,7 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
}
|
||||
}
|
||||
|
||||
void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int unicast)
|
||||
void relay_upstream4(struct in_addr iface_addr, int iface_index, struct dhcp_packet *mess, size_t sz, int unicast)
|
||||
{
|
||||
struct in_addr giaddr = mess->giaddr;
|
||||
u8 hops = mess->hops;
|
||||
@@ -3063,141 +3063,159 @@ void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int u
|
||||
size_t orig_sz = sz;
|
||||
unsigned char *endopt = NULL;
|
||||
|
||||
if (mess->op != BOOTREQUEST)
|
||||
if (mess->op != BOOTREQUEST || (mess->hops++) > 20)
|
||||
return;
|
||||
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
struct ifreq ifr;
|
||||
|
||||
/* restore orig packet */
|
||||
mess->hops = hops;
|
||||
mess->giaddr = giaddr;
|
||||
if (endopt)
|
||||
*endopt = OPTION_END;
|
||||
sz = orig_sz;
|
||||
|
||||
if ((mess->hops++) > 20)
|
||||
continue;
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
struct ifreq ifr;
|
||||
|
||||
if (relay->interface)
|
||||
{
|
||||
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
}
|
||||
|
||||
if (!relay->split_mode)
|
||||
{
|
||||
/* already gatewayed ? */
|
||||
if (giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
continue;
|
||||
}
|
||||
/* restore orig packet */
|
||||
mess->giaddr = giaddr;
|
||||
if (endopt)
|
||||
*endopt = OPTION_END;
|
||||
sz = orig_sz;
|
||||
|
||||
/* plug in our address */
|
||||
from.addr4 = mess->giaddr = relay->local.addr4;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Split mode. We put our address on the server-facing interface
|
||||
into giaddr for the server to talk back to us on.
|
||||
|
||||
Our address on client-facing interface goes into agent-id
|
||||
subnet-selector subopt, so that the server allocates the correct address. */
|
||||
|
||||
/* get our address on the server-facing interface. */
|
||||
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
|
||||
continue;
|
||||
|
||||
/* already gatewayed ? */
|
||||
if (giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (giaddr.s_addr == ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* giaddr is our address on the outgoing interface in split mode. */
|
||||
from.addr4 = mess->giaddr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
|
||||
if (!endopt)
|
||||
{
|
||||
/* Add an RFC3026 relay agent information option (2 bytes) at the very end of the options.
|
||||
Said option to contain a RFC 3527 link selection sub option (6 bytes) and
|
||||
RFC 5017 serverid-override option (6 bytes) and RFC5010 (3 bytes).
|
||||
New END option is a 18th byte, so we need 18 bytes free.
|
||||
We only need to do this once, and poke the address into the same place each time. */
|
||||
|
||||
if (!(endopt = option_find1((&mess->options[0] + sizeof(u32)), ((unsigned char *)mess) + sz, OPTION_END, 0)) ||
|
||||
(endopt + 18 > (unsigned char *)(mess + 1)))
|
||||
continue;
|
||||
|
||||
endopt[1] = 15; /* length */
|
||||
endopt[2] = SUBOPT_SUBNET_SELECT;
|
||||
endopt[3] = 4; /* length */
|
||||
endopt[8] = SUBOPT_SERVER_OR;
|
||||
endopt[9] = 4;
|
||||
endopt[14] = SUBOPT_FLAGS;
|
||||
endopt[15] = 1; /* length */
|
||||
endopt[17] = OPTION_END;
|
||||
sz = (endopt - (unsigned char *)mess) + 18;
|
||||
}
|
||||
|
||||
/* IP address is already in network byte order */
|
||||
memcpy(&endopt[4], &relay->local.addr4.s_addr, INADDRSZ);
|
||||
memcpy(&endopt[10], &relay->local.addr4.s_addr, INADDRSZ);
|
||||
endopt[16] = unicast ? 0x80 : 0x00;
|
||||
endopt[0] = OPTION_AGENT_ID;
|
||||
}
|
||||
|
||||
to.sa.sa_family = AF_INET;
|
||||
to.in.sin_addr = relay->server.addr4;
|
||||
to.in.sin_port = htons(relay->port);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
to.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
|
||||
/* Broadcasting to server. */
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
{
|
||||
if (!relay->interface || strchr(relay->interface, '*') ||
|
||||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
if (relay->interface)
|
||||
{
|
||||
union mysockaddr fromsock;
|
||||
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
fromsock.in.sin_addr = from.addr4;
|
||||
fromsock.sa.sa_family = AF_INET;
|
||||
|
||||
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
|
||||
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!relay->split_mode && relay->iface_index && relay->iface_index == iface_index)
|
||||
{
|
||||
/* already gatewayed ? */
|
||||
if (giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
/* plug in our address */
|
||||
mess->giaddr = relay->local.addr4;
|
||||
|
||||
from.addr4 = relay->local.addr4;
|
||||
}
|
||||
else if (relay->split_mode && relay->local.addr4.s_addr == iface_addr.s_addr)
|
||||
{
|
||||
/* Split mode. We put our address on the server-facing interface
|
||||
or a directly specified third address into giaddr for the server to talk back to us on.
|
||||
|
||||
Our address on client-facing interface goes into agent-id subnet-selector subopt,
|
||||
so that the server allocates the correct address. We also send a
|
||||
remote-id with the interface on which the request arrived,
|
||||
so that we can send the reply back the same way. */
|
||||
unsigned int net_index = htonl(iface_index);
|
||||
|
||||
if (relay->interface)
|
||||
{
|
||||
/* get our address on the server-facing interface. */
|
||||
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot send to server via interface %s: %s"), relay->interface, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
relay->uplink.addr4 = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
}
|
||||
|
||||
/* already gatewayed ? */
|
||||
if (giaddr.s_addr)
|
||||
{
|
||||
/* if so check if by us, to stomp on loops. */
|
||||
if (giaddr.s_addr == relay->uplink.addr4.s_addr)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* giaddr is our address on the outgoing interface in split mode. */
|
||||
mess->giaddr = relay->uplink.addr4;
|
||||
|
||||
if (!endopt)
|
||||
{
|
||||
/* Add an RFC3026 relay agent information option (2 bytes) at the very end of the options.
|
||||
Said option to contain a RFC 3527 link selection sub option (6 bytes) and
|
||||
RFC 5017 serverid-override option (6 bytes) and RFC5010 flags (3 bytes) and
|
||||
an RFC3046 remote-id which holds an interface index (6 bytes)
|
||||
|
||||
New END option is a 24th byte, so we need 24 bytes free.
|
||||
We only need to do this once, and poke the address/interface/flags into the same place each time. */
|
||||
|
||||
if (!(endopt = option_find1((&mess->options[0] + sizeof(u32)), ((unsigned char *)mess) + sz, OPTION_END, 0)) ||
|
||||
(endopt + 24 > (unsigned char *)(mess + 1)))
|
||||
continue;
|
||||
|
||||
endopt[1] = 21; /* length */
|
||||
endopt[2] = SUBOPT_SUBNET_SELECT;
|
||||
endopt[3] = 4; /* length */
|
||||
endopt[8] = SUBOPT_SERVER_OR;
|
||||
endopt[9] = 4;
|
||||
endopt[14] = SUBOPT_FLAGS;
|
||||
endopt[15] = 1; /* length */
|
||||
endopt[17] = SUBOPT_REMOTE_ID;
|
||||
endopt[18] = 4; /* length */
|
||||
endopt[23] = OPTION_END;
|
||||
sz = (endopt - (unsigned char *)mess) + 24;
|
||||
}
|
||||
|
||||
/* IP address is already in network byte order */
|
||||
memcpy(&endopt[4], &relay->local.addr4.s_addr, INADDRSZ);
|
||||
memcpy(&endopt[10], &relay->local.addr4.s_addr, INADDRSZ);
|
||||
endopt[16] = unicast ? 0x80 : 0x00;
|
||||
memcpy(&endopt[19], &net_index, 4);
|
||||
endopt[0] = OPTION_AGENT_ID;
|
||||
}
|
||||
|
||||
from.addr4 = relay->uplink.addr4;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
|
||||
else
|
||||
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
|
||||
}
|
||||
to.sa.sa_family = AF_INET;
|
||||
to.in.sin_addr = relay->server.addr4;
|
||||
to.in.sin_port = htons(relay->port);
|
||||
#ifdef HAVE_SOCKADDR_SA_LEN
|
||||
to.in.sin_len = sizeof(struct sockaddr_in);
|
||||
#endif
|
||||
|
||||
/* Broadcasting to server. */
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
{
|
||||
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s: %s"), relay->interface, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
{
|
||||
union mysockaddr fromsock;
|
||||
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
fromsock.in.sin_addr = from.addr4;
|
||||
fromsock.sa.sa_family = AF_INET;
|
||||
|
||||
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
|
||||
}
|
||||
#endif
|
||||
|
||||
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
|
||||
if (relay->server.addr4.s_addr == 0)
|
||||
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
|
||||
else
|
||||
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
|
||||
}
|
||||
}
|
||||
|
||||
/* restore in case of a local reply. */
|
||||
mess->hops = hops;
|
||||
@@ -3206,35 +3224,41 @@ void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz, int u
|
||||
*endopt = OPTION_END;
|
||||
}
|
||||
|
||||
struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
|
||||
unsigned int relay_reply4(struct dhcp_packet *mess, size_t sz, char *arrival_interface)
|
||||
{
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
|
||||
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
|
||||
return NULL;
|
||||
return 0;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
{
|
||||
unsigned int return_iface = 0;
|
||||
|
||||
if (relay->split_mode)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
|
||||
safe_strncpy(ifr.ifr_name, arrival_interface, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
|
||||
unsigned char *opt, *sopt;
|
||||
|
||||
/* giaddr is our address on the returning interface in split mode. */
|
||||
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1 ||
|
||||
mess->giaddr.s_addr != ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr.s_addr)
|
||||
continue;
|
||||
if (mess->giaddr.s_addr == relay->uplink.addr4.s_addr &&
|
||||
(opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
{
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, sizeof(unsigned int))))
|
||||
return_iface = option_uint(sopt, 0, sizeof(unsigned int));
|
||||
|
||||
/* delete agent info before return RFC 3046 para 2.1 */
|
||||
*opt = OPTION_END;
|
||||
memset(opt + 1, 0, option_len(opt) + 2);
|
||||
}
|
||||
}
|
||||
else if (mess->giaddr.s_addr != relay->local.addr4.s_addr)
|
||||
continue;
|
||||
else if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
return_iface = relay->iface_index;
|
||||
|
||||
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
|
||||
return relay->iface_index != 0 ? relay : NULL;
|
||||
if (return_iface && (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
|
||||
return return_iface;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+132
-96
@@ -18,12 +18,12 @@
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
|
||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
|
||||
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len);
|
||||
static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client);
|
||||
static void free_transfer(struct tftp_transfer *transfer);
|
||||
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
|
||||
static ssize_t tftp_err_oops(char *packet, const char *file);
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
|
||||
static ssize_t get_block(struct tftp_transfer *transfer);
|
||||
static char *next(char **p, char *end);
|
||||
static void sanitise(char *buf);
|
||||
|
||||
@@ -41,10 +41,9 @@ static void sanitise(char *buf);
|
||||
#define ERR_ILL 4
|
||||
#define ERR_TID 5
|
||||
|
||||
static void tftp_request(struct listener *listen, time_t now)
|
||||
static void tftp_request(char *packet, ssize_t plen, struct listener *listen, time_t now)
|
||||
{
|
||||
ssize_t len;
|
||||
char *packet = daemon->packet;
|
||||
char *filename, *mode, *p, *end;
|
||||
union mysockaddr addr, peer;
|
||||
struct msghdr msg;
|
||||
@@ -87,12 +86,10 @@ static void tftp_request(struct listener *listen, time_t now)
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
/* packet buff is DNS name workspace. */
|
||||
iov.iov_base = packet;
|
||||
iov.iov_len = daemon->packet_buff_sz;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
iov.iov_len = plen;
|
||||
|
||||
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
|
||||
return;
|
||||
|
||||
@@ -267,7 +264,7 @@ static void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
else
|
||||
{
|
||||
handle_tftp(now, transfer, len);
|
||||
handle_tftp(packet, now, transfer, len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -326,6 +323,8 @@ static void tftp_request(struct listener *listen, time_t now)
|
||||
transfer->start = now;
|
||||
transfer->backoff = 1;
|
||||
transfer->block = 1;
|
||||
transfer->ackprev = 0;
|
||||
transfer->block_hi = 0;
|
||||
transfer->blocksize = 512;
|
||||
transfer->windowsize = 1;
|
||||
|
||||
@@ -499,17 +498,21 @@ static 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, prefix, daemon->addrbuff)))
|
||||
if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff)))
|
||||
{
|
||||
transfer->lastack = transfer->block;
|
||||
transfer->retransmit = now + transfer->timeout;
|
||||
/* This packet is may be the first data packet, but only if windowsize == 1
|
||||
To get windowsize greater then one requires an option negotiation,
|
||||
in which case this packet is the OACK. */
|
||||
if ((len = get_block(packet, transfer)) == -1)
|
||||
if ((len = get_block(transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
else
|
||||
is_err = 0;
|
||||
{
|
||||
is_err = 0;
|
||||
/* get_block put the packet to send in a different buffer. */
|
||||
packet = daemon->packet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -532,9 +535,9 @@ static void tftp_request(struct listener *listen, time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
|
||||
static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client)
|
||||
{
|
||||
char *packet = daemon->packet, *namebuff = daemon->namebuff;
|
||||
char *namebuff = daemon->namebuff;
|
||||
struct tftp_file *file;
|
||||
struct tftp_transfer *t;
|
||||
uid_t uid = geteuid();
|
||||
@@ -597,6 +600,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
|
||||
file->size = statbuf.st_size;
|
||||
file->dev = statbuf.st_dev;
|
||||
file->inode = statbuf.st_ino;
|
||||
file->posn = 0;
|
||||
file->refcount = 1;
|
||||
strcpy(file->filename, namebuff);
|
||||
return file;
|
||||
@@ -616,12 +620,15 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
|
||||
|
||||
void check_tftp_listeners(time_t now)
|
||||
{
|
||||
/* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */
|
||||
char *packet = daemon->workspacename;
|
||||
ssize_t plen = MAXDNAME * 2;
|
||||
struct listener *listener;
|
||||
struct tftp_transfer *transfer, *tmp, **up;
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
|
||||
tftp_request(listener, now);
|
||||
tftp_request(packet, plen, listener, now);
|
||||
|
||||
/* In single port mode, all packets come via port 69 and tftp_request() */
|
||||
if (!option_bool(OPT_SINGLE_PORT))
|
||||
@@ -632,26 +639,23 @@ void check_tftp_listeners(time_t now)
|
||||
socklen_t addr_len = sizeof(union mysockaddr);
|
||||
ssize_t len;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||
if ((len = recvfrom(transfer->sockfd, packet, plen, 0, &peer.sa, &addr_len)) > 0)
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||
#endif
|
||||
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
handle_tftp(now, transfer, len);
|
||||
handle_tftp(packet, now, transfer, len);
|
||||
else
|
||||
{
|
||||
/* Wrong source address. See rfc1350 para 4. */
|
||||
prettyprint_addr(&peer, daemon->addrbuff);
|
||||
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
|
||||
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||
len = tftp_err(ERR_TID, packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
|
||||
while(retry_send(sendto(transfer->sockfd, packet, len, 0, &peer.sa, sa_len(&peer))));
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -671,7 +675,7 @@ void check_tftp_listeners(time_t now)
|
||||
endcon = 1;
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
if (get_block(daemon->packet, transfer) > 0)
|
||||
if (get_block(transfer) > 0)
|
||||
error = timeout = 1;
|
||||
}
|
||||
else if (difftime(now, transfer->retransmit) >= 0.0)
|
||||
@@ -680,41 +684,43 @@ void check_tftp_listeners(time_t now)
|
||||
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
||||
here. */
|
||||
ssize_t len;
|
||||
unsigned int i, winsize;
|
||||
|
||||
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
|
||||
transfer->backoff++;
|
||||
transfer->block = transfer->lastack;
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == 0)
|
||||
endcon = 1; /* got last ACK */
|
||||
else
|
||||
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
||||
winsize = transfer->block ? transfer->windowsize : 1;
|
||||
|
||||
for (i = 0; i < winsize; i++, transfer->block++)
|
||||
{
|
||||
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
||||
unsigned int i, winsize = transfer->block ? transfer->windowsize : 1;
|
||||
|
||||
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
|
||||
if ((len = get_block(transfer)) == 0)
|
||||
{
|
||||
if (i != 0)
|
||||
len = get_block(daemon->packet, transfer);
|
||||
if (i == 0)
|
||||
endcon = 1; /* got last ACK */
|
||||
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = error = 1;
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (len == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = error = 1;
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||
&transfer->peer, &transfer->source, transfer->if_index);
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* prefetch the block we'll probably need when we get an ACK. */
|
||||
if (!endcon)
|
||||
get_block(transfer);
|
||||
}
|
||||
|
||||
if (endcon)
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
@@ -743,23 +749,36 @@ void check_tftp_listeners(time_t now)
|
||||
}
|
||||
}
|
||||
|
||||
/* packet in daemon->packet as this is called. */
|
||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
{
|
||||
struct ack {
|
||||
unsigned short op, block;
|
||||
} *mess = (struct ack *)daemon->packet;
|
||||
} *mess = (struct ack *)packet;
|
||||
|
||||
if (len >= (ssize_t)sizeof(struct ack))
|
||||
{
|
||||
if (ntohs(mess->op) == OP_ACK)
|
||||
{
|
||||
/* try and handle 16-bit blockno wrap-around */
|
||||
unsigned int block = (unsigned short)ntohs(mess->block);
|
||||
if (block < transfer->lastack)
|
||||
block |= transfer->block & 0xffff0000;
|
||||
|
||||
/* ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
||||
/* Handle 16-bit block number wrap-around. */
|
||||
u16 new = ntohs(mess->block);
|
||||
u32 block;
|
||||
|
||||
/* If the last ack received was in the top quarter of a 64k block
|
||||
and this one is in the bottom quarter, assume it has wrapped.
|
||||
|
||||
Since this is UDP and an old packet can in theory wander in we may also
|
||||
need to drop back to a previous segment. Such an ACK is ignored below;
|
||||
here we're just getting the most likely 32 bit value from the
|
||||
16 bits that we have. */
|
||||
if (new <= 0x4000 && transfer->ackprev >= 0xc000)
|
||||
transfer->block_hi++;
|
||||
else if (new >= 0xc000 && transfer->ackprev <= 0x4000 && transfer->block_hi != 0)
|
||||
transfer->block_hi--;
|
||||
|
||||
transfer->ackprev = new;
|
||||
block = (((u32)transfer->block_hi) << 16) + (u32)new;
|
||||
|
||||
/* Ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
||||
if (block >= transfer->lastack &&
|
||||
block <= transfer->block)
|
||||
{
|
||||
@@ -767,20 +786,23 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
transfer->retransmit = transfer->start = now;
|
||||
transfer->backoff = 0;
|
||||
transfer->lastack = block + 1;
|
||||
|
||||
|
||||
/* We have no easy function from block no. to file offset when
|
||||
expanding line breaks in netascii mode, so we update the offset here
|
||||
as each block is acknowledged. This explains why the window size must be
|
||||
one for a netascii transfer; to avoid the block no. doing anything
|
||||
other than incrementing by one. */
|
||||
if (transfer->netascii && block != 0)
|
||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
||||
{
|
||||
transfer->offset += (off_t)transfer->blocksize - (off_t)transfer->expansion;
|
||||
transfer->lastcarrylf = transfer->carrylf;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ntohs(mess->op) == OP_ERR)
|
||||
{
|
||||
char *p = daemon->packet + sizeof(struct ack);
|
||||
char *end = daemon->packet + len;
|
||||
char *p = packet + sizeof(struct ack);
|
||||
char *end = packet + len;
|
||||
char *err = next(&p, end);
|
||||
|
||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
@@ -857,10 +879,6 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
|
||||
} *mess = (struct errmess *)packet;
|
||||
ssize_t len, ret = 4;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
if (file)
|
||||
sanitise(file);
|
||||
|
||||
@@ -884,12 +902,10 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
|
||||
}
|
||||
|
||||
/* return -1 for error, zero for done. */
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
static ssize_t get_block(struct tftp_transfer *transfer)
|
||||
{
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
static off_t saved_offset = 0;
|
||||
static ssize_t saved_len = 0;
|
||||
|
||||
if (transfer->block == 0)
|
||||
{
|
||||
@@ -898,8 +914,12 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
struct oackmess {
|
||||
unsigned short op;
|
||||
char data[];
|
||||
} *mess = (struct oackmess *)packet;
|
||||
|
||||
} *mess = (struct oackmess *)daemon->packet;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
memset(daemon->packet, 0, daemon->packet_buff_sz);
|
||||
|
||||
p = mess->data;
|
||||
mess->op = htons(OP_OACK);
|
||||
if (transfer->opt_blocksize)
|
||||
@@ -923,7 +943,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
||||
}
|
||||
|
||||
return p - packet;
|
||||
return p - daemon->packet;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -931,44 +951,58 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
struct datamess {
|
||||
unsigned short op, block;
|
||||
unsigned char data[];
|
||||
} *mess = (struct datamess *)packet;
|
||||
} *mess = (struct datamess *)daemon->packet;
|
||||
|
||||
size_t size;
|
||||
|
||||
if (!transfer->netascii)
|
||||
transfer->offset = (transfer->block - 1) * transfer->blocksize;
|
||||
transfer->offset = (off_t)(transfer->block - 1) * (off_t)transfer->blocksize;
|
||||
|
||||
if (transfer->offset > transfer->file->size)
|
||||
return 0; /* finished */
|
||||
|
||||
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
|
||||
size = transfer->blocksize;
|
||||
|
||||
/* We may have a prefetched block already in the buffer. */
|
||||
if (daemon->srv_save == transfer && saved_offset == transfer->offset)
|
||||
return saved_len;
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if ((size = transfer->file->size - transfer->offset) > (size_t)transfer->blocksize)
|
||||
size = (size_t)transfer->blocksize;
|
||||
|
||||
mess->op = htons(OP_DATA);
|
||||
mess->block = htons((unsigned short)(transfer->block));
|
||||
|
||||
if (size != 0 &&
|
||||
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
||||
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
|
||||
return -1;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
if (transfer->file->posn != transfer->offset &&
|
||||
lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1)
|
||||
return -1;
|
||||
|
||||
if (!read_write(transfer->file->fd, mess->data, size, RW_READ))
|
||||
return -1;
|
||||
|
||||
transfer->file->posn = transfer->offset + size;
|
||||
}
|
||||
|
||||
/* Map '\n' to CR-LF in netascii mode */
|
||||
if (transfer->netascii)
|
||||
{
|
||||
size_t i;
|
||||
int newcarrylf;
|
||||
|
||||
/* Map '\n' to CR-LF in netascii mode */
|
||||
transfer->expansion = transfer->carrylf = 0;
|
||||
|
||||
transfer->expansion = 0;
|
||||
|
||||
for (i = 0, newcarrylf = 0; i < size; i++)
|
||||
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
|
||||
for (i = 0; i < size; i++)
|
||||
if (mess->data[i] == '\n' && (i != 0 || !transfer->lastcarrylf))
|
||||
{
|
||||
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 */
|
||||
transfer->carrylf = 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));
|
||||
@@ -976,11 +1010,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
transfer->carrylf = newcarrylf;
|
||||
}
|
||||
|
||||
return size + 4;
|
||||
daemon->srv_save = transfer;
|
||||
saved_offset = transfer->offset;
|
||||
saved_len = size + 4;
|
||||
|
||||
return saved_len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user