mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
20
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
287d6bc88d | ||
|
|
c378d2c1de | ||
|
|
5846f749e5 | ||
|
|
c9a4240ec4 | ||
|
|
e7b87dee85 | ||
|
|
90b248582c | ||
|
|
ebef27f321 | ||
|
|
1861a881eb | ||
|
|
96bdb42d40 | ||
|
|
c7a909ad65 | ||
|
|
baf3c57af5 | ||
|
|
e48a2af4f5 | ||
|
|
91b800cc62 | ||
|
|
075e4a56b7 | ||
|
|
48658ebc54 | ||
|
|
692ed0dd32 | ||
|
|
43805c1859 | ||
|
|
4fbe1add95 | ||
|
|
57c7ae8fc0 | ||
|
|
d1008215dc |
No files matched your search
@@ -19,6 +19,48 @@ version 2.92
|
||||
would fail to spot an invalid domain. Thanks to Graham Clinch
|
||||
for spotting the problem.
|
||||
|
||||
Add --log-queries=auth option to only log replies from the auth DNS
|
||||
facility.
|
||||
|
||||
Fix some edge-cases with domains and --address and --server. There
|
||||
has been some regressions with this in previous releases. This change
|
||||
fixes the priority order from loqwer to highest as:
|
||||
--address with a IPv4 or IPv6 address (as long as the query matches the type)
|
||||
--address with # for all-zeros, as long as the query is A or AAAA)
|
||||
--address with no address, which returns NXDOMAIN or NOERROR for all types.
|
||||
--server with address set to # to use the unqualified servers.
|
||||
--server with matching domain.
|
||||
--server without domain or from /etc/resolv.conf.
|
||||
|
||||
Fix problems with ipset or nftset and TCP DNS transport. Previously
|
||||
this was racy, and insertion of addresses could fail on a busy server
|
||||
when DNS-over-TCP transport was involved.
|
||||
|
||||
DNSSEC validation change for reverse lookups in RFC-1918 ranges and friends.
|
||||
The large public DNS services seem not to return proof-of-nonexistence
|
||||
for DS records at the start of RFC-1918 in-addr.arpa domains and the their
|
||||
IPv6 equivalents. 10.in-addr.arpa, 168.192.in-addr.arpa etc.
|
||||
Since dnsmasq already has an option which instructs it not bother
|
||||
upstream servers with pointless queries about these address ranges,
|
||||
namely --bogus-priv, we extend that to enable behaviour which allows
|
||||
dnsmasq to assume that insecure NXDOMAIN replies for these domains
|
||||
are expected and to assume that the domains are legitimately unsigned.
|
||||
This behaviour only matters when some address range is directed to
|
||||
another upstream server using --rev-server. In that case it allows
|
||||
replies from that server to pass DNSSEC validation. Without such a
|
||||
server configured, queries are never sent upstream so they are never
|
||||
validated and the new behaviour is moot.
|
||||
|
||||
Add support for leasequery to the dnsmasq DHCPv4 server.
|
||||
This has to be specifically enabled with the --leasequery option.
|
||||
Many thanks to JAXPORT, Jacksonville Port Authority for sponsoring
|
||||
this enhancement to dnsmasq.
|
||||
|
||||
Fix failure to cache PTR RRs when a reply contains more than one answer.
|
||||
Thanks to Dmitry for spotting this.
|
||||
|
||||
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
|
||||
|
||||
|
||||
version 2.91
|
||||
Fix spurious "resource limit exceeded messages". Thanks to
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
/* leasequery is Copyright (c) 2025 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Author's email: simon@thekelleys.org.uk */
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <netdb.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/capability.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned int u32;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
#define INADDRSZ 4
|
||||
#define ADDRSTRLEN 46
|
||||
|
||||
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
|
||||
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
|
||||
|
||||
#define DHCP_CHADDR_MAX 16
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
#define BOOTREQUEST 1
|
||||
#define BOOTREPLY 2
|
||||
|
||||
#define DHCP_COOKIE 0x63825363
|
||||
|
||||
#define DHCPLEASEQUERY 10
|
||||
#define DHCPLEASEUNASSIGNED 11
|
||||
#define DHCPLEASEUNKNOWN 12
|
||||
#define DHCPLEASEACTIVE 13
|
||||
|
||||
#define OPTION_END 255
|
||||
#define OPTION_ROUTER 3
|
||||
#define OPTION_VENDOR_CLASS_OPT 43
|
||||
#define OPTION_LEASE_TIME 51
|
||||
#define OPTION_MESSAGE_TYPE 53
|
||||
#define OPTION_SERVER_IDENTIFIER 54
|
||||
#define OPTION_REQUESTED_OPTIONS 55
|
||||
#define OPTION_VENDOR_ID 60
|
||||
#define OPTION_CLIENT_ID 61
|
||||
|
||||
/* flags in top of length field for DHCP-option tables */
|
||||
#define OT_ADDR_LIST 0x8000
|
||||
#define OT_RFC1035_NAME 0x4000
|
||||
#define OT_INTERNAL 0x2000
|
||||
#define OT_NAME 0x1000
|
||||
#define OT_CSTRING 0x0800
|
||||
#define OT_DEC 0x0400
|
||||
#define OT_TIME 0x0200
|
||||
|
||||
#define GETSHORT(s, cp) do { \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
(s) = ((u16)t_cp[0] << 8) \
|
||||
| ((u16)t_cp[1]) \
|
||||
; \
|
||||
(cp) += 2; \
|
||||
} while(0)
|
||||
|
||||
#define GETLONG(l, cp) do { \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
(l) = ((u32)t_cp[0] << 24) \
|
||||
| ((u32)t_cp[1] << 16) \
|
||||
| ((u32)t_cp[2] << 8) \
|
||||
| ((u32)t_cp[3]) \
|
||||
; \
|
||||
(cp) += 4; \
|
||||
} while (0)
|
||||
|
||||
#define PUTSHORT(s, cp) do { \
|
||||
u16 t_s = (u16)(s); \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
*t_cp++ = t_s >> 8; \
|
||||
*t_cp = t_s; \
|
||||
(cp) += 2; \
|
||||
} while(0)
|
||||
|
||||
#define PUTLONG(l, cp) do { \
|
||||
u32 t_l = (u32)(l); \
|
||||
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||
*t_cp++ = t_l >> 24; \
|
||||
*t_cp++ = t_l >> 16; \
|
||||
*t_cp++ = t_l >> 8; \
|
||||
*t_cp = t_l; \
|
||||
(cp) += 4; \
|
||||
} while (0)
|
||||
|
||||
union all_addr {
|
||||
struct in_addr addr4;
|
||||
struct in6_addr addr6;
|
||||
};
|
||||
|
||||
struct dhcp_packet_with_opts{
|
||||
struct dhcp_packet {
|
||||
unsigned char op, htype, hlen, hops;
|
||||
unsigned int xid;
|
||||
unsigned short secs, flags;
|
||||
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
|
||||
unsigned char chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
|
||||
} header;
|
||||
unsigned char options[312];
|
||||
};
|
||||
|
||||
static const struct opttab_t {
|
||||
char *name;
|
||||
u16 val, size;
|
||||
} opttab[] = {
|
||||
{ "netmask", 1, OT_ADDR_LIST },
|
||||
{ "time-offset", 2, 4 },
|
||||
{ "router", 3, OT_ADDR_LIST },
|
||||
{ "dns-server", 6, OT_ADDR_LIST },
|
||||
{ "log-server", 7, OT_ADDR_LIST },
|
||||
{ "lpr-server", 9, OT_ADDR_LIST },
|
||||
{ "hostname", 12, OT_INTERNAL | OT_NAME },
|
||||
{ "boot-file-size", 13, 2 | OT_DEC },
|
||||
{ "domain-name", 15, OT_NAME },
|
||||
{ "swap-server", 16, OT_ADDR_LIST },
|
||||
{ "root-path", 17, OT_NAME },
|
||||
{ "extension-path", 18, OT_NAME },
|
||||
{ "ip-forward-enable", 19, 1 },
|
||||
{ "non-local-source-routing", 20, 1 },
|
||||
{ "policy-filter", 21, OT_ADDR_LIST },
|
||||
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
|
||||
{ "default-ttl", 23, 1 | OT_DEC },
|
||||
{ "mtu", 26, 2 | OT_DEC },
|
||||
{ "all-subnets-local", 27, 1 },
|
||||
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "router-discovery", 31, 1 },
|
||||
{ "router-solicitation", 32, OT_ADDR_LIST },
|
||||
{ "static-route", 33, OT_ADDR_LIST },
|
||||
{ "trailer-encapsulation", 34, 1 },
|
||||
{ "arp-timeout", 35, 4 | OT_DEC },
|
||||
{ "ethernet-encap", 36, 1 },
|
||||
{ "tcp-ttl", 37, 1 },
|
||||
{ "tcp-keepalive", 38, 4 | OT_DEC },
|
||||
{ "nis-domain", 40, OT_NAME },
|
||||
{ "nis-server", 41, OT_ADDR_LIST },
|
||||
{ "ntp-server", 42, OT_ADDR_LIST },
|
||||
{ "vendor-encap", 43, OT_INTERNAL },
|
||||
{ "netbios-ns", 44, OT_ADDR_LIST },
|
||||
{ "netbios-dd", 45, OT_ADDR_LIST },
|
||||
{ "netbios-nodetype", 46, 1 },
|
||||
{ "netbios-scope", 47, 0 },
|
||||
{ "x-windows-fs", 48, OT_ADDR_LIST },
|
||||
{ "x-windows-dm", 49, OT_ADDR_LIST },
|
||||
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
|
||||
{ "option-overload", 52, OT_INTERNAL },
|
||||
{ "message-type", 53, OT_INTERNAL | OT_DEC },
|
||||
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
|
||||
{ "parameter-request", 55, OT_INTERNAL },
|
||||
{ "message", 56, OT_INTERNAL },
|
||||
{ "max-message-size", 57, OT_INTERNAL },
|
||||
{ "T1", 58, OT_TIME},
|
||||
{ "T2", 59, OT_TIME},
|
||||
{ "vendor-class", 60, OT_NAME },
|
||||
{ "client-id", 61, OT_INTERNAL },
|
||||
{ "nis+-domain", 64, OT_NAME },
|
||||
{ "nis+-server", 65, OT_ADDR_LIST },
|
||||
{ "tftp-server", 66, OT_NAME },
|
||||
{ "bootfile-name", 67, OT_NAME },
|
||||
{ "mobile-ip-home", 68, OT_ADDR_LIST },
|
||||
{ "smtp-server", 69, OT_ADDR_LIST },
|
||||
{ "pop3-server", 70, OT_ADDR_LIST },
|
||||
{ "nntp-server", 71, OT_ADDR_LIST },
|
||||
{ "irc-server", 74, OT_ADDR_LIST },
|
||||
{ "user-class", 77, 0 },
|
||||
{ "rapid-commit", 80, 0 },
|
||||
{ "FQDN", 81, OT_INTERNAL },
|
||||
{ "agent-info", 82, OT_INTERNAL },
|
||||
{ "last-transaction", 91, 4 | OT_TIME },
|
||||
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "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 */
|
||||
{ "subnet-select", 118, OT_INTERNAL },
|
||||
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||
{ "sip-server", 120, 0 },
|
||||
{ "classless-static-route", 121, 0 },
|
||||
{ "vendor-id-encap", 125, 0 },
|
||||
{ "tftp-server-address", 150, OT_ADDR_LIST },
|
||||
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
|
||||
{ NULL, 0, 0 }
|
||||
};
|
||||
|
||||
static void prettyprint_time(char *buf, unsigned int t);
|
||||
static char *print_mac(char *buff, unsigned char *mac, int len);
|
||||
|
||||
int lookup_dhcp_opt(char *name)
|
||||
{
|
||||
const struct opttab_t *t = opttab;
|
||||
int i;
|
||||
|
||||
for (i = 0; t[i].name; i++)
|
||||
if (strcasecmp(t[i].name, name) == 0)
|
||||
return t[i].val;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *option_string(unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
|
||||
{
|
||||
int o, i, j, nodecode = 0;
|
||||
const struct opttab_t *ot = opttab;
|
||||
char addrbuff[ADDRSTRLEN];
|
||||
|
||||
for (o = 0; ot[o].name; o++)
|
||||
if (ot[o].val == opt)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
memset(buf, 0, buf_len);
|
||||
|
||||
if (ot[o].size & OT_ADDR_LIST)
|
||||
{
|
||||
union all_addr addr;
|
||||
int addr_len = INADDRSZ;
|
||||
|
||||
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
|
||||
{
|
||||
if (i != 0)
|
||||
strncat(buf, ", ", buf_len - strlen(buf));
|
||||
/* align */
|
||||
memcpy(&addr, &val[i], addr_len);
|
||||
inet_ntop(AF_INET, &val[i], addrbuff, ADDRSTRLEN);
|
||||
strncat(buf, addrbuff, buf_len - strlen(buf));
|
||||
}
|
||||
}
|
||||
else if (ot[o].size & OT_NAME)
|
||||
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
|
||||
{
|
||||
char c = val[i];
|
||||
if (isprint((unsigned char)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||
{
|
||||
unsigned int dec = 0;
|
||||
|
||||
for (i = 0; i < opt_len; i++)
|
||||
dec = (dec << 8) | val[i];
|
||||
|
||||
if (ot[o].size & OT_TIME)
|
||||
prettyprint_time(buf, dec);
|
||||
else
|
||||
sprintf(buf, "%u", dec);
|
||||
}
|
||||
else
|
||||
nodecode = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
|
||||
{
|
||||
int trunc = 0;
|
||||
if (opt_len > 14)
|
||||
{
|
||||
trunc = 1;
|
||||
opt_len = 14;
|
||||
}
|
||||
print_mac(buf, val, opt_len);
|
||||
if (trunc)
|
||||
strncat(buf, "...", buf_len - strlen(buf));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ot[o].name ? ot[o].name : "";
|
||||
|
||||
}
|
||||
|
||||
static void prettyprint_time(char *buf, unsigned int t)
|
||||
{
|
||||
if (t == 0xffffffff)
|
||||
sprintf(buf, "infinite");
|
||||
else
|
||||
{
|
||||
unsigned int x, p = 0;
|
||||
if ((x = t/86400))
|
||||
p += sprintf(&buf[p], "%ud", x);
|
||||
if ((x = (t/3600)%24))
|
||||
p += sprintf(&buf[p], "%uh", x);
|
||||
if ((x = (t/60)%60))
|
||||
p += sprintf(&buf[p], "%um", x);
|
||||
if ((x = t%60))
|
||||
sprintf(&buf[p], "%us", x);
|
||||
}
|
||||
}
|
||||
|
||||
static char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
{
|
||||
char *p = buff;
|
||||
int i;
|
||||
|
||||
if (len == 0)
|
||||
sprintf(p, "<null>");
|
||||
else
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
static unsigned char *dhcp_skip_opts(struct dhcp_packet_with_opts *pktp)
|
||||
{
|
||||
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||
while (*start != 0)
|
||||
start += start[1] + 2;
|
||||
return start;
|
||||
}
|
||||
|
||||
static unsigned char *dhcp_find_opt(struct dhcp_packet_with_opts *pktp, int optno)
|
||||
{
|
||||
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||
while (*start != OPTION_END)
|
||||
{
|
||||
if (*start == optno)
|
||||
return start;
|
||||
start += start[1] + 2;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void option_put(struct dhcp_packet_with_opts *pktp, int opt, int len, unsigned int val)
|
||||
{
|
||||
int i;
|
||||
unsigned char *p = dhcp_skip_opts(pktp);
|
||||
|
||||
if (p)
|
||||
{
|
||||
*p++ = opt;
|
||||
*p++ = len;
|
||||
for (i = 0; i < len; i++)
|
||||
*(p++) = val >> (8 * (len - (i + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
static void option_put_string(struct dhcp_packet_with_opts *pktp, int opt,
|
||||
const char *string, int null_term)
|
||||
{
|
||||
unsigned char *p;
|
||||
size_t len = strlen(string);
|
||||
|
||||
if (null_term && len != 255)
|
||||
len++;
|
||||
|
||||
if ((p = dhcp_skip_opts(pktp)))
|
||||
{
|
||||
*p++ = opt;
|
||||
*p++ = len;
|
||||
memcpy(p, string, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* in may equal out, when maxlen may be -1 (No max len).
|
||||
Return -1 for extraneous no-hex chars found. */
|
||||
int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
unsigned int *wildcard_mask, int *mac_type)
|
||||
{
|
||||
int done = 0, mask = 0, i = 0;
|
||||
char *r;
|
||||
|
||||
if (mac_type)
|
||||
*mac_type = 0;
|
||||
|
||||
while (!done && (maxlen == -1 || i < maxlen))
|
||||
{
|
||||
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
|
||||
if (*r != '*' && !isxdigit((unsigned char)*r))
|
||||
return -1;
|
||||
|
||||
if (*r == 0)
|
||||
done = 1;
|
||||
|
||||
if (r != in )
|
||||
{
|
||||
if (*r == '-' && i == 0 && mac_type)
|
||||
{
|
||||
*r = 0;
|
||||
*mac_type = strtol(in, NULL, 16);
|
||||
mac_type = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
*r = 0;
|
||||
if (strcmp(in, "*") == 0)
|
||||
{
|
||||
mask = (mask << 1) | 1;
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int j, bytes = (1 + (r - in))/2;
|
||||
for (j = 0; j < bytes; j++)
|
||||
{
|
||||
char sav;
|
||||
if (j < bytes - 1)
|
||||
{
|
||||
sav = in[(j+1)*2];
|
||||
in[(j+1)*2] = 0;
|
||||
}
|
||||
/* checks above allow mix of hexdigit and *, which
|
||||
is illegal. */
|
||||
if (strchr(&in[j*2], '*'))
|
||||
return -1;
|
||||
out[i] = strtol(&in[j*2], NULL, 16);
|
||||
mask = mask << 1;
|
||||
if (++i == maxlen)
|
||||
break;
|
||||
if (j < bytes - 1)
|
||||
in[(j+1)*2] = sav;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
in = r+1;
|
||||
}
|
||||
|
||||
if (wildcard_mask)
|
||||
*wildcard_mask = mask;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static char *split_chr(char *s, char c)
|
||||
{
|
||||
char *comma, *p;
|
||||
|
||||
if (!s || !(comma = strchr(s, c)))
|
||||
return NULL;
|
||||
|
||||
p = comma;
|
||||
*comma = ' ';
|
||||
|
||||
for (; *comma == ' '; comma++);
|
||||
|
||||
for (; (p >= s) && *p == ' '; p--)
|
||||
*p = 0;
|
||||
|
||||
return comma;
|
||||
}
|
||||
|
||||
static char *split(char *s)
|
||||
{
|
||||
return split_chr(s, ',');
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int fd;
|
||||
struct ifreq ifr;
|
||||
struct in_addr iface_addr, server_addr, lease_addr;
|
||||
struct dhcp_packet_with_opts pkt;
|
||||
struct sockaddr_in saddr;
|
||||
unsigned char *p;
|
||||
ssize_t sz;
|
||||
unsigned char mac[DHCP_CHADDR_MAX], clid[256], req_options[256];
|
||||
char buff[500];
|
||||
int clid_len = 0, mac_len = 0, mac_type = 0, opts_len = 0;
|
||||
unsigned short port = DHCP_SERVER_PORT;
|
||||
unsigned int xid;
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
srand(tv.tv_usec);
|
||||
xid = rand();
|
||||
server_addr.s_addr = lease_addr.s_addr = iface_addr.s_addr = 0;
|
||||
|
||||
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||
{
|
||||
perror("leasequery: cannot create socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
int option = getopt(argc, argv, "i:s:l:m:c:p:r:");
|
||||
|
||||
if (option == -1)
|
||||
break;
|
||||
|
||||
switch (option)
|
||||
{
|
||||
default:
|
||||
fprintf(stderr,
|
||||
"-p <port> port on DHCP server.\n"
|
||||
"-i <interface> exit interface to DHCP server.\n"
|
||||
"-s <inet-addr> DHCP server address.\n"
|
||||
"-l <inet-addr> Query lease by IP address.\n"
|
||||
"-m <MAC-addr> Query lease by MAC address.\n"
|
||||
"-c <hex> Query lease by client-id.\n"
|
||||
"-r <name>|<int>[,...] List of options to return.\n");
|
||||
exit(1);
|
||||
|
||||
case 'p':
|
||||
port = atoi(optarg);
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
strncpy(ifr.ifr_name, optarg, IF_NAMESIZE);
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1)
|
||||
{
|
||||
perror("leasequery: cannot get interface address");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (inet_pton(AF_INET, optarg, &server_addr) <= 0)
|
||||
{
|
||||
fprintf(stderr, "leasequery: bad server address\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (inet_pton(AF_INET, optarg, &lease_addr) <= 0)
|
||||
{
|
||||
fprintf(stderr, "leasequery: bad lease address\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
mac_type = 1; /* default ethernet */
|
||||
mac_len = parse_hex(optarg, mac, DHCP_CHADDR_MAX, NULL, &mac_type);
|
||||
if (!mac_type)
|
||||
mac_type = 1; /* default ethernet */
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
clid_len = parse_hex(optarg, clid, 256, NULL, NULL);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
{
|
||||
char *comma;
|
||||
int opt;
|
||||
|
||||
while (optarg)
|
||||
{
|
||||
comma = split(optarg);
|
||||
|
||||
if ((opt = lookup_dhcp_opt(optarg)) != -1 || (opt = atoi(optarg)) != 0)
|
||||
req_options[opts_len++] = opt;
|
||||
|
||||
optarg = comma;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!server_addr.s_addr)
|
||||
{
|
||||
fprintf(stderr, "leasequery: no server address\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&pkt, 0, sizeof pkt);
|
||||
pkt.header.op = BOOTREQUEST;
|
||||
pkt.header.xid = xid;
|
||||
pkt.header.ciaddr = lease_addr;
|
||||
pkt.header.hlen = mac_len;
|
||||
pkt.header.htype = mac_type;
|
||||
memcpy(pkt.header.chaddr, mac, mac_len);
|
||||
*((unsigned int *)&pkt.options[0]) = htonl(DHCP_COOKIE);
|
||||
|
||||
/* Dnsmasq extension. */
|
||||
pkt.header.giaddr.s_addr = iface_addr.s_addr ? iface_addr.s_addr : INADDR_BROADCAST;
|
||||
|
||||
option_put(&pkt, OPTION_MESSAGE_TYPE, 1, DHCPLEASEQUERY);
|
||||
|
||||
if (clid_len != 0)
|
||||
{
|
||||
p = dhcp_skip_opts(&pkt);
|
||||
*p++ = OPTION_CLIENT_ID;
|
||||
*p++ = clid_len;
|
||||
memcpy(p, clid, clid_len);
|
||||
}
|
||||
|
||||
if (opts_len != 0)
|
||||
{
|
||||
p = dhcp_skip_opts(&pkt);
|
||||
*p++ = OPTION_REQUESTED_OPTIONS;
|
||||
*p++ = opts_len;
|
||||
memcpy(p, req_options, opts_len);
|
||||
}
|
||||
|
||||
if (iface_addr.s_addr)
|
||||
{
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_addr.s_addr = iface_addr.s_addr;
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
|
||||
{
|
||||
perror("leasequery: cannot bind DHCP server socket");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(port);
|
||||
saddr.sin_addr = server_addr;
|
||||
|
||||
while((sz = sendto(fd, &pkt, dhcp_skip_opts(&pkt) - ((unsigned char *)&pkt), 0, (struct sockaddr *)&saddr, sizeof(saddr))) == -1 &&
|
||||
errno == EINTR);
|
||||
|
||||
if (sz == -1)
|
||||
{
|
||||
perror("leasequery: sendto()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sz = recv(fd, &pkt, sizeof(pkt), 0);
|
||||
|
||||
if (sz == -1)
|
||||
{
|
||||
perror("leasequery: recv()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sz >= sizeof(pkt.header) &&
|
||||
pkt.header.op == BOOTREPLY &&
|
||||
(p = dhcp_find_opt(&pkt, OPTION_MESSAGE_TYPE)) &&
|
||||
pkt.header.xid == xid)
|
||||
{
|
||||
if (p[2] == DHCPLEASEUNASSIGNED)
|
||||
printf("UNASSIGNED\n");
|
||||
else if (p[2] == DHCPLEASEUNKNOWN)
|
||||
printf("UNKNOWN\n");
|
||||
else if (p[2] == DHCPLEASEACTIVE)
|
||||
{
|
||||
print_mac(buff, pkt.header.chaddr, pkt.header.hlen);
|
||||
printf("ACTIVE %s %s\n", inet_ntoa(pkt.header.ciaddr), buff);
|
||||
|
||||
p = (unsigned char *)(((unsigned int *)&pkt.options[0]) + 1);
|
||||
|
||||
while (*p != OPTION_END)
|
||||
{
|
||||
if (*p != OPTION_MESSAGE_TYPE)
|
||||
{
|
||||
char *optname = option_string(p[0], option_ptr(p, 0), option_len(p), buff, 500);
|
||||
|
||||
printf("size:%3d option:%3d %s %s\n", option_len(p), p[0], optname, buff);
|
||||
}
|
||||
p += p[1] + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1438,6 +1438,18 @@ DHCP options. This make extra space available in the DHCP packet for
|
||||
options but can, rarely, confuse old or broken clients. This flag
|
||||
forces "simple and safe" behaviour to avoid problems in such a case.
|
||||
.TP
|
||||
.B --leasequery[=<addr>[/<prefix>]]
|
||||
Enable RFC 4388 leasequery. The dnsmasq DHCP server will answer LEASEQUERY messages from DHCP relays
|
||||
when this option is given. If an address or subnet is given, only queries from a relay at that source are allowed. Repeat
|
||||
the --leasequery option to specify multiple allowed sources.
|
||||
To correctly answer lease queries it is necessary to store extra data in
|
||||
the lease database, and this is also enabled by the \fB--leasequery\fP option. The extra fields
|
||||
(agent-info and vendorclass) are stored in the leases file in a somewhat backwards compatible manner.
|
||||
Enabling and then disabling leasequery will not cause problems; the extra information will be aged
|
||||
out of the database. However, enabling leasequery in release 2.92 or later, storing leases
|
||||
which come via DHCP relays which add option-82 agent-info data, and then moving back to a pre-2.92 dnsmasq
|
||||
binary may cause problems reading the leases file. As of release 2.92, leasequery is only supported for DHCPv4.
|
||||
.TP
|
||||
.B --dhcp-relay=<local address>[,<server address>[#<server port>]][,<interface]
|
||||
Configure dnsmasq to do DHCP relay. The local address is an address
|
||||
allocated to an interface on the host running dnsmasq. All DHCP
|
||||
|
||||
@@ -102,6 +102,8 @@ static const struct {
|
||||
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
|
||||
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
|
||||
{ 66, "DSYNC" }, /* Endpoint discovery for delegation synchronization [draft-ietf-dnsop-generalized-notify-03] DSYNC/dsync-completed-template 2024-12-10 */
|
||||
{ 67, "HHIT" }, /* [draft-ietf-drip-registries-28] */
|
||||
{ 68, "BRID" }, /* [draft-ietf-drip-registries-28] */
|
||||
{ 99, "SPF" }, /* [RFC7208] */
|
||||
{ 100, "UINFO" }, /* [IANA-Reserved] */
|
||||
{ 101, "UID" }, /* [IANA-Reserved] */
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
#define CHUSER "nobody"
|
||||
#define CHGRP "dip"
|
||||
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
|
||||
#define TFTP_MAX_WINDOW 32 /* max window size to negotiate */
|
||||
#define TFTP_TRANSFER_TIME 120 /* Abandon TFTP transfers after this long. Two mins. */
|
||||
#define LOG_MAX 5 /* log-queue length */
|
||||
#define RANDFILE "/dev/urandom"
|
||||
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
|
||||
|
||||
+3
-1
@@ -701,7 +701,9 @@ static const struct opttab_t {
|
||||
{ "user-class", 77, 0 },
|
||||
{ "rapid-commit", 80, 0 },
|
||||
{ "FQDN", 81, OT_INTERNAL },
|
||||
{ "agent-id", 82, OT_INTERNAL },
|
||||
{ "agent-info", 82, OT_INTERNAL },
|
||||
{ "last-transaction", 91, 4 | OT_TIME },
|
||||
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||
{ "client-arch", 93, 2 | OT_DEC },
|
||||
{ "client-interface-id", 94, 0 },
|
||||
{ "client-machine-id", 97, 0 },
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
#define OPTION_RAPID_COMMIT 80
|
||||
#define OPTION_CLIENT_FQDN 81
|
||||
#define OPTION_AGENT_ID 82
|
||||
#define OPTION_LAST_TRANSACTION 91
|
||||
#define OPTION_ASSOCIATED_IP 92
|
||||
#define OPTION_ARCH 93
|
||||
#define OPTION_PXE_UUID 97
|
||||
#define OPTION_SUBNET_SELECT 118
|
||||
@@ -87,6 +89,11 @@
|
||||
#define DHCPNAK 6
|
||||
#define DHCPRELEASE 7
|
||||
#define DHCPINFORM 8
|
||||
#define DHCPFORCERENEW 9
|
||||
#define DHCPLEASEQUERY 10
|
||||
#define DHCPLEASEUNASSIGNED 11
|
||||
#define DHCPLEASEUNKNOWN 12
|
||||
#define DHCPLEASEACTIVE 13
|
||||
|
||||
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
|
||||
|
||||
|
||||
+28
-7
@@ -135,7 +135,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
struct dhcp_packet *mess;
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
int is_relay_reply = 0;
|
||||
int is_relay_reply = 0, is_relay_use_source = 0;
|
||||
struct iname *tmp;
|
||||
struct ifreq ifr;
|
||||
struct msghdr msg;
|
||||
@@ -230,6 +230,20 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
return;
|
||||
|
||||
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
|
||||
|
||||
/* Non-standard extension:
|
||||
If giaddr == 255.255.255.255 we reply to the source
|
||||
address in the request packet header. This makes
|
||||
stand-alone leasequery clients easier, as they
|
||||
can leave source address determination to the kernel.
|
||||
In this case, set a flag and clear giaddr here,
|
||||
to avoid massive relay confusion. */
|
||||
if (mess->giaddr.s_addr == INADDR_BROADCAST)
|
||||
{
|
||||
mess->giaddr.s_addr = 0;
|
||||
is_relay_use_source = 1;
|
||||
}
|
||||
|
||||
loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
@@ -337,8 +351,9 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
return;
|
||||
|
||||
lease_prune(NULL, now); /* lose any expired leases */
|
||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
|
||||
now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
|
||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, now, unicast_dest,
|
||||
loopback, &is_inform, pxe_fd, iface_addr, recvtime,
|
||||
is_relay_use_source ? dest.sin_addr : mess->giaddr);
|
||||
lease_update_file(now);
|
||||
lease_update_dns(0);
|
||||
|
||||
@@ -365,11 +380,17 @@ void dhcp_packet(time_t now, int pxe_fd)
|
||||
if (mess->ciaddr.s_addr != 0)
|
||||
dest.sin_addr = mess->ciaddr;
|
||||
}
|
||||
else if (mess->giaddr.s_addr && !is_relay_reply)
|
||||
if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
|
||||
{
|
||||
/* Send to BOOTP relay */
|
||||
dest.sin_port = htons(daemon->dhcp_server_port);
|
||||
dest.sin_addr = mess->giaddr;
|
||||
/* Send to BOOTP relay. */
|
||||
if (is_relay_use_source)
|
||||
/* restore as-received value */
|
||||
mess->giaddr.s_addr = INADDR_BROADCAST;
|
||||
else
|
||||
{
|
||||
dest.sin_addr = mess->giaddr;
|
||||
dest.sin_port = htons(daemon->dhcp_server_port);
|
||||
}
|
||||
}
|
||||
else if (mess->ciaddr.s_addr)
|
||||
{
|
||||
|
||||
+14
-3
@@ -30,7 +30,9 @@ static volatile pid_t pid = 0;
|
||||
static volatile int pipewrite;
|
||||
|
||||
static void set_dns_listeners(void);
|
||||
#ifdef HAVE_TFTP
|
||||
static void set_tftp_listeners(void);
|
||||
#endif
|
||||
static void check_dns_listeners(time_t now);
|
||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
|
||||
static void sig_handler(int sig);
|
||||
@@ -62,14 +64,16 @@ int main (int argc, char **argv)
|
||||
int need_cap_net_raw = 0;
|
||||
int need_cap_net_bind_service = 0;
|
||||
int have_cap_chown = 0;
|
||||
# ifdef HAVE_DHCP
|
||||
char *bound_device = NULL;
|
||||
int did_bind = 0;
|
||||
# endif
|
||||
struct server *serv;
|
||||
char *netlink_warn;
|
||||
#else
|
||||
int bind_fallback = 0;
|
||||
#endif
|
||||
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
|
||||
#if defined(HAVE_DHCP)
|
||||
struct dhcp_context *context;
|
||||
struct dhcp_relay *relay;
|
||||
#endif
|
||||
@@ -77,6 +81,10 @@ int main (int argc, char **argv)
|
||||
int tftp_prefix_missing = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
(void)netlink_warn;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
@@ -1939,11 +1947,14 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
pid_t p;
|
||||
union mysockaddr tcp_addr;
|
||||
socklen_t tcp_len = sizeof(union mysockaddr);
|
||||
unsigned char a = 0, *buff;
|
||||
unsigned char *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
unsigned char a = 0;
|
||||
#endif
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
@@ -2052,7 +2063,7 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
|
||||
+20
-9
@@ -48,6 +48,12 @@
|
||||
#define ATTRIBUTE_NORETURN
|
||||
#endif
|
||||
|
||||
#if __GNUC__ + 0 >= 8 // clang 20.1.0 does not yet support this
|
||||
#define ATTRIBUTE_NONSTRING __attribute__ ((nonstring))
|
||||
#else
|
||||
#define ATTRIBUTE_NONSTRING
|
||||
#endif
|
||||
|
||||
/* get these before config.h for IPv6 stuff... */
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
@@ -282,7 +288,8 @@ struct event_desc {
|
||||
#define OPT_NO_0x20 74
|
||||
#define OPT_DO_0x20 75
|
||||
#define OPT_AUTH_LOG 76
|
||||
#define OPT_LAST 77
|
||||
#define OPT_LEASEQUERY 77
|
||||
#define OPT_LAST 78
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -864,6 +871,8 @@ struct dhcp_lease {
|
||||
int last_interface;
|
||||
int new_interface; /* save possible originated interface */
|
||||
int new_prefixlen; /* and its prefix length */
|
||||
unsigned char *agent_id, *vendorclass;
|
||||
int agent_id_len, vendorclass_len;
|
||||
#ifdef HAVE_DHCP6
|
||||
struct in6_addr addr6;
|
||||
unsigned int iaid;
|
||||
@@ -1108,14 +1117,13 @@ struct tftp_file {
|
||||
|
||||
struct tftp_transfer {
|
||||
int sockfd;
|
||||
time_t timeout;
|
||||
int backoff;
|
||||
unsigned int block, blocksize, expansion;
|
||||
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;
|
||||
char opt_blocksize, opt_transize, netascii, carrylf;
|
||||
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
|
||||
struct tftp_file *file;
|
||||
struct tftp_transfer *next;
|
||||
};
|
||||
@@ -1183,7 +1191,7 @@ extern struct daemon {
|
||||
char *runfile;
|
||||
char *lease_change_command;
|
||||
struct iname *if_names, *if_addrs, *if_except, *dhcp_except, *auth_peers, *tftp_interfaces;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr;
|
||||
struct bogus_addr *bogus_addr, *ignore_addr, *leasequery_addr;
|
||||
struct server *servers, *servers_tail, *local_domains, **serverarray;
|
||||
struct rebind_domain *no_rebind;
|
||||
int server_has_wildcard;
|
||||
@@ -1410,7 +1418,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
|
||||
unsigned short *typep, unsigned short *classp);
|
||||
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
|
||||
time_t now, struct ipsets *ipsets, struct ipsets *nftsets,
|
||||
int check_rebind, int no_cache_dnssec, int secure);
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
void report_addresses(struct dns_header *header, size_t len, u32 mark);
|
||||
@@ -1619,6 +1627,7 @@ void lease6_reset(void);
|
||||
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type,
|
||||
unsigned char *clid, int clid_len, unsigned int iaid);
|
||||
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
|
||||
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr);
|
||||
u64 lease_find_max_addr6(struct dhcp_context *context);
|
||||
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
|
||||
void lease_update_slaac(time_t now);
|
||||
@@ -1631,6 +1640,8 @@ void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
|
||||
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
|
||||
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
|
||||
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
|
||||
unsigned char *clid, int clid_len);
|
||||
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
||||
@@ -1651,7 +1662,8 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
|
||||
#ifdef HAVE_DHCP
|
||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime);
|
||||
int *is_inform, int pxe, struct in_addr fallback,
|
||||
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);
|
||||
#endif
|
||||
@@ -1755,7 +1767,6 @@ void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *p
|
||||
|
||||
/* tftp.c */
|
||||
#ifdef HAVE_TFTP
|
||||
void tftp_request(struct listener *listen, time_t now);
|
||||
void check_tftp_listeners(time_t now);
|
||||
int do_tftp_script_run(void);
|
||||
#endif
|
||||
|
||||
+1
-3
@@ -343,10 +343,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 ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
else
|
||||
nlow = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -494,7 +494,7 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
|
||||
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
|
||||
|
||||
struct umbrella_opt {
|
||||
u8 magic[4];
|
||||
u8 magic[4] ATTRIBUTE_NONSTRING;
|
||||
u8 version;
|
||||
u8 flags;
|
||||
/* We have 4 possible fields since we'll never send both IPv4 and
|
||||
|
||||
+3
-1
@@ -670,6 +670,7 @@ int fast_retry(time_t now)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
|
||||
/* Similar algorithm to search_servers. */
|
||||
struct ipsets *ipset_pos, *ret = NULL;
|
||||
@@ -690,6 +691,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
|
||||
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
|
||||
@@ -819,7 +821,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
}
|
||||
else
|
||||
{
|
||||
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
|
||||
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, check_rebind, no_cache, cache_secure);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
|
||||
+106
-85
@@ -133,29 +133,44 @@ void inotify_dnsmasq_init()
|
||||
}
|
||||
}
|
||||
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
|
||||
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *file)
|
||||
{
|
||||
/* Check if this file is already known in dd->files */
|
||||
struct hostsfile *ah = NULL;
|
||||
for(ah = dd->files; ah; ah = ah->next)
|
||||
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
|
||||
struct hostsfile *ah;
|
||||
size_t dirlen = strlen(dd->dname);
|
||||
|
||||
/* ah->fname always starts with the string in dd->dname */
|
||||
for (ah = dd->files; ah; ah = ah->next)
|
||||
if (ah->fname[dirlen] == '/' &&
|
||||
strcmp(&ah->fname[dirlen+1], file) == 0)
|
||||
return ah;
|
||||
|
||||
|
||||
/* Not known, create new hostsfile record for this dyndir */
|
||||
struct hostsfile *newah = NULL;
|
||||
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
|
||||
return NULL;
|
||||
if ((ah = whine_malloc(sizeof(struct hostsfile))))
|
||||
{
|
||||
char *path;
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
newah->next = dd->files;
|
||||
dd->files = newah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
newah->flags = dd->flags;
|
||||
newah->index = daemon->host_index++;
|
||||
newah->fname = path;
|
||||
|
||||
return newah;
|
||||
if (!(path = whine_malloc(dirlen + strlen(file) + 2)))
|
||||
{
|
||||
free(ah);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, file);
|
||||
|
||||
/* Add this file to the tip of the linked list */
|
||||
ah->next = dd->files;
|
||||
dd->files = ah;
|
||||
|
||||
/* Copy flags, set index and the full file path */
|
||||
ah->flags = dd->flags;
|
||||
ah->index = daemon->host_index++;
|
||||
ah->fname = path;
|
||||
}
|
||||
|
||||
return ah;
|
||||
}
|
||||
|
||||
|
||||
@@ -204,10 +219,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
|
||||
while ((ent = readdir(dir_stream)))
|
||||
{
|
||||
size_t lendir = strlen(dd->dname);
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
char *path;
|
||||
|
||||
|
||||
/* ignore emacs backups and dotfiles */
|
||||
if (lenfile == 0 ||
|
||||
ent->d_name[lenfile - 1] == '~' ||
|
||||
@@ -215,33 +228,36 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
|
||||
ent->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
if ((path = whine_malloc(lendir + lenfile + 2)))
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
struct hostsfile *ah;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
if (!(ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
free(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
{
|
||||
if (dd->flags & AH_HOSTS)
|
||||
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
|
||||
if ((ah = dyndir_addhosts(dd, ent->d_name)) &&
|
||||
stat(ah->fname, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
total_size = read_hostsfile(ah->fname, ah->index, total_size, rhash, revhashsz);
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
|
||||
{
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(strlen(dd->dname) + lenfile + 2)))
|
||||
{
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, ent->d_name);
|
||||
|
||||
/* ignore non-regular files */
|
||||
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
|
||||
option_read_dynfile(path, dd->flags);
|
||||
#endif
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
closedir(dir_stream);
|
||||
}
|
||||
}
|
||||
@@ -251,6 +267,8 @@ int inotify_check(time_t now)
|
||||
int hit = 0;
|
||||
struct dyndir *dd;
|
||||
|
||||
(void)now;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int rc;
|
||||
@@ -283,50 +301,27 @@ int inotify_check(time_t now)
|
||||
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
|
||||
if (dd->wd == in->wd)
|
||||
{
|
||||
size_t lendir = strlen(dd->dname);
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(lendir + in->len + 2)))
|
||||
if (dd->flags & AH_HOSTS)
|
||||
{
|
||||
struct hostsfile *ah = NULL;
|
||||
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if (dd->flags & AH_HOSTS)
|
||||
struct hostsfile *ah;
|
||||
if ((ah = dyndir_addhosts(dd, in->name)))
|
||||
{
|
||||
if ((ah = dyndir_addhosts(dd, path)))
|
||||
{
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
|
||||
const unsigned int removed = cache_remove_uid(ah->index);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(path, ah->index, 0, NULL, 0);
|
||||
/* Is this is a deletion event? */
|
||||
if (in->mask & IN_DELETE)
|
||||
my_syslog(LOG_INFO, _("inotify: %s removed"), ah->fname);
|
||||
else
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), ah->fname);
|
||||
|
||||
if (removed > 0)
|
||||
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, ah->fname);
|
||||
|
||||
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
|
||||
if (!(in->mask & IN_DELETE))
|
||||
read_hostsfile(ah->fname, ah->index, 0, NULL, 0);
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (dd->flags & AH_DHCP_HST)
|
||||
{
|
||||
if (option_read_dynfile(path, AH_DHCP_HST))
|
||||
if (daemon->dhcp || daemon->doing_dhcp6)
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
@@ -334,17 +329,43 @@ int inotify_check(time_t now)
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
}
|
||||
else if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
#endif
|
||||
|
||||
if (!ah)
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (!(in->mask & IN_DELETE))
|
||||
{
|
||||
char *path;
|
||||
|
||||
if ((path = whine_malloc(strlen(dd->dname) + in->len + 2)))
|
||||
{
|
||||
strcpy(path, dd->dname);
|
||||
strcat(path, "/");
|
||||
strcat(path, in->name);
|
||||
|
||||
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
|
||||
|
||||
if ((dd->flags & AH_DHCP_HST) && option_read_dynfile(path, AH_DHCP_HST))
|
||||
{
|
||||
/* Propagate the consequences of loading a new dhcp-host */
|
||||
dhcp_update_configs(daemon->dhcp_conf);
|
||||
lease_update_from_configs();
|
||||
lease_update_file(now);
|
||||
lease_update_dns(1);
|
||||
}
|
||||
|
||||
if (dd->flags & AH_DHCP_OPT)
|
||||
option_read_dynfile(path, AH_DHCP_OPT);
|
||||
|
||||
free(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
|
||||
+125
-7
@@ -25,7 +25,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
unsigned long ei;
|
||||
union all_addr addr;
|
||||
struct dhcp_lease *lease;
|
||||
int clid_len, hw_len, hw_type;
|
||||
int opt_len, clid_len, hw_len, hw_type;
|
||||
int items;
|
||||
|
||||
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
|
||||
@@ -55,6 +55,34 @@ static int read_leases(time_t now, FILE *leasestream)
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Weird backwards compatible way of adding extra fields to leases */
|
||||
if ((strcmp(daemon->dhcp_buff3, "vendorclass") == 0 || strcmp(daemon->dhcp_buff3, "agent-info") == 0))
|
||||
{
|
||||
if (fscanf(leasestream, " %764s", daemon->packet) == 1)
|
||||
{
|
||||
if (inet_pton(AF_INET, daemon->dhcp_buff2, &addr.addr4))
|
||||
lease = lease_find_by_addr(addr.addr4);
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (inet_pton(AF_INET6, daemon->dhcp_buff2, &addr.addr6))
|
||||
lease = lease6_find_by_plain_addr(&addr.addr6);
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
if (lease)
|
||||
{
|
||||
opt_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||
|
||||
if (strcmp(daemon->dhcp_buff3, "vendorclass") == 0)
|
||||
lease_set_vendorclass(lease, (unsigned char *)daemon->packet, opt_len);
|
||||
else if (strcmp(daemon->dhcp_buff3, "agent-info") == 0)
|
||||
lease_set_agent_id(lease, (unsigned char *)daemon->packet, opt_len);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fscanf(leasestream, " %64s %255s %764s",
|
||||
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
|
||||
@@ -251,8 +279,8 @@ void lease_update_file(time_t now)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
time_t next_event;
|
||||
int i, err = 0;
|
||||
|
||||
int i, err = 0, extras;
|
||||
|
||||
if (file_dirty != 0 && daemon->lease_stream)
|
||||
{
|
||||
errno = 0;
|
||||
@@ -260,9 +288,11 @@ void lease_update_file(time_t now)
|
||||
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
|
||||
err = errno;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
for (extras = 0, lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
|
||||
if (lease->agent_id || lease->vendorclass)
|
||||
extras = 1;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
continue;
|
||||
@@ -335,7 +365,37 @@ void lease_update_file(time_t now)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (extras)
|
||||
{
|
||||
/* Dump this at the end for least confusion with older parsing code. */
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
#endif
|
||||
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if (lease->agent_id)
|
||||
{
|
||||
ourprintf(&err, "agent-info %s ", daemon->addrbuff);
|
||||
for (i = 0; i < lease->agent_id_len - 1; i++)
|
||||
ourprintf(&err, "%.2x:", lease->agent_id[i]);
|
||||
ourprintf(&err, "%.2x\n", lease->agent_id[i]);
|
||||
}
|
||||
|
||||
if (lease->vendorclass)
|
||||
{
|
||||
ourprintf(&err, "vendorclass %s ", daemon->addrbuff);
|
||||
for (i = 0; i < lease->vendorclass_len - 1; i++)
|
||||
ourprintf(&err, "%.2x:", lease->vendorclass[i]);
|
||||
ourprintf(&err, "%.2x\n", lease->vendorclass[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fflush(daemon->lease_stream) != 0 ||
|
||||
fsync(fileno(daemon->lease_stream)) < 0)
|
||||
err = errno;
|
||||
@@ -713,6 +773,22 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
for (lease = leases; lease; lease = lease->next)
|
||||
{
|
||||
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
|
||||
continue;
|
||||
|
||||
if (IN6_ARE_ADDR_EQUAL(&lease->addr6, addr))
|
||||
return lease;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find largest assigned address in context */
|
||||
u64 lease_find_max_addr6(struct dhcp_context *context)
|
||||
{
|
||||
@@ -1084,6 +1160,46 @@ void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
|
||||
#endif
|
||||
}
|
||||
|
||||
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||
{
|
||||
|
||||
if (!lease->agent_id && !new)
|
||||
return;
|
||||
|
||||
if (lease->agent_id && new && lease->agent_id_len == len && memcmp(lease->agent_id, new, len) == 0)
|
||||
return;
|
||||
|
||||
file_dirty = 1;
|
||||
free(lease->agent_id);
|
||||
lease->agent_id = NULL;
|
||||
|
||||
if (new && (lease->agent_id = whine_malloc(len)))
|
||||
{
|
||||
memcpy(lease->agent_id, new, len);
|
||||
lease->agent_id_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||
{
|
||||
if (!lease->vendorclass && !new)
|
||||
return;
|
||||
|
||||
if (lease->vendorclass && new && lease->vendorclass_len == len && memcmp(lease->vendorclass, new, len) == 0)
|
||||
return;
|
||||
|
||||
file_dirty = 1;
|
||||
free(lease->vendorclass);
|
||||
lease->vendorclass = NULL;
|
||||
|
||||
if (new && (lease->vendorclass = whine_malloc(len)))
|
||||
{
|
||||
memcpy(lease->vendorclass, new, len);
|
||||
lease->vendorclass_len = len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void rerun_scripts(void)
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
@@ -1143,9 +1259,11 @@ int do_script_run(time_t now)
|
||||
#endif
|
||||
old_leases = lease->next;
|
||||
|
||||
free(lease->old_hostname);
|
||||
free(lease->hostname);
|
||||
free(lease->clid);
|
||||
free(lease->extradata);
|
||||
free(lease->agent_id);
|
||||
free(lease->vendorclass);
|
||||
free(lease);
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -43,6 +43,10 @@ const char * metric_names[] = {
|
||||
"leases_allocated_6",
|
||||
"leases_pruned_6",
|
||||
"tcp_connections",
|
||||
"dhcp_leasequery",
|
||||
"dhcp_lease_unassigned",
|
||||
"dhcp_lease_actve",
|
||||
"dhcp_lease_unknown"
|
||||
};
|
||||
|
||||
const char* get_metric_name(int i) {
|
||||
|
||||
@@ -42,6 +42,10 @@ enum {
|
||||
METRIC_LEASES_ALLOCATED_6,
|
||||
METRIC_LEASES_PRUNED_6,
|
||||
METRIC_TCP_CONNECTIONS,
|
||||
METRIC_DHCPLEASEQUERY,
|
||||
METRIC_DHCPLEASEUNASSIGNED,
|
||||
METRIC_DHCPLEASEACTIVE,
|
||||
METRIC_DHCPLEASEUNKNOWN,
|
||||
|
||||
__METRIC_MAX,
|
||||
};
|
||||
|
||||
+5
-1
@@ -1568,6 +1568,8 @@ void check_servers(int no_loop_check)
|
||||
struct serverfd *sfd, *tmp, **up;
|
||||
int port = 0, count;
|
||||
int locals = 0;
|
||||
|
||||
(void)no_loop_check;
|
||||
|
||||
#ifdef HAVE_LOOP
|
||||
if (!no_loop_check)
|
||||
@@ -1775,8 +1777,10 @@ int reload_servers(char *fname)
|
||||
/* Called when addresses are added or deleted from an interface */
|
||||
void newaddress(time_t now)
|
||||
{
|
||||
#ifdef HAVE_DHCP
|
||||
struct dhcp_relay *relay;
|
||||
|
||||
#endif
|
||||
|
||||
(void)now;
|
||||
|
||||
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
|
||||
|
||||
+20
-1
@@ -195,6 +195,7 @@ struct myoption {
|
||||
#define LOPT_PXE_OPT 386
|
||||
#define LOPT_NO_ENCODE 387
|
||||
#define LOPT_DO_ENCODE 388
|
||||
#define LOPT_LEASEQUERY 389
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -394,6 +395,7 @@ static const struct myoption opts[] =
|
||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||
{ "leasequery", 2, 0, LOPT_LEASEQUERY },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -499,6 +501,7 @@ static struct {
|
||||
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
||||
{ LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
|
||||
{ LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
|
||||
{ LOPT_LEASEQUERY, ARG_DUP, "[<addr>[/prefix>]]", gettext_noop("Enable RFC 4388 leasequery functions for DHCPv4"), NULL },
|
||||
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
|
||||
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
|
||||
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
|
||||
@@ -2912,7 +2915,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
arg = comma;
|
||||
} while (arg);
|
||||
break;
|
||||
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
||||
case LOPT_LEASEQUERY:
|
||||
set_option_bool(OPT_LEASEQUERY);
|
||||
if (!arg)
|
||||
break;
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
case 'B': /* --bogus-nxdomain */
|
||||
case LOPT_IGNORE_ADDR: /* --ignore-address */
|
||||
{
|
||||
@@ -2948,6 +2960,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
baddr->next = daemon->bogus_addr;
|
||||
daemon->bogus_addr = baddr;
|
||||
}
|
||||
#ifdef HAVE_DHCP
|
||||
else if (option == LOPT_LEASEQUERY)
|
||||
{
|
||||
baddr->next = daemon->leasequery_addr;
|
||||
daemon->leasequery_addr = baddr;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
baddr->next = daemon->ignore_addr;
|
||||
|
||||
+77
-139
@@ -687,12 +687,12 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int flag)
|
||||
Return 2 if the packet is malformed.
|
||||
*/
|
||||
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
|
||||
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
|
||||
struct ipsets *ipsets, struct ipsets *nftsets, int check_rebind,
|
||||
int no_cache_dnssec, int secure)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int j, qtype, qclass, aqtype, aqclass, ardlen, res;
|
||||
unsigned long ttl = 0;
|
||||
unsigned long ttl;
|
||||
union all_addr addr;
|
||||
#ifdef HAVE_IPSET
|
||||
char **ipsets_cur;
|
||||
@@ -704,13 +704,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
#else
|
||||
(void)nftsets; /* unused */
|
||||
#endif
|
||||
int found = 0, cname_count = CNAME_CHAIN;
|
||||
struct crec *cpp = NULL;
|
||||
int name_encoding, found = 0, ptr = 0;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int cname_short = 0;
|
||||
#endif
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
@@ -724,125 +719,73 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (qclass != C_IN)
|
||||
return 0;
|
||||
|
||||
/* PTRs: we chase CNAMEs here, since we have no way to
|
||||
represent them in the cache. */
|
||||
if (qtype == T_PTR)
|
||||
|
||||
/* If the PTR record encodes an address, store using a name/address record with F_REVERSE set.
|
||||
Otherwise, it gets stored as an arbitrary RR below. If the query is answerable with
|
||||
a CNAME, also take the arbitrary-RR route, since the cache can't represent a CNAME
|
||||
whose target is stored in a F_REVERSE record. */
|
||||
if (qtype == T_PTR && !(flags & F_NXDOMAIN) && (name_encoding = in_arpa_name_2_addr(name, &addr)))
|
||||
{
|
||||
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
|
||||
ptr = 1;
|
||||
|
||||
if (!(flags & F_NXDOMAIN))
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
p = p1;
|
||||
GETLONG(ttl, p1);
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
if (aqclass == C_IN && res == 1 && aqtype == T_PTR)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
found = 1;
|
||||
|
||||
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
|
||||
ttl = daemon->max_ttl;
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
/* TTL of record is minimum of CNAMES and PTR */
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
|
||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
{
|
||||
/* validated RR anywhere in CNAME chain, don't cache. */
|
||||
if (cname_short || aqtype == T_CNAME)
|
||||
insert = 0;
|
||||
|
||||
secflag = F_DNSSECOK;
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < cttl)
|
||||
cttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES, we can't cache. */
|
||||
#ifdef HAVE_DNSSEC
|
||||
cname_short = 1;
|
||||
#endif
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
|
||||
if (!name_encoding)
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
|
||||
else
|
||||
{
|
||||
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
if (insert)
|
||||
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
|
||||
|
||||
if (name_encoding)
|
||||
flags |= F_REVERSE | name_encoding;
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
|
||||
if (name_encoding && !option_bool(OPT_NO_NEG))
|
||||
{
|
||||
/* For reverse records, we use the name field to store the SOA name. */
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
|
||||
|
||||
if (have_soa || daemon->neg_ttl)
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
{
|
||||
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
|
||||
if (!have_soa)
|
||||
{
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
ttl = daemon->neg_ttl;
|
||||
}
|
||||
|
||||
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
|
||||
secflag = F_DNSSECOK;
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < ttl)
|
||||
ttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
PUTLONG(ttl, p);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
cache_insert(name, &addr, C_IN, now, ttl, name_encoding | secflag | F_REVERSE);
|
||||
|
||||
/* restore query into name */
|
||||
p1 = namep;
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 2; /* bad packet */
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if (!ptr || !found)
|
||||
{
|
||||
/* everything other than PTR */
|
||||
struct crec *newc;
|
||||
int addrlen = 0, insert = 1;
|
||||
|
||||
struct crec *newc, *cpp = NULL;
|
||||
int cname_count = CNAME_CHAIN, addrlen = 0, insert = 1;
|
||||
|
||||
if (qtype == T_A)
|
||||
{
|
||||
addrlen = INADDRSZ;
|
||||
@@ -854,12 +797,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
flags |= F_IPV6;
|
||||
}
|
||||
else if (qtype != T_CNAME &&
|
||||
(qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
|
||||
(qtype == T_SRV || qtype == T_PTR || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
|
||||
flags |= F_RR;
|
||||
else
|
||||
insert = 0; /* NOTE: do not cache data from CNAME queries. */
|
||||
|
||||
cname_loop1:
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
@@ -872,12 +815,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
p = p1;
|
||||
GETLONG(ttl, p1);
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
@@ -890,6 +829,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
p1 = endrr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
|
||||
ttl = daemon->max_ttl;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
|
||||
@@ -897,10 +839,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
secflag = F_DNSSECOK;
|
||||
|
||||
/* limit TTl based on sig. */
|
||||
if (daemon->rr_status[j] < attl)
|
||||
attl = daemon->rr_status[j];
|
||||
if (daemon->rr_status[j] < ttl)
|
||||
ttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
PUTLONG(ttl, p);
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
@@ -911,7 +855,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (insert)
|
||||
{
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, ttl, F_CNAME | F_FORWARD | secflag)))
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
@@ -924,16 +868,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
}
|
||||
|
||||
cpp = newc;
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
}
|
||||
|
||||
/* Set the query to the CNAME target and go again unless the query was just for a CNAME. */
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
|
||||
return 2;
|
||||
|
||||
if (qtype != T_CNAME)
|
||||
goto cname_loop1;
|
||||
goto cname_loop;
|
||||
|
||||
found = 1;
|
||||
}
|
||||
@@ -1089,7 +1032,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
if (insert)
|
||||
{
|
||||
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
@@ -1668,7 +1611,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
int rd_bit = (header->hb3 & HB3_RD);
|
||||
int count = 255; /* catch loops */
|
||||
|
||||
/* Suppress cached answers of no_cache set. */
|
||||
/* Suppress cached answers if no_cache set. */
|
||||
if (no_cache)
|
||||
rd_bit = 0;
|
||||
|
||||
@@ -1678,14 +1621,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (filtered)
|
||||
*filtered = 0;
|
||||
|
||||
/* never answer queries with RD unset, to avoid cache snooping. */
|
||||
if ( ntohs(header->qdcount) != 1 ||
|
||||
ntohs(header->ancount) != 0 ||
|
||||
if (ntohs(header->qdcount) != 1 ||
|
||||
ntohs(header->ancount) != 0 ||
|
||||
ntohs(header->nscount) != 0 ||
|
||||
ntohs(header->qdcount) == 0 ||
|
||||
OPCODE(header) != QUERY )
|
||||
return 0;
|
||||
|
||||
|
||||
/* Don't return AD set if checking disabled. */
|
||||
if (header->hb4 & HB4_CD)
|
||||
sec_data = 0;
|
||||
@@ -2348,14 +2289,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
|
||||
For FORWARD NEG records, the addr.rrdata.datalen field of the othewise
|
||||
empty addr is used to held an offset in to the name which yields the SOA
|
||||
name. For REVERSE NEG records, the otherwise empty name field holds the
|
||||
SOA name. If soa_name has zero length, then no SOA is known. soa_lookup
|
||||
MUST be a neg record here.
|
||||
|
||||
name.
|
||||
If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */
|
||||
if (soa_lookup && !(soa_lookup->flags & F_NO_RR))
|
||||
{
|
||||
char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen;
|
||||
char *soa_name = name + soa_lookup->addr.rrdata.datalen;
|
||||
|
||||
crecp = NULL;
|
||||
while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR)))
|
||||
|
||||
+321
-120
@@ -56,7 +56,8 @@ static void do_options(struct dhcp_context *context,
|
||||
time_t now,
|
||||
unsigned int lease_time,
|
||||
unsigned short fuzz,
|
||||
const char *pxevendor);
|
||||
const char *pxevendor,
|
||||
int leasequery);
|
||||
|
||||
|
||||
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
|
||||
@@ -73,7 +74,7 @@ static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned
|
||||
|
||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
|
||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime, struct in_addr leasequery_source)
|
||||
{
|
||||
unsigned char *opt, *clid = NULL;
|
||||
struct dhcp_lease *ltmp, *lease = NULL;
|
||||
@@ -108,7 +109,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
#endif
|
||||
|
||||
subnet_addr.s_addr = override.s_addr = 0;
|
||||
|
||||
|
||||
/* set tag with name == interface */
|
||||
iface_id.net = iface_name;
|
||||
iface_id.next = NULL;
|
||||
@@ -185,7 +186,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
if (mess_type != DHCPLEASEQUERY && (opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||
{
|
||||
/* Any agent-id needs to be copied back out, verbatim, as the last option
|
||||
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
|
||||
@@ -211,7 +212,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SERVER_OR, INADDRSZ)))
|
||||
override = option_addr(sopt);
|
||||
|
||||
/* if a circuit-id or remote-is option is provided, exact-match to options. */
|
||||
/* if a circuit-id or remote-is option is provided, exact-match to options. */
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int search;
|
||||
@@ -224,7 +225,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
search = SUBOPT_SUBSCR_ID;
|
||||
else
|
||||
continue;
|
||||
|
||||
|
||||
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), search, 1)) &&
|
||||
vendor->len == option_len(sopt) &&
|
||||
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
|
||||
@@ -234,7 +235,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Check for RFC3011 subnet selector - only if RFC3527 one not present */
|
||||
if (subnet_addr.s_addr == 0 && (opt = option_find(mess, sz, OPTION_SUBNET_SELECT, INADDRSZ)))
|
||||
subnet_addr = option_addr(opt);
|
||||
@@ -373,44 +374,47 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
context = context_new;
|
||||
}
|
||||
|
||||
if (!context)
|
||||
if (mess_type != DHCPLEASEQUERY)
|
||||
{
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
if (!context)
|
||||
{
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
const char *via;
|
||||
if (subnet_addr.s_addr)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
via = _("with subnet selector");
|
||||
inet_ntop(AF_INET, &subnet_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
via = _("via");
|
||||
if (mess->giaddr.s_addr)
|
||||
inet_ntop(AF_INET, &mess->giaddr, daemon->addrbuff, ADDRSTRLEN);
|
||||
else
|
||||
safe_strncpy(daemon->addrbuff, iface_name, ADDRSTRLEN);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("no address range available for DHCP request %s %s"),
|
||||
via, daemon->addrbuff);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
struct dhcp_context *context_tmp;
|
||||
for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->start, daemon->namebuff, MAXDNAME);
|
||||
if (context_tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->netmask, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP subnet: %s/%s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
else
|
||||
{
|
||||
inet_ntop(AF_INET, &context_tmp->end, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),
|
||||
ntohl(mess->xid), daemon->namebuff, daemon->addrbuff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -672,7 +676,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
clear_packet(mess, end);
|
||||
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
|
||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,8 +822,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config)
|
||||
|
||||
if (mess_type != DHCPLEASEQUERY && config)
|
||||
{
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
@@ -860,7 +864,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
clid = NULL;
|
||||
|
||||
/* Check if client is PXE client. */
|
||||
if (daemon->enable_pxe &&
|
||||
if (mess_type != DHCPLEASEQUERY &&
|
||||
daemon->enable_pxe &&
|
||||
is_pxe_client(mess, sz, &pxevendor))
|
||||
{
|
||||
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
|
||||
@@ -1035,7 +1040,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
|
||||
/* if we're just a proxy server, go no further */
|
||||
if ((context->flags & CONTEXT_PROXY) || pxe)
|
||||
if (mess_type != DHCPLEASEQUERY &&
|
||||
((context->flags & CONTEXT_PROXY) || pxe))
|
||||
return 0;
|
||||
|
||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
|
||||
@@ -1047,6 +1053,176 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
switch (mess_type)
|
||||
{
|
||||
case DHCPLEASEQUERY:
|
||||
mess_type = DHCPLEASEUNKNOWN;
|
||||
|
||||
if (!option_bool(OPT_LEASEQUERY))
|
||||
return 0;
|
||||
|
||||
if (leasequery_source.s_addr == 0)
|
||||
return 0;
|
||||
|
||||
inet_ntop(AF_INET, &leasequery_source, daemon->workspacename, ADDRSTRLEN);
|
||||
|
||||
if (daemon->leasequery_addr)
|
||||
{
|
||||
struct bogus_addr *baddrp;
|
||||
|
||||
for (baddrp = daemon->leasequery_addr; baddrp; baddrp = baddrp->next)
|
||||
if (!baddrp->is6 && is_same_net_prefix(leasequery_source, baddrp->addr.addr4, baddrp->prefix))
|
||||
break;
|
||||
|
||||
if (!baddrp)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("leasequery from %s not permitted"), daemon->workspacename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DHCPLEASEQUERY]++;
|
||||
log_packet("DHCPLEASEQUERY", mess->ciaddr.s_addr ? &mess->ciaddr : NULL, emac_len != 0 ? emac : NULL, emac_len,
|
||||
iface_name, "from ", daemon->workspacename, mess->xid);
|
||||
|
||||
/* Put all the contexts on the ->current list for the next stages. */
|
||||
for (context = daemon->dhcp; context; context = context->next)
|
||||
context->current = context->next;
|
||||
|
||||
/* Have maybe already found the lease by MAC or clid. */
|
||||
if (mess->ciaddr.s_addr != 0 &&
|
||||
!(lease = lease_find_by_addr(mess->ciaddr)) &&
|
||||
address_available(daemon->dhcp, mess->ciaddr, tagif_netid))
|
||||
{
|
||||
mess_type = DHCPLEASEUNASSIGNED;
|
||||
daemon->metrics[METRIC_DHCPLEASEUNASSIGNED]++;
|
||||
}
|
||||
|
||||
if (lease)
|
||||
{
|
||||
/* RFC4388 para 6.4.2 */
|
||||
if (lease->agent_id)
|
||||
{
|
||||
unsigned char *sopt ;
|
||||
|
||||
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||
{
|
||||
int search;
|
||||
|
||||
if (vendor->match_type == MATCH_CIRCUIT)
|
||||
search = SUBOPT_CIRCUIT_ID;
|
||||
else if (vendor->match_type == MATCH_REMOTE)
|
||||
search = SUBOPT_REMOTE_ID;
|
||||
else if (vendor->match_type == MATCH_SUBSCRIBER)
|
||||
search = SUBOPT_SUBSCR_ID;
|
||||
else
|
||||
continue;
|
||||
|
||||
if ((sopt = option_find1(lease->agent_id, lease->agent_id + lease->agent_id_len, search, 1)) &&
|
||||
vendor->len == option_len(sopt) &&
|
||||
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
|
||||
{
|
||||
vendor->netid.next = netid;
|
||||
netid = &vendor->netid;
|
||||
}
|
||||
}
|
||||
|
||||
tagif_netid = run_tag_if(netid);
|
||||
}
|
||||
|
||||
/* Now find the context for this lease and config for this host. */
|
||||
if ((context = narrow_context(daemon->dhcp, lease->addr, tagif_netid)))
|
||||
{
|
||||
if ((config = find_config(daemon->dhcp_conf, context, lease->clid, lease->clid_len,
|
||||
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, lease->hostname, tagif_netid)))
|
||||
{
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
for (list = config->netid; list; list = list->next)
|
||||
{
|
||||
list->list->next = netid;
|
||||
netid = list->list;
|
||||
}
|
||||
|
||||
tagif_netid = run_tag_if(netid);
|
||||
}
|
||||
|
||||
if (context->netid.net)
|
||||
{
|
||||
context->netid.next = netid;
|
||||
tagif_netid = run_tag_if(&context->netid);
|
||||
}
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
emac = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len, lease->hwaddr, lease->clid_len, lease->clid, &emac_len);
|
||||
mess_type = DHCPLEASEACTIVE;
|
||||
daemon->metrics[METRIC_DHCPLEASEACTIVE]++;
|
||||
}
|
||||
}
|
||||
|
||||
log_packet(mess_type == DHCPLEASEACTIVE ? "DHCPLEASEACTIVE" : (mess_type == DHCPLEASEUNASSIGNED ? "DHCPLEASEUNASSIGNED" : "DHCPLEASEUNKNOWN"),
|
||||
mess_type == DHCPLEASEACTIVE ? &lease->addr : (mess->ciaddr.s_addr != 0 ? &mess->ciaddr : NULL),
|
||||
emac_len != 0 ? emac : NULL, emac_len,
|
||||
iface_name, mess_type == DHCPLEASEACTIVE ? lease->hostname : NULL, NULL, mess->xid);
|
||||
|
||||
clear_packet(mess, end);
|
||||
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, mess_type);
|
||||
|
||||
if (mess_type == DHCPLEASEUNKNOWN)
|
||||
{
|
||||
daemon->metrics[METRIC_DHCPLEASEUNKNOWN]++;
|
||||
mess->ciaddr.s_addr = 0;
|
||||
}
|
||||
|
||||
if (mess_type == DHCPLEASEACTIVE)
|
||||
{
|
||||
unsigned char *p;
|
||||
|
||||
mess->ciaddr = lease->addr;
|
||||
mess->hlen = lease->hwaddr_len;
|
||||
mess->htype = lease->hwaddr_type;
|
||||
memcpy(mess->chaddr, lease->hwaddr, lease->hwaddr_len);
|
||||
|
||||
if (lease->clid && in_list(req_options, OPTION_CLIENT_ID) &&
|
||||
(p = free_space(mess, end, OPTION_CLIENT_ID, lease->clid_len)))
|
||||
memcpy(p, lease->clid, lease->clid_len);
|
||||
|
||||
if (in_list(req_options, OPTION_LEASE_TIME))
|
||||
{
|
||||
if (lease->expires == 0) /* infinite lease */
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, 0xffffffff);
|
||||
else
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, (unsigned int)(lease->expires - now));
|
||||
}
|
||||
|
||||
if (lease->expires != 0)
|
||||
{
|
||||
time = calc_time(context, config, NULL);
|
||||
|
||||
if (in_list(req_options, OPTION_T1) && (lease->expires - now) > time/2)
|
||||
option_put(mess, end, OPTION_T1, 4, ((unsigned int)(lease->expires - now)) - time/2);
|
||||
if (in_list(req_options, OPTION_T2) && (lease->expires - now) > time/8)
|
||||
option_put(mess, end, OPTION_T2, 4, ((unsigned int)(lease->expires - now)) - time/8);
|
||||
if (in_list(req_options, OPTION_LAST_TRANSACTION) && (lease->expires - now) < time)
|
||||
option_put(mess, end, OPTION_LAST_TRANSACTION, 4, time - ((unsigned int)(lease->expires - now)));
|
||||
}
|
||||
|
||||
if (lease->vendorclass)
|
||||
{
|
||||
memcpy(daemon->dhcp_buff3, lease->vendorclass, lease->vendorclass_len);
|
||||
vendor_class_len = lease->vendorclass_len;
|
||||
}
|
||||
|
||||
subnet_addr.s_addr = 0;
|
||||
do_options(context, mess, end, req_options, lease->hostname, get_domain(lease->addr), netid, subnet_addr,
|
||||
0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 1);
|
||||
|
||||
/* Does this have to be last for leasequery replies also? RFC 4388 is silent on the subject. */
|
||||
if (lease->agent_id && in_list(req_options, OPTION_AGENT_ID) &&
|
||||
(p = free_space(mess, end, OPTION_AGENT_ID, lease->agent_id_len)))
|
||||
memcpy(p, lease->agent_id, lease->agent_id_len);
|
||||
}
|
||||
|
||||
return dhcp_packet_size(mess, NULL, real_end);
|
||||
|
||||
case DHCPDECLINE:
|
||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
||||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
|
||||
@@ -1197,7 +1373,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
|
||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
|
||||
@@ -1528,7 +1704,21 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
lease_set_expires(lease, time, now);
|
||||
lease_set_interface(lease, int_index, now);
|
||||
|
||||
|
||||
if (option_bool(OPT_LEASEQUERY))
|
||||
{
|
||||
if (agent_id)
|
||||
lease_set_agent_id(lease, option_ptr(agent_id, 0), option_len(agent_id));
|
||||
if (vendor_class_len != 0)
|
||||
lease_set_vendorclass(lease, (unsigned char *)daemon->dhcp_buff3, vendor_class_len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* if leasequery no longer enabled, remove stuff that may have been stored when it was. */
|
||||
lease_set_agent_id(lease, NULL, 0);
|
||||
lease_set_vendorclass(lease, NULL, 0);
|
||||
}
|
||||
|
||||
if (override.s_addr != 0)
|
||||
lease->override = override;
|
||||
else
|
||||
@@ -1544,7 +1734,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (rapid_commit)
|
||||
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
|
||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||
}
|
||||
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
@@ -1611,7 +1801,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
|
||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
|
||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor, 0);
|
||||
|
||||
*is_inform = 1; /* handle reply differently */
|
||||
return dhcp_packet_size(mess, agent_id, real_end);
|
||||
@@ -1720,29 +1910,33 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
daemon->addrbuff[0] = 0;
|
||||
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
|
||||
|
||||
if (addr)
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
if (ext_mac)
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
|
||||
ntohl(xid),
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s%s%s%s",
|
||||
type,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
@@ -2412,7 +2606,8 @@ static void do_options(struct dhcp_context *context,
|
||||
time_t now,
|
||||
unsigned int lease_time,
|
||||
unsigned short fuzz,
|
||||
const char *pxevendor)
|
||||
const char *pxevendor,
|
||||
int leasequery)
|
||||
{
|
||||
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
|
||||
struct dhcp_boot *boot;
|
||||
@@ -2450,70 +2645,73 @@ static void do_options(struct dhcp_context *context,
|
||||
}
|
||||
}
|
||||
|
||||
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
||||
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
||||
break;
|
||||
if (id_list)
|
||||
mess->flags |= htons(0x8000); /* force broadcast */
|
||||
|
||||
if (context)
|
||||
mess->siaddr = context->local;
|
||||
|
||||
/* See if we can send the boot stuff as options.
|
||||
To do this we need a requested option list, BOOTP
|
||||
and very old DHCP clients won't have this, we also
|
||||
provide a manual option to disable it.
|
||||
Some PXE ROMs have bugs (surprise!) and need zero-terminated
|
||||
names, so we always send those. */
|
||||
if ((boot = find_boot(tagif)))
|
||||
if (!leasequery)
|
||||
{
|
||||
if (boot->sname)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_SNAME))
|
||||
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
|
||||
}
|
||||
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
||||
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
||||
break;
|
||||
if (id_list)
|
||||
mess->flags |= htons(0x8000); /* force broadcast */
|
||||
|
||||
if (boot->file)
|
||||
if (context)
|
||||
mess->siaddr = context->local;
|
||||
|
||||
/* See if we can send the boot stuff as options.
|
||||
To do this we need a requested option list, BOOTP
|
||||
and very old DHCP clients won't have this, we also
|
||||
provide a manual option to disable it.
|
||||
Some PXE ROMs have bugs (surprise!) and need zero-terminated
|
||||
names, so we always send those. */
|
||||
if ((boot = find_boot(tagif)))
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_FILENAME))
|
||||
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
|
||||
if (boot->sname)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_SNAME))
|
||||
option_put_string(mess, end, OPTION_SNAME, boot->sname, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->sname, boot->sname, sizeof(mess->sname));
|
||||
}
|
||||
|
||||
if (boot->file)
|
||||
{
|
||||
if (!option_bool(OPT_NO_OVERRIDE) &&
|
||||
req_options &&
|
||||
in_list(req_options, OPTION_FILENAME))
|
||||
option_put_string(mess, end, OPTION_FILENAME, boot->file, 1);
|
||||
else
|
||||
safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
|
||||
}
|
||||
|
||||
if (boot->next_server.s_addr)
|
||||
mess->siaddr = boot->next_server;
|
||||
else if (boot->tftp_sname)
|
||||
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
|
||||
}
|
||||
|
||||
if (boot->next_server.s_addr)
|
||||
mess->siaddr = boot->next_server;
|
||||
else if (boot->tftp_sname)
|
||||
mess->siaddr = a_record_from_hosts(boot->tftp_sname, now);
|
||||
}
|
||||
else
|
||||
/* Use the values of the relevant options if no dhcp-boot given and
|
||||
they're not explicitly asked for as options. OPTION_END is used
|
||||
as an internal way to specify siaddr without using dhcp-boot, for use in
|
||||
dhcp-optsfile. */
|
||||
{
|
||||
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
|
||||
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
else
|
||||
/* Use the values of the relevant options if no dhcp-boot given and
|
||||
they're not explicitly asked for as options. OPTION_END is used
|
||||
as an internal way to specify siaddr without using dhcp-boot, for use in
|
||||
dhcp-optsfile. */
|
||||
{
|
||||
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
|
||||
done_file = 1;
|
||||
if ((!req_options || !in_list(req_options, OPTION_FILENAME)) &&
|
||||
(opt = option_find2(OPTION_FILENAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->file, (char *)opt->val, sizeof(mess->file));
|
||||
done_file = 1;
|
||||
}
|
||||
|
||||
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
|
||||
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
|
||||
done_server = 1;
|
||||
}
|
||||
|
||||
if ((opt = option_find2(OPTION_END)))
|
||||
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
||||
}
|
||||
|
||||
if ((!req_options || !in_list(req_options, OPTION_SNAME)) &&
|
||||
(opt = option_find2(OPTION_SNAME)) && !(opt->flags & DHOPT_FORCE))
|
||||
{
|
||||
safe_strncpy((char *)mess->sname, (char *)opt->val, sizeof(mess->sname));
|
||||
done_server = 1;
|
||||
}
|
||||
|
||||
if ((opt = option_find2(OPTION_END)))
|
||||
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
||||
}
|
||||
|
||||
/* We don't want to do option-overload for BOOTP, so make the file and sname
|
||||
@@ -2534,7 +2732,7 @@ static void do_options(struct dhcp_context *context,
|
||||
end -= 3;
|
||||
|
||||
/* rfc3011 says this doesn't need to be in the requested options list. */
|
||||
if (subnet_addr.s_addr)
|
||||
if (!leasequery && subnet_addr.s_addr)
|
||||
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
|
||||
|
||||
if (lease_time != 0xffffffff)
|
||||
@@ -2575,14 +2773,17 @@ static void do_options(struct dhcp_context *context,
|
||||
/* replies to DHCPINFORM may not have a valid context */
|
||||
if (context)
|
||||
{
|
||||
if (!option_find2(OPTION_NETMASK))
|
||||
/* Netmask and broadcast always sent, except leasequery. */
|
||||
if (!option_find2(OPTION_NETMASK) &&
|
||||
(!leasequery || in_list(req_options, OPTION_NETMASK)))
|
||||
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
||||
|
||||
|
||||
/* May not have a "guessed" broadcast address if we got no packets via a relay
|
||||
from this net yet (ie just unicast renewals after a restart */
|
||||
if (context->broadcast.s_addr &&
|
||||
!option_find2(OPTION_BROADCAST))
|
||||
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
!option_find2(OPTION_BROADCAST) &&
|
||||
(!leasequery || in_list(req_options, OPTION_BROADCAST)))
|
||||
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||
|
||||
/* Same comments as broadcast apply, and also may not be able to get a sensible
|
||||
default when using subnet select. User must configure by steam in that case. */
|
||||
@@ -2663,7 +2864,7 @@ static void do_options(struct dhcp_context *context,
|
||||
continue;
|
||||
|
||||
/* was it asked for, or are we sending it anyway? */
|
||||
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
|
||||
if ((!(opt->flags & DHOPT_FORCE) || leasequery) && !in_list(req_options, optno))
|
||||
continue;
|
||||
|
||||
/* prohibit some used-internally options. T1 and T2 already handled. */
|
||||
@@ -2721,7 +2922,7 @@ static void do_options(struct dhcp_context *context,
|
||||
handle_encap(mess, end, req_options, null_term, tagif, pxe_arch != 1);
|
||||
|
||||
force_encap = prune_vendor_opts(tagif);
|
||||
|
||||
|
||||
if (context && pxe_arch != -1)
|
||||
{
|
||||
pxe_misc(mess, end, uuid, pxevendor);
|
||||
@@ -2729,8 +2930,8 @@ static void do_options(struct dhcp_context *context,
|
||||
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
||||
}
|
||||
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
|
||||
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
|
||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT) || in_list(req_options, OPTION_VENDOR_ID)) &&
|
||||
(leasequery || do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term)) &&
|
||||
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
||||
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
||||
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
||||
|
||||
+289
-186
@@ -41,11 +41,11 @@ static void sanitise(char *buf);
|
||||
#define ERR_ILL 4
|
||||
#define ERR_TID 5
|
||||
|
||||
void tftp_request(struct listener *listen, time_t now)
|
||||
static void tftp_request(struct listener *listen, time_t now)
|
||||
{
|
||||
ssize_t len;
|
||||
char *packet = daemon->packet;
|
||||
char *filename, *mode, *p, *end, *opt;
|
||||
char *filename, *mode, *p, *end;
|
||||
union mysockaddr addr, peer;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
@@ -198,11 +198,11 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
name = namebuff;
|
||||
|
||||
addra.addr4 = addr.in.sin_addr;
|
||||
|
||||
if (family == AF_INET6)
|
||||
addra.addr6 = addr.in6.sin6_addr;
|
||||
|
||||
else
|
||||
addra.addr4 = addr.in.sin_addr;
|
||||
|
||||
if (daemon->tftp_interfaces)
|
||||
{
|
||||
/* dedicated tftp interface list */
|
||||
@@ -308,7 +308,9 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
/* May reuse struct transfer from abandoned transfer in single port mode. */
|
||||
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
|
||||
return;
|
||||
|
||||
|
||||
memset(transfer, 0, sizeof(struct tftp_transfer));
|
||||
|
||||
if (option_bool(OPT_SINGLE_PORT))
|
||||
transfer->sockfd = listen->tftpfd;
|
||||
else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
|
||||
@@ -320,15 +322,13 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
transfer->peer = peer;
|
||||
transfer->source = addra;
|
||||
transfer->if_index = if_index;
|
||||
transfer->timeout = now + 2;
|
||||
transfer->timeout = 2;
|
||||
transfer->start = now;
|
||||
transfer->backoff = 1;
|
||||
transfer->block = 1;
|
||||
transfer->blocksize = 512;
|
||||
transfer->offset = 0;
|
||||
transfer->file = NULL;
|
||||
transfer->opt_blocksize = transfer->opt_transize = 0;
|
||||
transfer->netascii = transfer->carrylf = 0;
|
||||
|
||||
transfer->windowsize = 1;
|
||||
|
||||
(void)prettyprint_addr(&peer, daemon->addrbuff);
|
||||
|
||||
/* if we have a nailed-down range, iterate until we find a free one. */
|
||||
@@ -361,136 +361,167 @@ void tftp_request(struct listener *listen, time_t now)
|
||||
|
||||
p = packet + 2;
|
||||
end = packet + len;
|
||||
|
||||
len = 0;
|
||||
|
||||
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
|
||||
!(filename = next(&p, end)) ||
|
||||
!(mode = next(&p, end)) ||
|
||||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
if (ntohs(*((unsigned short *)packet)) == OP_WRQ)
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported write request from %s"),daemon->addrbuff, NULL);
|
||||
else if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
|
||||
{
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL);
|
||||
is_err = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcasecmp(mode, "netascii") == 0)
|
||||
transfer->netascii = 1;
|
||||
|
||||
while ((opt = next(&p, end)))
|
||||
if (!(filename = next(&p, end)))
|
||||
len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL);
|
||||
else if (!(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"),daemon->addrbuff, NULL);
|
||||
else
|
||||
{
|
||||
if (strcasecmp(opt, "blksize") == 0)
|
||||
char *opt, *arg;
|
||||
|
||||
if (strcasecmp(mode, "netascii") == 0)
|
||||
transfer->netascii = 1;
|
||||
|
||||
while ((opt = next(&p, end)) && (arg = next(&p, end)))
|
||||
{
|
||||
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
|
||||
unsigned int val = atoi(arg);
|
||||
|
||||
if (strcasecmp(opt, "blksize") == 0 && !option_bool(OPT_TFTP_NOBLOCK))
|
||||
{
|
||||
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
|
||||
int overhead = (family == AF_INET) ? 32 : 52;
|
||||
transfer->blocksize = atoi(opt);
|
||||
if (transfer->blocksize < 1)
|
||||
transfer->blocksize = 1;
|
||||
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
|
||||
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
|
||||
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
|
||||
transfer->blocksize = (unsigned)mtu - overhead;
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
if (val > (unsigned)daemon->packet_buff_sz - 4)
|
||||
val = (unsigned)daemon->packet_buff_sz - 4;
|
||||
if (mtu != 0 && val > (unsigned)mtu - overhead)
|
||||
val = (unsigned)mtu - overhead;
|
||||
transfer->blocksize = val;
|
||||
transfer->opt_blocksize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
|
||||
{
|
||||
transfer->opt_transize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* cope with backslashes from windows boxen. */
|
||||
for (p = filename; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
else if (option_bool(OPT_TFTP_LC))
|
||||
*p = tolower((unsigned char)*p);
|
||||
|
||||
strcpy(daemon->namebuff, "/");
|
||||
if (prefix)
|
||||
{
|
||||
if (prefix[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_IP))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
else if (strcasecmp(opt, "tsize") == 0 && !transfer->netascii)
|
||||
{
|
||||
transfer->opt_transize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
else if (strcasecmp(opt, "timeout") == 0)
|
||||
{
|
||||
if (val > 255)
|
||||
val = 255;
|
||||
transfer->timeout = val;
|
||||
transfer->opt_timeout = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
else if (strcasecmp(opt, "windowsize") == 0 && !transfer->netascii)
|
||||
{
|
||||
/* windowsize option only supported for binary transfers. */
|
||||
if (val < 1)
|
||||
val = 1;
|
||||
if (val > TFTP_MAX_WINDOW)
|
||||
val = TFTP_MAX_WINDOW;
|
||||
transfer->windowsize = val;
|
||||
transfer->opt_windowsize = 1;
|
||||
transfer->block = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_MAC))
|
||||
/* cope with backslashes from windows boxen. */
|
||||
for (p = filename; *p; p++)
|
||||
if (*p == '\\')
|
||||
*p = '/';
|
||||
else if (option_bool(OPT_TFTP_LC))
|
||||
*p = tolower((unsigned char)*p);
|
||||
|
||||
strcpy(daemon->namebuff, "/");
|
||||
if (prefix)
|
||||
{
|
||||
unsigned char *macaddr = NULL;
|
||||
unsigned char macbuf[DHCP_CHADDR_MAX];
|
||||
if (prefix[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
if (prefix[strlen(prefix)-1] != '/')
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
||||
{
|
||||
/* Check if the client IP is in our lease database */
|
||||
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
||||
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
||||
macaddr = lease->hwaddr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
||||
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
||||
macaddr = macbuf;
|
||||
|
||||
if (macaddr)
|
||||
{
|
||||
if (option_bool(OPT_TFTP_APREF_IP))
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||
|
||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
}
|
||||
|
||||
if (option_bool(OPT_TFTP_APREF_MAC))
|
||||
{
|
||||
unsigned char *macaddr = NULL;
|
||||
unsigned char macbuf[DHCP_CHADDR_MAX];
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
||||
{
|
||||
/* Check if the client IP is in our lease database */
|
||||
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
||||
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
||||
macaddr = lease->hwaddr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
||||
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
||||
macaddr = macbuf;
|
||||
|
||||
if (macaddr)
|
||||
{
|
||||
size_t oldlen = strlen(daemon->namebuff);
|
||||
struct stat statbuf;
|
||||
|
||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||
|
||||
/* remove unique-directory if it doesn't exist */
|
||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||
daemon->namebuff[oldlen] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Absolute pathnames OK if they match prefix */
|
||||
if (filename[0] == '/')
|
||||
{
|
||||
if (strstr(filename, daemon->namebuff) == filename)
|
||||
daemon->namebuff[0] = 0;
|
||||
else
|
||||
filename++;
|
||||
}
|
||||
}
|
||||
else if (filename[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||
|
||||
/* Absolute pathnames OK if they match prefix */
|
||||
if (filename[0] == '/')
|
||||
/* check permissions and open file */
|
||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
||||
{
|
||||
if (strstr(filename, daemon->namebuff) == filename)
|
||||
daemon->namebuff[0] = 0;
|
||||
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)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
else
|
||||
filename++;
|
||||
is_err = 0;
|
||||
}
|
||||
}
|
||||
else if (filename[0] == '/')
|
||||
daemon->namebuff[0] = 0;
|
||||
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 ((len = get_block(packet, transfer)) == -1)
|
||||
len = tftp_err_oops(packet, daemon->namebuff);
|
||||
else
|
||||
is_err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
|
||||
if (len)
|
||||
{
|
||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (is_err)
|
||||
free_transfer(transfer);
|
||||
@@ -606,6 +637,10 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||
{
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||
#endif
|
||||
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
handle_tftp(now, transfer, len);
|
||||
else
|
||||
@@ -624,66 +659,90 @@ void check_tftp_listeners(time_t now)
|
||||
|
||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||
{
|
||||
tmp = transfer->next;
|
||||
int endcon = 0, error = 0, timeout = 0;
|
||||
|
||||
if (difftime(now, transfer->timeout) >= 0.0)
|
||||
tmp = transfer->next;
|
||||
|
||||
/* ->start set to zero in handle_tftp() when we recv an error packet. */
|
||||
if (transfer->start == 0)
|
||||
endcon = error = 1;
|
||||
else if (difftime(now, transfer->start) > TFTP_TRANSFER_TIME)
|
||||
{
|
||||
int endcon = 0;
|
||||
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)
|
||||
error = timeout = 1;
|
||||
}
|
||||
else if (difftime(now, transfer->retransmit) >= 0.0)
|
||||
{
|
||||
/* Do transmission or re-transmission. When we get an ACK, the call to handle_tftp()
|
||||
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
||||
here. */
|
||||
ssize_t len;
|
||||
|
||||
/* timeout, retransmit */
|
||||
transfer->timeout += 1 + (1<<(transfer->backoff/2));
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if ((len = get_block(daemon->packet, transfer)) == -1)
|
||||
{
|
||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||
endcon = 1;
|
||||
}
|
||||
else if (++transfer->backoff > 7)
|
||||
{
|
||||
/* don't complain about timeout when we're awaiting the last
|
||||
ACK, some clients never send it */
|
||||
if ((unsigned)len == transfer->blocksize + 4)
|
||||
endcon = 1;
|
||||
len = 0;
|
||||
}
|
||||
|
||||
if (len != 0)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
if (endcon || len == 0)
|
||||
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
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
sanitise(daemon->namebuff);
|
||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
/* unlink */
|
||||
*up = tmp;
|
||||
if (endcon)
|
||||
free_transfer(transfer);
|
||||
else
|
||||
/* 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++)
|
||||
{
|
||||
/* put on queue to be sent to script and deleted */
|
||||
transfer->next = daemon->tftp_done_trans;
|
||||
daemon->tftp_done_trans = transfer;
|
||||
if (i != 0)
|
||||
len = get_block(daemon->packet, transfer);
|
||||
|
||||
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
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
up = &transfer->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (endcon)
|
||||
{
|
||||
strcpy(daemon->namebuff, transfer->file->filename);
|
||||
sanitise(daemon->namebuff);
|
||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||
if (timeout)
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("timeout sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
else if (error)
|
||||
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
else
|
||||
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||
|
||||
/* unlink */
|
||||
*up = tmp;
|
||||
if (error)
|
||||
free_transfer(transfer);
|
||||
else
|
||||
{
|
||||
/* put on queue to be sent to script and deleted */
|
||||
transfer->next = daemon->tftp_done_trans;
|
||||
daemon->tftp_done_trans = transfer;
|
||||
}
|
||||
}
|
||||
else
|
||||
up = &transfer->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* packet in daemon->packet as this is called. */
|
||||
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
{
|
||||
@@ -693,13 +752,30 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
|
||||
if (len >= (ssize_t)sizeof(struct ack))
|
||||
{
|
||||
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
|
||||
if (ntohs(mess->op) == OP_ACK)
|
||||
{
|
||||
/* Got ack, ensure we take the (re)transmit path */
|
||||
transfer->timeout = now;
|
||||
transfer->backoff = 0;
|
||||
if (transfer->block++ != 0)
|
||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
||||
/* 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. */
|
||||
if (block >= transfer->lastack &&
|
||||
block <= transfer->block)
|
||||
{
|
||||
/* Got ack, move forward and ensure we take the (re)transmit path */
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (ntohs(mess->op) == OP_ERR)
|
||||
{
|
||||
@@ -720,8 +796,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
||||
daemon->addrbuff);
|
||||
|
||||
/* Got err, ensure we take abort */
|
||||
transfer->timeout = now;
|
||||
transfer->backoff = 100;
|
||||
transfer->start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -755,14 +830,22 @@ static char *next(char **p, char *end)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If we don't do anything, don't write the the input/ouptut
|
||||
buffer. This allows us to pass in safe read-only strings constants. */
|
||||
static void sanitise(char *buf)
|
||||
{
|
||||
unsigned char *q, *r;
|
||||
|
||||
for (q = r = (unsigned char *)buf; *r; r++)
|
||||
if (isprint((int)*r))
|
||||
*(q++) = *r;
|
||||
*q = 0;
|
||||
|
||||
{
|
||||
if (q != r)
|
||||
*q = *r;
|
||||
q++;
|
||||
}
|
||||
|
||||
if (q != r)
|
||||
*q = 0;
|
||||
}
|
||||
|
||||
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */
|
||||
@@ -773,7 +856,10 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
|
||||
char message[];
|
||||
} *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);
|
||||
@@ -801,7 +887,10 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
|
||||
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
{
|
||||
memset(packet, 0, daemon->packet_buff_sz);
|
||||
|
||||
|
||||
/* we overwrote the buffer... */
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
if (transfer->block == 0)
|
||||
{
|
||||
/* send OACK */
|
||||
@@ -823,7 +912,17 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
p += (sprintf(p,"tsize") + 1);
|
||||
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
|
||||
}
|
||||
|
||||
if (transfer->opt_timeout)
|
||||
{
|
||||
p += (sprintf(p,"timeout") + 1);
|
||||
p += (sprintf(p, "%u", transfer->timeout) + 1);
|
||||
}
|
||||
if (transfer->opt_windowsize)
|
||||
{
|
||||
p += (sprintf(p,"windowsize") + 1);
|
||||
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
||||
}
|
||||
|
||||
return p - packet;
|
||||
}
|
||||
else
|
||||
@@ -834,31 +933,35 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
unsigned char data[];
|
||||
} *mess = (struct datamess *)packet;
|
||||
|
||||
size_t size = transfer->file->size - transfer->offset;
|
||||
size_t size;
|
||||
|
||||
if (!transfer->netascii)
|
||||
transfer->offset = (transfer->block - 1) * transfer->blocksize;
|
||||
|
||||
if (transfer->offset > transfer->file->size)
|
||||
return 0; /* finished */
|
||||
|
||||
if (size > transfer->blocksize)
|
||||
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
|
||||
size = transfer->blocksize;
|
||||
|
||||
mess->op = htons(OP_DATA);
|
||||
mess->block = htons((unsigned short)(transfer->block));
|
||||
|
||||
if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
||||
!read_write(transfer->file->fd, mess->data, size, RW_READ))
|
||||
|
||||
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;
|
||||
|
||||
transfer->expansion = 0;
|
||||
|
||||
/* Map '\n' to CR-LF in netascii mode */
|
||||
if (transfer->netascii)
|
||||
{
|
||||
size_t i;
|
||||
int newcarrylf;
|
||||
|
||||
|
||||
transfer->expansion = 0;
|
||||
|
||||
for (i = 0, newcarrylf = 0; i < size; i++)
|
||||
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
|
||||
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
|
||||
{
|
||||
transfer->expansion++;
|
||||
|
||||
@@ -873,8 +976,8 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
transfer->carrylf = newcarrylf;
|
||||
|
||||
}
|
||||
|
||||
return size + 4;
|
||||
|
||||
@@ -213,6 +213,11 @@ static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct u
|
||||
size_t num_patterns = 0;
|
||||
struct blob_attr *tb[policy_len];
|
||||
struct blob_attr *attr;
|
||||
|
||||
(void)ctx;
|
||||
(void)obj;
|
||||
(void)req;
|
||||
(void)method;
|
||||
|
||||
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
Reference in new issue
Block a user