mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
37
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb60168382 | ||
|
|
57a2f5778c | ||
|
|
7d5fbe7da3 | ||
|
|
ded935be37 | ||
|
|
aa9b71c681 | ||
|
|
e497f3f2c8 | ||
|
|
ee09f0655c | ||
|
|
1e83316e06 | ||
|
|
9a566c0913 | ||
|
|
06e2d479c9 | ||
|
|
b52d1d2017 | ||
|
|
18195e7bb2 | ||
|
|
d81b1d76a0 | ||
|
|
1677c6e10b | ||
|
|
ff30fa4b91 | ||
|
|
c91c66ee63 | ||
|
|
fc9f6985ab | ||
|
|
ea5b0e64e2 | ||
|
|
e775264539 | ||
|
|
d976d94e3d | ||
|
|
3034746748 | ||
|
|
3ceea9e755 | ||
|
|
dfcef19fb4 | ||
|
|
f74b74e521 | ||
|
|
c9342cb556 | ||
|
|
2b844b8c83 | ||
|
|
65e9a8957c | ||
|
|
da868a2fbe | ||
|
|
bceb5287b9 | ||
|
|
84445dec26 | ||
|
|
c70b92b2a4 | ||
|
|
57e582492b | ||
|
|
ec8f3e65c1 | ||
|
|
15841f187d | ||
|
|
ade97495e6 | ||
|
|
2b19285724 | ||
|
|
a444715bf0 |
No files matched your search
@@ -61,6 +61,21 @@ version 2.92
|
||||
|
||||
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
|
||||
|
||||
Change the behaviour of the DHVPv6 server when a REBIND message
|
||||
is received but no lease exists. Under these circumstances a new
|
||||
lease is created _only_ when the --dhcp-authoritative option is
|
||||
set. This matches the behavior of the DHCPv4 server.
|
||||
|
||||
Add --dhcp-split-relay option. This makes a DHCPv4 relay which
|
||||
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
|
||||
|
||||
@@ -95,7 +95,5 @@ a contribution towards my expenses, please use the donation button below.
|
||||
<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal – The safer, easier way to pay online.">
|
||||
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
|
||||
</form>
|
||||
|
||||
|
||||
</BODY>
|
||||
|
||||
</HTML>
|
||||
+29
-3
@@ -673,7 +673,7 @@ machine on which dnsmasq is running) for each
|
||||
local machine. Local machines are those in /etc/hosts or with DHCP
|
||||
leases.
|
||||
.TP
|
||||
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
|
||||
.B \-W, --srv-host=<_service>.<_prot>[.<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
|
||||
Return a SRV DNS record. See RFC2782 for details. If not supplied, the
|
||||
domain defaults to that given by
|
||||
.B --domain.
|
||||
@@ -1487,7 +1487,31 @@ DHCPv4 to a DHCPv6 server or vice-versa.
|
||||
The DHCP relay function for IPv6 includes the ability to snoop
|
||||
prefix-delegation from relayed DHCP transactions. See
|
||||
.B --dhcp-script
|
||||
for details.
|
||||
for details.
|
||||
.TP
|
||||
.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 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 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
|
||||
client-facing relay interface unless explicitly configured: that is a sensible default.
|
||||
The normal default DNS server (the same address as the DHCP server)
|
||||
will not be appropriate when there is no route bewteen the
|
||||
two, so this will have to be explicitly configured.
|
||||
.TP
|
||||
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
|
||||
Map from a vendor-class string to a tag. Most DHCP clients provide a
|
||||
@@ -1739,7 +1763,9 @@ For DHCPv4, it changes the behaviour from strict RFC compliance so that DHCP req
|
||||
unknown leases from unknown hosts are not ignored. This allows new hosts
|
||||
to get a lease without a tedious timeout under all circumstances. It also
|
||||
allows dnsmasq to rebuild its lease database without each client needing to
|
||||
reacquire a lease, if the database is lost. For DHCPv6 it sets the
|
||||
reacquire a lease, if the database is lost. For DHCPv6 it controls the same
|
||||
behaviour as DHCPv4 with missing leases (except for the RFC uncompliance - the
|
||||
DHCPv6 RFCs allow this behaviour if configured). It also sets the
|
||||
priority in replies to 255 (the maximum) instead of 0 (the minimum).
|
||||
.TP
|
||||
.B --dhcp-rapid-commit
|
||||
|
||||
+2870
File diff suppressed because it is too large.
Load diff
+885
-704
File diff suppressed because it is too large.
Load diff
@@ -229,3 +229,5 @@ contents of the cache to the syslog.
|
||||
|
||||
<P>For a complete listing of options please take a look at the manpage
|
||||
dnsmasq(8).
|
||||
</BODY>
|
||||
</HTML>
|
||||
@@ -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
|
||||
|
||||
+11
-1
@@ -290,6 +290,7 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_BSD_NETWORK
|
||||
#define HAVE_GETOPT_LONG
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#define NO_IPSET
|
||||
/* Define before sys/socket.h is included so we get socklen_t */
|
||||
#define _BSD_SOCKLEN_T_
|
||||
/* Select the RFC_3542 version of the IPv6 socket API.
|
||||
@@ -349,7 +350,6 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_LINUX_NETWORK)
|
||||
#undef HAVE_IPSET
|
||||
#undef HAVE_NFTSET
|
||||
#endif
|
||||
|
||||
@@ -357,6 +357,16 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_IPSET
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IPSET)
|
||||
# if defined(HAVE_LINUX_NETWORK)
|
||||
# define HAVE_LINUX_IPSET
|
||||
# elif defined(HAVE_BSD_NETWORK)
|
||||
# define HAVE_BSD_IPSET
|
||||
# else
|
||||
# undef HAVE_IPSET
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef NO_LOOP
|
||||
#undef HAVE_LOOP
|
||||
#endif
|
||||
|
||||
+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,
|
||||
|
||||
+6
-2
@@ -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
|
||||
@@ -1067,10 +1069,12 @@ void log_relay(int family, struct dhcp_relay *relay)
|
||||
{
|
||||
if (broadcast)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
|
||||
else if (relay->split_mode)
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP split-relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
|
||||
}
|
||||
else
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@
|
||||
#define SUBOPT_REMOTE_ID 2
|
||||
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
|
||||
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
|
||||
#define SUBOPT_FLAGS 10 /* RFC 5010 */
|
||||
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
|
||||
|
||||
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */
|
||||
|
||||
+46
-134
@@ -32,8 +32,6 @@ static int complete_context(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
|
||||
struct in_addr netmask, struct in_addr broadcast, void *vparam);
|
||||
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
|
||||
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
|
||||
|
||||
static int make_fd(int port)
|
||||
{
|
||||
@@ -144,13 +142,14 @@ 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;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
struct arpreq arp_req;
|
||||
struct timeval tv;
|
||||
struct in_addr dst_addr;
|
||||
#endif
|
||||
|
||||
union {
|
||||
@@ -176,13 +175,11 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
|
||||
#endif
|
||||
|
||||
#if defined (HAVE_LINUX_NETWORK)
|
||||
if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
|
||||
recvtime = tv.tv_sec;
|
||||
|
||||
dst_addr.s_addr = 0;
|
||||
|
||||
if (msg.msg_controllen >= sizeof(struct cmsghdr))
|
||||
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
|
||||
@@ -194,7 +191,8 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
} p;
|
||||
p.c = CMSG_DATA(cmptr);
|
||||
iface_index = p.p->ipi_ifindex;
|
||||
if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
|
||||
dst_addr = p.p->ipi_addr;
|
||||
if (dst_addr.s_addr != INADDR_BROADCAST)
|
||||
unicast_dest = 1;
|
||||
}
|
||||
|
||||
@@ -224,7 +222,25 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
iface_index = *(p.i);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
union mysockaddr *sockp = NULL;
|
||||
|
||||
# ifdef HAVE_LINUX_NETWORK
|
||||
union mysockaddr tosock;
|
||||
|
||||
sockp = &tosock;
|
||||
tosock.in.sin_port = htons(daemon->dhcp_server_port);
|
||||
tosock.in.sin_addr = dst_addr;
|
||||
tosock.sa.sa_family = AF_INET;
|
||||
# ifdef HAVE_SOCKADDR_SA_LEN
|
||||
tosock.in.sin_len = sizeof(struct sockaddr_in);
|
||||
# endif
|
||||
# endif
|
||||
|
||||
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, sockp, -1);
|
||||
#endif
|
||||
|
||||
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
|
||||
ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
|
||||
return;
|
||||
@@ -286,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;
|
||||
@@ -314,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;
|
||||
@@ -344,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);
|
||||
relay_upstream4(iface_addr, iface_index, mess, (size_t)sz, unicast_dest);
|
||||
|
||||
/* May have configured relay, but not DHCP server */
|
||||
if (!daemon->dhcp)
|
||||
@@ -648,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;
|
||||
}
|
||||
@@ -666,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)
|
||||
@@ -1091,122 +1121,4 @@ char *host_from_dns(struct in_addr addr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
|
||||
{
|
||||
struct in_addr giaddr = mess->giaddr;
|
||||
u8 hops = mess->hops;
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
if (mess->op != BOOTREQUEST)
|
||||
return;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
break;
|
||||
|
||||
/* No relay config. */
|
||||
if (!relay)
|
||||
return;
|
||||
|
||||
for (; relay; relay = relay->next)
|
||||
if (relay->iface_index != 0 && relay->iface_index == iface_index)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
|
||||
mess->hops = hops;
|
||||
mess->giaddr = giaddr;
|
||||
|
||||
if ((mess->hops++) > 20)
|
||||
continue;
|
||||
|
||||
/* source address == relay address */
|
||||
from.addr4 = relay->local.addr4;
|
||||
|
||||
/* 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.s_addr = relay->local.addr4.s_addr;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
|
||||
if (relay->interface)
|
||||
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
|
||||
|
||||
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
|
||||
{
|
||||
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->giaddr = giaddr;
|
||||
}
|
||||
|
||||
|
||||
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
|
||||
{
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
|
||||
return NULL;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
{
|
||||
if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
|
||||
{
|
||||
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
|
||||
return relay->iface_index != 0 ? relay : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
+30
-8
@@ -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;
|
||||
@@ -176,6 +177,7 @@ void dhcp6_packet(time_t now)
|
||||
else
|
||||
{
|
||||
struct dhcp_bridge *bridge, *alias;
|
||||
int multicast_dest = 0;
|
||||
|
||||
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
|
||||
@@ -230,14 +232,24 @@ 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;
|
||||
|
||||
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
|
||||
we're listening there for DHCPv6 server reasons. */
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
|
||||
|
||||
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
|
||||
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
|
||||
return;
|
||||
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
|
||||
multicast_dest = 1;
|
||||
else
|
||||
{
|
||||
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
|
||||
we're listening there for DHCPv6 server reasons. */
|
||||
if (relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=complete_context6}))
|
||||
return;
|
||||
@@ -266,7 +278,7 @@ void dhcp6_packet(time_t now)
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
|
||||
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
|
||||
port = dhcp6_reply(parm.current, multicast_dest, if_index, ifr.ifr_name, &parm.fallback,
|
||||
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
|
||||
|
||||
/* The port in the source address of the original request should
|
||||
@@ -444,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;
|
||||
}
|
||||
|
||||
+17
-2
@@ -1777,9 +1777,9 @@ void clear_cache_and_reload(time_t now)
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
reread_dhcp();
|
||||
if (option_bool(OPT_ETHERS))
|
||||
dhcp_read_ethers();
|
||||
reread_dhcp();
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
@@ -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 */
|
||||
|
||||
+13
-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,10 +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;
|
||||
@@ -1275,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;
|
||||
@@ -1666,6 +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(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 */
|
||||
@@ -1801,7 +1807,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
|
||||
|
||||
/* rfc3315.c */
|
||||
#ifdef HAVE_DHCP6
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now);
|
||||
int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address,
|
||||
@@ -1912,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);
|
||||
@@ -1938,7 +1945,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
char *name, char *limit, int first, int last, int ede);
|
||||
int server_samegroup(struct server *a, struct server *b);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
|
||||
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp);
|
||||
#endif
|
||||
void mark_servers(int flag);
|
||||
void cleanup_servers(void);
|
||||
|
||||
+24
-8
@@ -95,6 +95,7 @@ void build_server_array(void)
|
||||
|
||||
A flag of F_SERVER returns an upstream server only.
|
||||
A flag of F_DNSSECOK disables NODOTS servers from consideration.
|
||||
A flag of F_DS returns parent domain server.
|
||||
A flag of F_DOMAINSRV returns a domain-specific server only.
|
||||
A flag of F_CONFIG returns anything that generates a local
|
||||
reply of IPv4 or IPV6.
|
||||
@@ -106,12 +107,23 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
ssize_t qlen;
|
||||
int try, high, low = 0;
|
||||
int nlow = 0, nhigh = 0;
|
||||
char *cp, *qdomain = domain;
|
||||
|
||||
char *cp, *qdomain;
|
||||
|
||||
/* may be no configured servers. */
|
||||
if (daemon->serverarraysz == 0)
|
||||
return 0;
|
||||
|
||||
/* DS records should come from the parent domain. */
|
||||
if (flags & F_DS)
|
||||
{
|
||||
if ((cp = strchr(domain, '.')))
|
||||
domain = cp+1;
|
||||
else
|
||||
domain = "";
|
||||
}
|
||||
|
||||
qdomain = domain;
|
||||
|
||||
/* find query length and presence of '.' */
|
||||
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
|
||||
if (*cp == '.')
|
||||
@@ -244,7 +256,10 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
if (nodots &&
|
||||
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
|
||||
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
|
||||
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
||||
{
|
||||
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
|
||||
qlen = 0;
|
||||
}
|
||||
|
||||
if (lowout)
|
||||
*lowout = nlow;
|
||||
@@ -343,7 +358,8 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
else
|
||||
{
|
||||
/* If we want a server for a particular domain, and this one isn't, return nothing. */
|
||||
if (nlow < daemon->serverarraysz && nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
if (nlow < daemon->serverarraysz && nlow != nhigh && (flags & F_DOMAINSRV) &&
|
||||
daemon->serverarray[nlow]->domain_len == 0 && !(daemon->serverarray[nlow]->flags & SERV_FOR_NODOTS))
|
||||
nlow = nhigh;
|
||||
}
|
||||
}
|
||||
@@ -399,7 +415,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
gotname &= ~F_QUERY;
|
||||
gotname &= ~(F_QUERY | F_DS);
|
||||
|
||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
|
||||
@@ -459,14 +475,14 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
|
||||
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp)
|
||||
{
|
||||
int first, last, index;
|
||||
|
||||
|
||||
/* Find server to send DNSSEC query to. This will normally be the
|
||||
same as for the original query, but may be another if
|
||||
servers for domains are involved. */
|
||||
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last))
|
||||
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK | (is_ds ? F_DS : 0), &first, &last))
|
||||
return -1;
|
||||
|
||||
for (index = first; index != last; index++)
|
||||
|
||||
+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;
|
||||
}
|
||||
|
||||
+54
-43
@@ -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)))
|
||||
{
|
||||
@@ -763,6 +763,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
/* Complain loudly if the upstream server is non-recursive. */
|
||||
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
|
||||
server && !(server->flags & SERV_WARNED_RECURSIVE))
|
||||
{
|
||||
(void)prettyprint_addr(&server->addr, daemon->namebuff);
|
||||
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
|
||||
if (!option_bool(OPT_LOG))
|
||||
server->flags |= SERV_WARNED_RECURSIVE;
|
||||
}
|
||||
|
||||
header->hb4 |= HB4_RA; /* recursion if available */
|
||||
|
||||
if (OPCODE(header) != QUERY)
|
||||
@@ -778,16 +788,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
return resize_packet(header, n, pheader, plen);
|
||||
}
|
||||
|
||||
/* Complain loudly if the upstream server is non-recursive. */
|
||||
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
|
||||
server && !(server->flags & SERV_WARNED_RECURSIVE))
|
||||
{
|
||||
(void)prettyprint_addr(&server->addr, daemon->namebuff);
|
||||
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
|
||||
if (!option_bool(OPT_LOG))
|
||||
server->flags |= SERV_WARNED_RECURSIVE;
|
||||
}
|
||||
|
||||
if (header->hb3 & HB3_TC)
|
||||
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
|
||||
else if (!bogusanswer || (header->hb4 & HB4_CD))
|
||||
@@ -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
|
||||
@@ -1024,7 +1024,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
|
||||
/* Make sure we don't expire and free the orig frec during the
|
||||
allocation of a new one: third arg of get_new_frec() does that. */
|
||||
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 &&
|
||||
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
|
||||
(server = daemon->serverarray[serverind]) &&
|
||||
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
daemon->keyname, forward->class, get_id(),
|
||||
@@ -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);
|
||||
@@ -2199,7 +2199,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
first = start = server->arrayposn;
|
||||
last = first + 1;
|
||||
|
||||
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, &first, &last)) == -1)
|
||||
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, STAT_ISEQUAL(status, STAT_NEED_DS), &first, &last)) == -1)
|
||||
new_status = STAT_ABANDONED;
|
||||
else
|
||||
{
|
||||
@@ -2307,7 +2307,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||
|
||||
if ((start = dnssec_server(server, keyname, &first, &last)) == -1)
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_IPSET)
|
||||
#if defined(HAVE_LINUX_IPSET)
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
+30
-6
@@ -196,6 +196,7 @@ struct myoption {
|
||||
#define LOPT_NO_ENCODE 387
|
||||
#define LOPT_DO_ENCODE 388
|
||||
#define LOPT_LEASEQUERY 389
|
||||
#define LOPT_SPLIT_RELAY 390
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -374,6 +375,7 @@ static const struct myoption opts[] =
|
||||
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
|
||||
{ "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS },
|
||||
{ "dhcp-relay", 1, 0, LOPT_RELAY },
|
||||
{ "dhcp-split-relay", 1, 0, LOPT_SPLIT_RELAY },
|
||||
{ "ra-param", 1, 0, LOPT_RA_PARAM },
|
||||
{ "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
|
||||
{ "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
|
||||
@@ -542,6 +544,7 @@ static struct {
|
||||
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
|
||||
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
|
||||
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
|
||||
{ LOPT_SPLIT_RELAY, ARG_DUP, "<local-addr>,<server>,<iface>", gettext_noop("Relay DHCP requests to a remote server"), NULL},
|
||||
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
|
||||
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
|
||||
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
|
||||
@@ -2917,13 +2920,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
# if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
# endif
|
||||
case LOPT_LEASEQUERY:
|
||||
set_option_bool(OPT_LEASEQUERY);
|
||||
if (!arg)
|
||||
break;
|
||||
#pragma GCC diagnostic pop
|
||||
# if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
case 'B': /* --bogus-nxdomain */
|
||||
case LOPT_IGNORE_ADDR: /* --ignore-address */
|
||||
@@ -4712,11 +4719,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
|
||||
case LOPT_RELAY: /* --dhcp-relay */
|
||||
case LOPT_SPLIT_RELAY: /* --dhcp-splt-relay */
|
||||
{
|
||||
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
|
||||
char *two = split(arg);
|
||||
char *three = split(two);
|
||||
|
||||
|
||||
if (option == LOPT_SPLIT_RELAY)
|
||||
{
|
||||
new->split_mode = 1;
|
||||
|
||||
/* split mode must have two addresses and a non-wildcard interface name. */
|
||||
if (!three || strchr(three, '*'))
|
||||
two = NULL;
|
||||
}
|
||||
|
||||
new->iface_index = 0;
|
||||
|
||||
if (two)
|
||||
@@ -4739,12 +4756,15 @@ 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;
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, arg, &new->local))
|
||||
else if (inet_pton(AF_INET6, arg, &new->local) && !new->split_mode)
|
||||
{
|
||||
char *hash = split_chr(two, '#');
|
||||
|
||||
@@ -4765,7 +4785,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
daemon->relay6 = new;
|
||||
}
|
||||
#endif
|
||||
|
||||
else
|
||||
two = NULL;
|
||||
|
||||
new->interface = opt_string_alloc(three);
|
||||
}
|
||||
|
||||
@@ -6258,6 +6280,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
strchr(srv->name, '.') &&
|
||||
strchr(srv->name, '.') == strrchr(srv->name, '.'))
|
||||
{
|
||||
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
|
||||
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
|
||||
strcpy(buff, srv->name);
|
||||
strcat(buff, ".");
|
||||
strcat(buff, daemon->domain_suffix);
|
||||
|
||||
+24
-11
@@ -415,6 +415,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
|
||||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
|
||||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
|
||||
((ip_addr & 0xFFC00000) == 0x64400000) /* 100.64.0.0/10 (CG-NAT) RFC6598/RFC7793*/ ||
|
||||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
|
||||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
|
||||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
|
||||
@@ -515,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;
|
||||
@@ -555,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;
|
||||
@@ -567,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))
|
||||
@@ -587,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;
|
||||
|
||||
@@ -1078,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)
|
||||
{
|
||||
@@ -1100,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1244,12 +1259,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
return F_IPV4 | F_IPV6;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Make the behaviour for DS and DNSKEY queries we forward the same
|
||||
as for DS and DNSKEY queries we originate. */
|
||||
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
|
||||
return F_DNSSECOK;
|
||||
#endif
|
||||
if (qtype == T_DS || qtype == T_DNSKEY)
|
||||
return F_DNSSECOK | (qtype == T_DS ? F_DS : 0);
|
||||
|
||||
return F_QUERY;
|
||||
}
|
||||
|
||||
+219
-1
@@ -204,6 +204,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
memcpy(agent_id, opt, total);
|
||||
}
|
||||
|
||||
/* look for RFC5010 flags sub-option */
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_FLAGS, INADDRSZ)))
|
||||
unicast_dest = !!(option_uint(opt, 0, 1) & 0x80);
|
||||
|
||||
/* look for RFC3527 Link selection sub-option */
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
|
||||
subnet_addr = option_addr(sopt);
|
||||
@@ -289,6 +293,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
{
|
||||
addr = subnet_addr;
|
||||
force = 1;
|
||||
if (mess->giaddr.s_addr)
|
||||
via_relay = 1;
|
||||
}
|
||||
else if (mess->giaddr.s_addr)
|
||||
{
|
||||
@@ -357,7 +363,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (context_tmp->local.s_addr == 0)
|
||||
context_tmp->local = fallback;
|
||||
if (context_tmp->router.s_addr == 0 && !share)
|
||||
context_tmp->router = mess->giaddr;
|
||||
{
|
||||
if (override.s_addr)
|
||||
context_tmp->router = override;
|
||||
else
|
||||
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 )
|
||||
@@ -3044,4 +3055,211 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
struct dhcp_relay *relay;
|
||||
size_t orig_sz = sz;
|
||||
unsigned char *endopt = NULL;
|
||||
|
||||
if (mess->op != BOOTREQUEST || (mess->hops++) > 20)
|
||||
return;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
{
|
||||
union mysockaddr to;
|
||||
union all_addr from;
|
||||
struct ifreq ifr;
|
||||
|
||||
/* restore orig packet */
|
||||
mess->giaddr = giaddr;
|
||||
if (endopt)
|
||||
*endopt = OPTION_END;
|
||||
sz = orig_sz;
|
||||
|
||||
if (relay->interface)
|
||||
{
|
||||
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
mess->giaddr = giaddr;
|
||||
if (endopt)
|
||||
*endopt = OPTION_END;
|
||||
}
|
||||
|
||||
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 0;
|
||||
|
||||
for (relay = daemon->relay4; relay; relay = relay->next)
|
||||
{
|
||||
unsigned int return_iface = 0;
|
||||
|
||||
if (relay->split_mode)
|
||||
{
|
||||
unsigned char *opt, *sopt;
|
||||
|
||||
/* giaddr is our address on the returning interface in split mode. */
|
||||
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)
|
||||
return_iface = relay->iface_index;
|
||||
|
||||
if (return_iface && (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
|
||||
return return_iface;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_DHCP */
|
||||
+81
-81
@@ -21,7 +21,7 @@
|
||||
|
||||
struct state {
|
||||
unsigned char *clid;
|
||||
int clid_len, ia_type, interface, hostname_auth, lease_allocate;
|
||||
int multicast_dest, clid_len, ia_type, interface, hostname_auth, lease_allocate;
|
||||
char *client_hostname, *hostname, *domain, *send_domain;
|
||||
struct dhcp_context *context;
|
||||
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
|
||||
@@ -68,7 +68,7 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
|
||||
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
|
||||
|
||||
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
|
||||
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
|
||||
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
|
||||
size_t sz, struct in6_addr *client_addr, time_t now)
|
||||
{
|
||||
@@ -87,6 +87,7 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
|
||||
|
||||
reset_counter();
|
||||
state.context = context;
|
||||
state.multicast_dest = multicast_dest;
|
||||
state.interface = interface;
|
||||
state.iface_name = iface_name;
|
||||
state.fallback = fallback;
|
||||
@@ -244,7 +245,11 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
struct in6_addr align;
|
||||
/* the packet data is unaligned, copy to aligned storage */
|
||||
memcpy(&align, inbuff + 2, IN6ADDRSZ);
|
||||
state->link_address = &align;
|
||||
|
||||
|
||||
/* RFC6221 para 4 */
|
||||
if (!IN6_IS_ADDR_UNSPECIFIED(&align))
|
||||
state->link_address = &align;
|
||||
/* zero is_unicast since that is now known to refer to the
|
||||
relayed packet, not the original sent by the client */
|
||||
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
|
||||
@@ -333,20 +338,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
else if (msg_type != DHCP6IREQ)
|
||||
return 0;
|
||||
|
||||
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
|
||||
have a server-id. 3315 para 15.x */
|
||||
opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1);
|
||||
|
||||
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND)
|
||||
|
||||
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND || msg_type == DHCP6IREQ)
|
||||
{
|
||||
if (opt)
|
||||
/* Above message types must be multicast 3315 Section 15. */
|
||||
if (!state->multicast_dest)
|
||||
return 0;
|
||||
}
|
||||
else if (msg_type == DHCP6IREQ)
|
||||
{
|
||||
/* If server-id provided, it must match. */
|
||||
if (opt && (opt6_len(opt) != daemon->duid_len ||
|
||||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
|
||||
|
||||
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
|
||||
have a server-id. 3315 para 15.x */
|
||||
if (msg_type == DHCP6IREQ)
|
||||
{
|
||||
/* If server-id provided in IREQ, it must match. */
|
||||
if (opt && (opt6_len(opt) != daemon->duid_len ||
|
||||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
|
||||
return 0;
|
||||
}
|
||||
else if (opt)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
@@ -634,7 +643,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
case DHCP6SOLICIT:
|
||||
{
|
||||
int address_assigned = 0;
|
||||
int address_assigned;
|
||||
/* tags without all prefix-class tags */
|
||||
struct dhcp_netid *solicit_tags;
|
||||
struct dhcp_context *c;
|
||||
@@ -653,6 +662,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
request_no_address:
|
||||
solicit_tags = tagif;
|
||||
address_assigned = 0;
|
||||
|
||||
if (ignore)
|
||||
return 0;
|
||||
@@ -764,15 +774,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
if (address_assigned != 1)
|
||||
{
|
||||
/* If the server will not assign any addresses to any IAs in a
|
||||
subsequent Request from the client, the server MUST send an Advertise
|
||||
message to the client that doesn't include any IA options. */
|
||||
if (!state->lease_allocate)
|
||||
{
|
||||
save_counter(o);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If the server cannot assign any addresses to an IA in the message
|
||||
from the client, the server MUST include the IA in the Reply message
|
||||
with no addresses in the IA and a Status Code option in the IA
|
||||
@@ -799,7 +800,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
o = new_opt6(OPTION6_PREFERENCE);
|
||||
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
|
||||
end_opt6(o);
|
||||
tagif = add_options(state, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -819,7 +819,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tagif = add_options(state, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -977,8 +978,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
{
|
||||
if (msg_type == DHCP6REBIND)
|
||||
{
|
||||
/* When rebinding, we can create a lease if it doesn't exist. */
|
||||
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
|
||||
/* When rebinding, we can create a lease if it doesn't exist, as long
|
||||
as --dhcp-authoritative is set. */
|
||||
if (option_bool(OPT_AUTHORITATIVE))
|
||||
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
|
||||
if (lease)
|
||||
lease_set_iaid(lease, state->iaid);
|
||||
else
|
||||
@@ -2276,64 +2279,61 @@ int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
|
||||
{
|
||||
void *opt, *opts = inbuff + 34;
|
||||
void *end = inbuff + sz;
|
||||
for (opt = opts; opt; opt = opt6_next(opt, end))
|
||||
if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
|
||||
{
|
||||
int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
|
||||
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
|
||||
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
|
||||
peer->sin6_scope_id = relay->iface_index;
|
||||
|
||||
if (encap_type == DHCP6RELAYREPL)
|
||||
{
|
||||
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
|
||||
{
|
||||
/* decapsulate relayed message */
|
||||
opts = opt6_ptr(opt, 4);
|
||||
end = opt6_ptr(opt, opt6_len(opt));
|
||||
|
||||
for (opt = opts; opt; opt = opt6_next(opt, end))
|
||||
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
|
||||
{
|
||||
void *ia_opts = opt6_ptr(opt, 12);
|
||||
void *ia_end = opt6_ptr(opt, opt6_len(opt));
|
||||
void *ia_opt;
|
||||
|
||||
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
|
||||
/* valid lifetime must not be zero. */
|
||||
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
|
||||
{
|
||||
if (daemon->free_snoops ||
|
||||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
|
||||
{
|
||||
struct snoop_record *snoop = daemon->free_snoops;
|
||||
|
||||
daemon->free_snoops = snoop->next;
|
||||
snoop->client = peer->sin6_addr;
|
||||
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
|
||||
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
|
||||
snoop->next = relay->snoop_records;
|
||||
relay->snoop_records = snoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((opt = opt6_find(opts, end, OPTION6_RELAY_MSG, 4)))
|
||||
{
|
||||
int encap_type = opt6_uint(opt, 0, 1);
|
||||
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
|
||||
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
|
||||
peer->sin6_scope_id = relay->iface_index;
|
||||
|
||||
if (encap_type == DHCP6RELAYREPL)
|
||||
{
|
||||
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
|
||||
{
|
||||
/* skip over message type and transaction-id. to get to options. */
|
||||
opts = opt6_ptr(opt, 4);
|
||||
end = opt6_ptr(opt, opt6_len(opt));
|
||||
|
||||
if ((opt = opt6_find(opts, end, OPTION6_IA_PD, 12)))
|
||||
{
|
||||
opts = opt6_ptr(opt, 12);
|
||||
end = opt6_ptr(opt, opt6_len(opt));
|
||||
|
||||
for (opt = opt6_find(opts, end, OPTION6_IAPREFIX, 25); opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IAPREFIX, 25))
|
||||
/* valid lifetime must not be zero. */
|
||||
if (opt6_uint(opt, 4, 4) != 0)
|
||||
{
|
||||
if (daemon->free_snoops ||
|
||||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
|
||||
{
|
||||
struct snoop_record *snoop = daemon->free_snoops;
|
||||
|
||||
daemon->free_snoops = snoop->next;
|
||||
snoop->client = peer->sin6_addr;
|
||||
snoop->prefix_len = opt6_uint(opt, 8, 1);
|
||||
memcpy(&snoop->prefix, opt6_ptr(opt, 9), IN6ADDRSZ);
|
||||
snoop->next = relay->snoop_records;
|
||||
relay->snoop_records = snoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_SCRIPT
|
||||
int do_snoop_script_run(void)
|
||||
{
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
|
||||
#if defined(HAVE_BSD_IPSET)
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
||||
+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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
-4
@@ -34,6 +34,10 @@
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BSD_NETWORK
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
/* SURF random number generator */
|
||||
|
||||
static u32 seed[32];
|
||||
@@ -831,9 +835,34 @@ void close_fds(long max_fd, int spare1, int spare2, int spare3)
|
||||
#endif
|
||||
|
||||
#ifdef FDESCFS
|
||||
DIR *d;
|
||||
DIR *d = NULL;
|
||||
|
||||
if ((d = opendir(FDESCFS)))
|
||||
# ifdef HAVE_BSD_NETWORK
|
||||
dev_t dirdev = 0;
|
||||
char fdescfs[] = FDESCFS; /* string must be writable */
|
||||
struct stat statbuf;
|
||||
|
||||
/* On BSD, fdescfs is normally mounted at /dev/fd. However
|
||||
if it is NOT mounted, devfs creates a directory at /dev/fd
|
||||
which contains (only) the file descriptors 0,1 and 2.
|
||||
|
||||
Under these conditions, opendir() will succeed, and
|
||||
if we proceed we will fail to close extant
|
||||
file descriptors which should be closed.
|
||||
|
||||
Check that there is a filesystem mounted at /dev/fd
|
||||
by checking that the device changes between /dev/fd
|
||||
and /dev. If if doesn't, fall back to the dumb path. */
|
||||
|
||||
if (stat(fdescfs, &statbuf) != -1)
|
||||
dirdev = statbuf.st_dev;
|
||||
|
||||
if (stat(dirname(fdescfs), &statbuf) != -1 &&
|
||||
dirdev != statbuf.st_dev)
|
||||
# endif
|
||||
d = opendir(FDESCFS);
|
||||
|
||||
if (d)
|
||||
{
|
||||
struct dirent *de;
|
||||
|
||||
@@ -855,9 +884,9 @@ void close_fds(long max_fd, int spare1, int spare2, int spare3)
|
||||
|
||||
closedir(d);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/* fallback, dumb code. */
|
||||
for (max_fd--; max_fd >= 0; max_fd--)
|
||||
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO &&
|
||||
|
||||
Reference in new issue
Block a user