Compare commits
32 Commits
v2.92test2
...
v2.92test1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c378d2c1de | ||
|
|
5846f749e5 | ||
|
|
c9a4240ec4 | ||
|
|
e7b87dee85 | ||
|
|
90b248582c | ||
|
|
ebef27f321 | ||
|
|
1861a881eb | ||
|
|
96bdb42d40 | ||
|
|
c7a909ad65 | ||
|
|
baf3c57af5 | ||
|
|
e48a2af4f5 | ||
|
|
91b800cc62 | ||
|
|
075e4a56b7 | ||
|
|
48658ebc54 | ||
|
|
692ed0dd32 | ||
|
|
43805c1859 | ||
|
|
4fbe1add95 | ||
|
|
57c7ae8fc0 | ||
|
|
d1008215dc | ||
|
|
b0aa604fcc | ||
|
|
8ddabd11bc | ||
|
|
98189ff988 | ||
|
|
e86d53c438 | ||
|
|
e127a972d1 | ||
|
|
a458c2bfb0 | ||
|
|
9e67099ce7 | ||
|
|
cfa1313e1f | ||
|
|
e3a2c8dadf | ||
|
|
95b74a7acf | ||
|
|
ae57f84061 | ||
|
|
0620309b73 | ||
|
|
942a35f517 |
42
CHANGELOG
42
CHANGELOG
@@ -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
|
||||
|
||||
695
contrib/leasequery/leasequery.c
Normal file
695
contrib/leasequery/leasequery.c
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,8 @@ only, to stop dnsmasq daemonising in production, use
|
||||
Log the results of DNS queries handled by dnsmasq. Enable a full cache dump on receipt of SIGUSR1. If the argument "extra" is supplied, ie
|
||||
.B --log-queries=extra
|
||||
then the log has extra information at the start of each line.
|
||||
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries.
|
||||
This consists of a serial number which ties together the log lines associated with an individual query, and the IP address of the requestor. If the argument "proto" is supplied, this shows everything that "extra" does and also the network protocol used to communicate the queries. Logging of only queries to the authoritative server can be configured with
|
||||
.B --log-queries=auth
|
||||
.TP
|
||||
.B \-8, --log-facility=<facility>
|
||||
Set the facility to which dnsmasq will send syslog entries, this
|
||||
@@ -345,6 +346,10 @@ Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192
|
||||
which are not found in /etc/hosts or the DHCP leases file are answered
|
||||
with "no such domain" rather than being forwarded upstream. The
|
||||
set of prefixes affected is the list given in RFC6303, for IPv4 and IPv6.
|
||||
Enabling this also subtly alters DNSSEC validation for reverse lookups in the
|
||||
private ranges such that a non-secure DS record is accepted as proof that
|
||||
the range is not signed. This works around behaviour by the public DNS services
|
||||
which seem not to return validated proof-of-non-existence for DS records in these domains.
|
||||
.TP
|
||||
.B \-V, --alias=[<old-ip>]|[<start-ip>-<end-ip>],<new-ip>[,<mask>]
|
||||
Modify IPv4 addresses returned from upstream nameservers; old-ip is
|
||||
@@ -1433,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
|
||||
|
||||
81
src/auth.c
81
src/auth.c
@@ -103,9 +103,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
unsigned char *p, *ansp;
|
||||
int qtype, qclass, rc;
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
int anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0;
|
||||
int auth = !local_query, trunc = 0, nxdomain = 1, soa = 0, ns = 0, axfr = 0, out_of_zone = 0, notimp = 0;
|
||||
struct auth_zone *zone = NULL;
|
||||
struct addrlist *subnet = NULL;
|
||||
char *cut;
|
||||
@@ -116,18 +116,20 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
union all_addr addr;
|
||||
struct cname *a, *candidate;
|
||||
unsigned int wclen;
|
||||
unsigned int log_flags = local_query ? 0 : F_NOERR;
|
||||
|
||||
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
|
||||
if (ntohs(header->qdcount) != 1)
|
||||
return 0;
|
||||
|
||||
/* determine end of question section (we put answers there) */
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
/* now process each question, answers go in RRs after the question */
|
||||
p = (unsigned char *)(header+1);
|
||||
|
||||
for (q = ntohs(header->qdcount); q != 0; q--)
|
||||
if (OPCODE(header) != QUERY)
|
||||
notimp = 1;
|
||||
else
|
||||
{
|
||||
unsigned int flag = 0;
|
||||
int found = 0;
|
||||
@@ -147,7 +149,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
auth = 0;
|
||||
out_of_zone = 1;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((qtype == T_PTR || qtype == T_SOA || qtype == T_NS) &&
|
||||
@@ -162,7 +164,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
else if (qtype == T_SOA)
|
||||
soa = 1, found = 1;
|
||||
@@ -210,7 +212,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (local_query || in_zone(zone, intr->name, NULL))
|
||||
{
|
||||
found = 1;
|
||||
log_query(flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||
log_query(log_flags | flag | F_REVERSE | F_CONFIG, intr->name, &addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
T_PTR, C_IN, "d", intr->name))
|
||||
@@ -234,7 +236,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
strcat(name, ".");
|
||||
strcat(name, zone->domain);
|
||||
}
|
||||
log_query(flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | flag | F_DHCP | F_REVERSE, name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -243,7 +245,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
else if (crecp->flags & (F_DHCP | F_HOSTS) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(crecp->flags & ~F_FORWARD, name, &addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | (crecp->flags & ~F_FORWARD), name, &addr, record_source(crecp->uid), 0);
|
||||
found = 1;
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL,
|
||||
@@ -257,7 +259,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (!found && is_rev_synth(flag, &addr, name) && (local_query || in_zone(zone, name, NULL)))
|
||||
{
|
||||
log_query(F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
|
||||
log_query(log_flags | F_CONFIG | F_REVERSE | flag, name, &addr, NULL, 0);
|
||||
found = 1;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
@@ -269,9 +271,9 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (found)
|
||||
nxdomain = 0;
|
||||
else
|
||||
log_query(flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||
log_query(log_flags | flag | F_NEG | F_NXDOMAIN | F_REVERSE | (auth ? F_AUTH : 0), NULL, &addr, NULL, 0);
|
||||
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
|
||||
cname_restart:
|
||||
@@ -288,7 +290,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
out_of_zone = 1;
|
||||
auth = 0;
|
||||
continue;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +302,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_MX)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<MX>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_MX, C_IN, "sd", rec->weight, rec->target))
|
||||
anscount++;
|
||||
@@ -315,7 +317,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_SRV)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<SRV>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_SRV, C_IN, "sssd",
|
||||
rec->priority, rec->weight, rec->srvport, rec->target))
|
||||
@@ -349,7 +351,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && txt->class == qtype)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, NULL, txt->class);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -363,7 +365,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_TXT)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<TXT>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_TXT, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -377,7 +379,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (rc == 2 && qtype == T_NAPTR)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||
log_query(log_flags | F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>", 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, T_NAPTR, C_IN, "sszzzd",
|
||||
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
|
||||
@@ -407,7 +409,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
continue;
|
||||
|
||||
found = 1;
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addrlist->addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &addrlist->addr))
|
||||
@@ -419,7 +421,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||
log_query(log_flags | F_FORWARD | F_CONFIG | flag, name, &addr, NULL, 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN, qtype == T_A ? "4" : "6", &addr))
|
||||
anscount++;
|
||||
@@ -432,7 +434,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (qtype == T_SOA)
|
||||
{
|
||||
auth = soa = 1; /* inhibits auth section */
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<SOA>", 0);
|
||||
}
|
||||
else if (qtype == T_AXFR)
|
||||
{
|
||||
@@ -468,13 +470,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
ns = 1; /* ensure we include NS records! */
|
||||
axfr = 1;
|
||||
axfroffset = nameoffset;
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<AXFR>", 0);
|
||||
}
|
||||
else if (qtype == T_NS)
|
||||
{
|
||||
auth = 1;
|
||||
ns = 1; /* inhibits auth section */
|
||||
log_query(F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||
log_query(log_flags | F_RRNAME | F_AUTH, zone->domain, NULL, "<NS>", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +494,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
(local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
*cut = '.'; /* restore domain part */
|
||||
log_query(crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | crecp->flags, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
*cut = 0; /* remove domain part */
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
@@ -513,7 +515,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
nxdomain = 0;
|
||||
if ((crecp->flags & flag) && (local_query || filter_zone(zone, flag, &(crecp->addr))))
|
||||
{
|
||||
log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
log_query(log_flags | (crecp->flags & ~F_REVERSE), name, &crecp->addr, record_source(crecp->uid), 0);
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->auth_ttl, NULL, qtype, C_IN,
|
||||
qtype == T_A ? "4" : "6", &crecp->addr))
|
||||
@@ -560,7 +562,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
if (candidate)
|
||||
{
|
||||
log_query(F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||
log_query(log_flags | F_CONFIG | F_CNAME, name, NULL, NULL, 0);
|
||||
strcpy(name, candidate->target);
|
||||
if (!strchr(name, '.'))
|
||||
{
|
||||
@@ -578,10 +580,12 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
else if (cache_find_non_terminal(name, now))
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||
log_query(log_flags | flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
/* Add auth section */
|
||||
if (auth && zone)
|
||||
@@ -873,7 +877,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
if (!(ansp = skip_questions(header, qlen)))
|
||||
return 0; /* bad packet */
|
||||
anscount = authcount = 0;
|
||||
log_query(F_AUTH, "reply", NULL, "truncated", 0);
|
||||
log_query(log_flags | F_AUTH, "reply", NULL, "truncated", 0);
|
||||
}
|
||||
|
||||
if ((auth || local_query) && nxdomain)
|
||||
@@ -885,14 +889,23 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
header->nscount = htons(authcount);
|
||||
header->arcount = htons(0);
|
||||
|
||||
if (!local_query && out_of_zone)
|
||||
if ((!local_query && out_of_zone) || notimp)
|
||||
{
|
||||
SET_RCODE(header, REFUSED);
|
||||
if (out_of_zone)
|
||||
{
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
addr.log.rcode = NOTIMP;
|
||||
addr.log.ede = EDE_UNSET;
|
||||
}
|
||||
|
||||
SET_RCODE(header, addr.log.rcode);
|
||||
header->ancount = htons(0);
|
||||
header->nscount = htons(0);
|
||||
addr.log.rcode = REFUSED;
|
||||
addr.log.ede = EDE_NOT_AUTH;
|
||||
log_query(F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
|
||||
log_query(log_flags | F_UPSTREAM | F_RCODE, "error", &addr, NULL, 0);
|
||||
return resize_packet(header, ansp - (unsigned char *)header, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
412
src/cache.c
412
src/cache.c
@@ -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] */
|
||||
@@ -779,7 +781,14 @@ void cache_end_insert(void)
|
||||
{
|
||||
if (insert_error)
|
||||
return;
|
||||
|
||||
|
||||
/* signal start of cache insert transaction to master process */
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
unsigned char op = PIPE_OP_INSERT;
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
}
|
||||
|
||||
while (new_chain)
|
||||
{
|
||||
struct crec *tmp = new_chain->next;
|
||||
@@ -806,7 +815,7 @@ void cache_end_insert(void)
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)name, m, RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->ttd, sizeof(new_chain->ttd), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&new_chain->addr, sizeof(new_chain->addr), RW_WRITE);
|
||||
|
||||
if (flags & F_RR)
|
||||
@@ -816,7 +825,7 @@ void cache_end_insert(void)
|
||||
blockdata_write(new_chain->addr.rrblock.rrdata, new_chain->addr.rrblock.datalen, daemon->pipe_to_parent);
|
||||
}
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
else if (flags & F_DNSKEY)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&class, sizeof(class), RW_WRITE);
|
||||
blockdata_write(new_chain->addr.key.keydata, new_chain->addr.key.keylen, daemon->pipe_to_parent);
|
||||
@@ -839,166 +848,237 @@ void cache_end_insert(void)
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
{
|
||||
ssize_t m = -1;
|
||||
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
m = daemon->metrics[METRIC_CRYPTO_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
m = daemon->metrics[METRIC_SIG_FAIL_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
m = daemon->metrics[METRIC_WORK_HWM];
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
new_chain = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
void cache_update_hwm(void)
|
||||
{
|
||||
/* Sneak out possibly updated crypto HWM values. */
|
||||
unsigned char op = PIPE_OP_STATS;
|
||||
|
||||
/* A marshalled cache entry arrives on fd, read, unmarshall and insert into cache of master process. */
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_CRYPTO_HWM],
|
||||
sizeof(daemon->metrics[METRIC_CRYPTO_HWM]), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_SIG_FAIL_HWM],
|
||||
sizeof(daemon->metrics[METRIC_SIG_FAIL_HWM]), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent,
|
||||
(unsigned char *)&daemon->metrics[METRIC_WORK_HWM],
|
||||
sizeof(daemon->metrics[METRIC_WORK_HWM]), RW_WRITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
void cache_send_ipset(unsigned char op, struct ipsets *sets, int flags, union all_addr *addr)
|
||||
{
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&sets, sizeof(sets), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&flags, sizeof(flags), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)addr, sizeof(*addr), RW_WRITE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Retrieve and handle a result from child TCP-handler.
|
||||
Return 0 when pipe is closed by far end. */
|
||||
int cache_recv_insert(time_t now, int fd)
|
||||
{
|
||||
ssize_t m;
|
||||
union all_addr addr;
|
||||
unsigned long ttl;
|
||||
time_t ttd;
|
||||
unsigned int flags;
|
||||
struct crec *crecp = NULL;
|
||||
unsigned char op;
|
||||
|
||||
cache_start_insert();
|
||||
if (!read_write(fd, &op, sizeof(op), RW_READ))
|
||||
return 0;
|
||||
|
||||
while (1)
|
||||
switch (op)
|
||||
{
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
case PIPE_OP_INSERT:
|
||||
{
|
||||
/* A marshalled set if cache entries arrives on fd, read, unmarshall and insert into cache of master process. */
|
||||
ssize_t m;
|
||||
union all_addr addr;
|
||||
unsigned long ttl;
|
||||
time_t ttd;
|
||||
unsigned int flags;
|
||||
struct crec *crecp = NULL;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* loop reading RRs, since we don't want to go back to the poll() loop
|
||||
and start processing other queries which might pollute the insertion
|
||||
chain. The child will never block between the first OP_RR and the
|
||||
minus-one length marking the end. */
|
||||
while (1)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
|
||||
if (m == -1)
|
||||
{
|
||||
cache_end_insert();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
daemon->namebuff[m] = 0;
|
||||
|
||||
ttl = difftime(ttd, now);
|
||||
|
||||
if (flags & F_CNAME)
|
||||
{
|
||||
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
|
||||
/* This relies on the fact that the target of a CNAME immediately precedes
|
||||
it because of the order of extraction in extract_addresses, and
|
||||
the order reversal on the new_chain. */
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
|
||||
if (!crecp)
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
else
|
||||
{
|
||||
next_uid(crecp);
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
newc->addr.cname.uid = crecp->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short class = C_IN;
|
||||
struct blockdata *block = NULL;
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(block = addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
continue;
|
||||
#ifdef HAVE_DNSSEC
|
||||
else if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
|
||||
return 0;
|
||||
if (!(block = addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
continue;
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ))
|
||||
return 0;
|
||||
if (!(flags & F_NEG) && !(block = addr.ds.keydata = blockdata_read(fd, addr.ds.keylen)))
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (!(crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags)))
|
||||
blockdata_free(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m == -1)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* Sneak in possibly updated crypto HWM. */
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_SIG_FAIL_HWM])
|
||||
daemon->metrics[METRIC_SIG_FAIL_HWM] = m;
|
||||
if (!read_write(fd, (unsigned char *)&m, sizeof(m), RW_READ))
|
||||
return 0;
|
||||
if (m > daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = m;
|
||||
#endif
|
||||
cache_end_insert();
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
/* UDP validation moved to TCP to avoid truncation.
|
||||
Restart UDP validation process with the returned result. */
|
||||
if (m == -2)
|
||||
{
|
||||
int status, uid, keycount, validatecount;
|
||||
int *keycountp, *validatecountp;
|
||||
size_t ret_len;
|
||||
|
||||
struct frec *forward;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ))
|
||||
return 0;
|
||||
if (!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
|
||||
return 0;
|
||||
|
||||
/* There's a tiny chance that the frec may have been freed
|
||||
and reused before the TCP process returns. Detect that with
|
||||
the uid field which is unique modulo 2^32 for each use. */
|
||||
if (uid == forward->uid)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
|
||||
else
|
||||
pop_and_retry_query(forward, status, now);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!read_write(fd, (unsigned char *)daemon->namebuff, m, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ttd, sizeof(ttd), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
daemon->namebuff[m] = 0;
|
||||
|
||||
ttl = difftime(ttd, now);
|
||||
case PIPE_OP_STATS:
|
||||
{
|
||||
/* Sneak in possibly updated crypto HWM. */
|
||||
unsigned int val;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_CRYPTO_HWM])
|
||||
daemon->metrics[METRIC_CRYPTO_HWM] = val;
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_SIG_FAIL_HWM])
|
||||
daemon->metrics[METRIC_SIG_FAIL_HWM] = val;
|
||||
if (!read_write(fd, (unsigned char *)&val, sizeof(val), RW_READ))
|
||||
return 0;
|
||||
if (val > daemon->metrics[METRIC_WORK_HWM])
|
||||
daemon->metrics[METRIC_WORK_HWM] = val;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (flags & F_CNAME)
|
||||
{
|
||||
struct crec *newc = really_insert(daemon->namebuff, NULL, C_IN, now, ttl, flags);
|
||||
/* This relies on the fact that the target of a CNAME immediately precedes
|
||||
it because of the order of extraction in extract_addresses, and
|
||||
the order reversal on the new_chain. */
|
||||
if (newc)
|
||||
{
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
|
||||
if (!crecp)
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
else
|
||||
{
|
||||
next_uid(crecp);
|
||||
newc->addr.cname.target.cache = crecp;
|
||||
newc->addr.cname.uid = crecp->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned short class = C_IN;
|
||||
|
||||
if ((flags & F_RR) && !(flags & F_NEG) && (flags & F_KEYTAG)
|
||||
&& !(addr.rrblock.rrdata = blockdata_read(fd, addr.rrblock.datalen)))
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (flags & F_DNSKEY)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
|
||||
!(addr.key.keydata = blockdata_read(fd, addr.key.keylen)))
|
||||
return 0;
|
||||
}
|
||||
else if (flags & F_DS)
|
||||
{
|
||||
if (!read_write(fd, (unsigned char *)&class, sizeof(class), RW_READ) ||
|
||||
(!(flags & F_NEG) && !(addr.key.keydata = blockdata_read(fd, addr.key.keylen))))
|
||||
return 0;
|
||||
}
|
||||
case PIPE_OP_RESULT:
|
||||
{
|
||||
/* UDP validation moved to TCP to avoid truncation.
|
||||
Restart UDP validation process with the returned result. */
|
||||
int status, uid, keycount, validatecount;
|
||||
int *keycountp, *validatecountp;
|
||||
size_t ret_len;
|
||||
|
||||
struct frec *forward;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&status, sizeof(status), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&ret_len, sizeof(ret_len), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)daemon->packet, ret_len, RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&forward, sizeof(forward), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&uid, sizeof(uid), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycount, sizeof(keycount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&keycountp, sizeof(keycountp), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecount, sizeof(validatecount), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&validatecountp, sizeof(validatecountp), RW_READ))
|
||||
return 0;
|
||||
|
||||
/* There's a tiny chance that the frec may have been freed
|
||||
and reused before the TCP process returns. Detect that with
|
||||
the uid field which is unique modulo 2^32 for each use. */
|
||||
if (uid == forward->uid)
|
||||
{
|
||||
/* repatriate the work counters from the child process. */
|
||||
*keycountp = keycount;
|
||||
*validatecountp = validatecount;
|
||||
|
||||
if (!forward->dependent)
|
||||
return_reply(now, forward, (struct dns_header *)daemon->packet, ret_len, status);
|
||||
else
|
||||
pop_and_retry_query(forward, status, now);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
crecp = really_insert(daemon->namebuff, &addr, class, now, ttl, flags);
|
||||
}
|
||||
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
case PIPE_OP_IPSET:
|
||||
case PIPE_OP_NFTSET:
|
||||
{
|
||||
struct ipsets *sets;
|
||||
char **sets_cur;
|
||||
unsigned int flags;
|
||||
union all_addr addr;
|
||||
|
||||
if (!read_write(fd, (unsigned char *)&sets, sizeof(sets), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&flags, sizeof(flags), RW_READ) ||
|
||||
!read_write(fd, (unsigned char *)&addr, sizeof(addr), RW_READ))
|
||||
return 0;
|
||||
|
||||
for (sets_cur = sets->sets; *sets_cur; sets_cur++)
|
||||
{
|
||||
int rc = -1;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (op == PIPE_OP_IPSET)
|
||||
rc = add_to_ipset(*sets_cur, &addr, flags, 0);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NFTSET
|
||||
if (op == PIPE_OP_NFTSET)
|
||||
rc = add_to_nftset(*sets_cur, &addr, flags, 0);
|
||||
#endif
|
||||
|
||||
if (rc == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, sets->domain, &addr, *sets_cur, op == PIPE_OP_IPSET);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cache_find_non_terminal(char *name, time_t now)
|
||||
@@ -2175,12 +2255,17 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
char *extra = "";
|
||||
char *gap = " ";
|
||||
char portstring[7]; /* space for #<portnum> */
|
||||
|
||||
char opcodestring[3]; /* maximum is 15 */
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
/* F_NOERR is reused here to indicate logs arrising from auth queries */
|
||||
if (!(flags & F_NOERR) && option_bool(OPT_AUTH_LOG))
|
||||
return;
|
||||
|
||||
/* build query type string if requested */
|
||||
if (!(flags & (F_SERVER | F_IPSET)) && type > 0)
|
||||
if (!(flags & (F_SERVER | F_IPSET | F_QUERY)) && type > 0)
|
||||
arg = querystr(arg, type);
|
||||
|
||||
dest = arg;
|
||||
@@ -2213,6 +2298,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
dest = "SERVFAIL";
|
||||
else if (rcode == REFUSED)
|
||||
dest = "REFUSED";
|
||||
else if (rcode == FORMERR)
|
||||
dest = "FORMERR";
|
||||
else if (rcode == NOTIMP)
|
||||
dest = "not implemented";
|
||||
else
|
||||
@@ -2271,6 +2358,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = arg;
|
||||
else if (flags & F_UPSTREAM)
|
||||
source = "reply";
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_SECSTAT)
|
||||
{
|
||||
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
|
||||
@@ -2281,8 +2370,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "validation";
|
||||
dest = arg;
|
||||
}
|
||||
else if (flags & F_AUTH)
|
||||
source = "auth";
|
||||
else if (flags & F_DNSSEC)
|
||||
{
|
||||
source = arg;
|
||||
@@ -2293,11 +2380,6 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "forwarded";
|
||||
verb = "to";
|
||||
}
|
||||
else if (flags & F_QUERY)
|
||||
{
|
||||
source = arg;
|
||||
verb = "from";
|
||||
}
|
||||
else if (flags & F_IPSET)
|
||||
{
|
||||
source = type ? "ipset add" : "nftset add";
|
||||
@@ -2309,7 +2391,21 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
source = "cached-stale";
|
||||
else
|
||||
source = "cached";
|
||||
|
||||
|
||||
if (flags & F_QUERY)
|
||||
{
|
||||
if (flags & F_CONFIG)
|
||||
{
|
||||
sprintf(opcodestring, "%u", type & 0xf);
|
||||
source = "non-query opcode";
|
||||
name = opcodestring;
|
||||
}
|
||||
else if (!(flags & F_AUTH))
|
||||
source = "query";
|
||||
|
||||
verb = "from";
|
||||
}
|
||||
|
||||
if (!name)
|
||||
gap = name = "";
|
||||
else if (!name[0])
|
||||
|
||||
12
src/config.h
12
src/config.h
@@ -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 */
|
||||
@@ -153,6 +155,7 @@ NO_AUTH
|
||||
NO_DUMPFILE
|
||||
NO_LOOP
|
||||
NO_INOTIFY
|
||||
NO_IPSET
|
||||
these are available to explicitly disable compile time options which would
|
||||
otherwise be enabled automatically or which are enabled by default
|
||||
in the distributed source tree. Building dnsmasq
|
||||
@@ -287,7 +290,6 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#define HAVE_BSD_NETWORK
|
||||
#define HAVE_GETOPT_LONG
|
||||
#define HAVE_SOCKADDR_SA_LEN
|
||||
#define NO_IPSET
|
||||
/* Define before sys/socket.h is included so we get socklen_t */
|
||||
#define _BSD_SOCKLEN_T_
|
||||
/* Select the RFC_3542 version of the IPv6 socket API.
|
||||
@@ -297,7 +299,6 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#ifndef SOL_TCP
|
||||
# define SOL_TCP IPPROTO_TCP
|
||||
#endif
|
||||
#define NO_IPSET
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#define HAVE_BSD_NETWORK
|
||||
@@ -347,6 +348,11 @@ HAVE_SOCKADDR_SA_LEN
|
||||
#undef HAVE_AUTH
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_LINUX_NETWORK)
|
||||
#undef HAVE_IPSET
|
||||
#undef HAVE_NFTSET
|
||||
#endif
|
||||
|
||||
#if defined(NO_IPSET)
|
||||
#undef HAVE_IPSET
|
||||
#endif
|
||||
@@ -460,4 +466,4 @@ static char *compile_opts =
|
||||
#endif
|
||||
"dumpfile";
|
||||
|
||||
#endif /* defined(HAVE_DHCP) */
|
||||
#endif /* defined(DNSMASQ_COMPILE_OPTS) */
|
||||
|
||||
@@ -768,10 +768,10 @@ static DBusMessage *dbus_get_server_metrics(DBusMessage* message)
|
||||
add_dict_entry(&dict_array, "address", daemon->namebuff);
|
||||
|
||||
add_dict_int(&dict_array, "port", port);
|
||||
add_dict_int(&dict_array, "queries", serv->queries);
|
||||
add_dict_int(&dict_array, "failed_queries", serv->failed_queries);
|
||||
add_dict_int(&dict_array, "nxdomain", serv->nxdomain_replies);
|
||||
add_dict_int(&dict_array, "retries", serv->retrys);
|
||||
add_dict_int(&dict_array, "queries", queries);
|
||||
add_dict_int(&dict_array, "failed_queries", failed_queries);
|
||||
add_dict_int(&dict_array, "nxdomain", nxdomain_replies);
|
||||
add_dict_int(&dict_array, "retries", retrys);
|
||||
add_dict_int(&dict_array, "latency", sigma_latency/count_latency);
|
||||
|
||||
dbus_message_iter_close_container(&server_array, &dict_array);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
35
src/dhcp.c
35
src/dhcp.c
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
@@ -38,7 +40,6 @@ static void async_event(int pipe, time_t now);
|
||||
static void fatal_event(struct event_desc *ev, char *msg);
|
||||
static int read_event(int fd, struct event_desc *evp, char **msg);
|
||||
static void poll_resolv(int force, int do_reload, time_t now);
|
||||
static void tcp_init(void);
|
||||
|
||||
int main (int argc, char **argv)
|
||||
{
|
||||
@@ -63,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
|
||||
@@ -78,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
|
||||
@@ -422,7 +429,11 @@ int main (int argc, char **argv)
|
||||
/* safe_malloc returns zero'd memory */
|
||||
daemon->randomsocks = safe_malloc(daemon->numrrand * sizeof(struct randfd));
|
||||
|
||||
tcp_init();
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
@@ -1071,10 +1082,6 @@ int main (int argc, char **argv)
|
||||
|
||||
daemon->pipe_to_parent = -1;
|
||||
|
||||
if (daemon->port != 0)
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
|
||||
#ifdef HAVE_INOTIFY
|
||||
/* Using inotify, have to select a resolv file at startup */
|
||||
poll_resolv(1, 0, now);
|
||||
@@ -1940,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);
|
||||
|
||||
@@ -2053,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
|
||||
|
||||
@@ -2124,6 +2134,10 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
|
||||
#endif
|
||||
|
||||
close(daemon->pipe_to_parent);
|
||||
flush_log();
|
||||
_exit(0);
|
||||
@@ -2229,10 +2243,10 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
ssize_t m = -2;
|
||||
unsigned char op = PIPE_OP_RESULT;
|
||||
|
||||
/* tell our parent we're done, and what the result was then exit. */
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&m, sizeof(m), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, &op, sizeof(op), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&status, sizeof(status), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)plen, sizeof(*plen), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)header, *plen, RW_WRITE);
|
||||
@@ -2242,6 +2256,9 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&keycount, sizeof(keycount), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)validatecount, sizeof(*validatecount), RW_WRITE);
|
||||
read_write(daemon->pipe_to_parent, (unsigned char *)&validatecount, sizeof(validatecount), RW_WRITE);
|
||||
|
||||
cache_update_hwm(); /* Sneak out possibly updated crypto HWM values. */
|
||||
|
||||
close(daemon->pipe_to_parent);
|
||||
|
||||
flush_log();
|
||||
@@ -2412,8 +2429,4 @@ int delay_dhcp(time_t start, int sec, int fd, uint32_t addr, unsigned short id)
|
||||
}
|
||||
#endif /* HAVE_DHCP */
|
||||
|
||||
void tcp_init(void)
|
||||
{
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -281,7 +287,9 @@ struct event_desc {
|
||||
#define OPT_LOG_PROTO 73
|
||||
#define OPT_NO_0x20 74
|
||||
#define OPT_DO_0x20 75
|
||||
#define OPT_LAST 76
|
||||
#define OPT_AUTH_LOG 76
|
||||
#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) )
|
||||
@@ -536,6 +544,11 @@ struct crec {
|
||||
#define SRC_HOSTS 2
|
||||
#define SRC_AH 3
|
||||
|
||||
#define PIPE_OP_INSERT 1 /* Cache entry */
|
||||
#define PIPE_OP_RESULT 2 /* Validation result */
|
||||
#define PIPE_OP_STATS 3 /* Update parent's stats */
|
||||
#define PIPE_OP_IPSET 4 /* Update IPset */
|
||||
#define PIPE_OP_NFTSET 5 /* Update NFTset */
|
||||
|
||||
/* struct sockaddr is not large enough to hold any address,
|
||||
and specifically not big enough to hold an IPv6 address.
|
||||
@@ -553,9 +566,9 @@ union mysockaddr {
|
||||
|
||||
|
||||
/* The actual values here matter, since we sort on them to get records in the order
|
||||
IPv6 addr, IPv4 addr, all zero return, resolvconf servers, upstream server, no-data return */
|
||||
#define SERV_LITERAL_ADDRESS 1 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||
#define SERV_USE_RESOLV 2 /* forward this domain in the normal way */
|
||||
IPv6 addr, IPv4 addr, all zero return, no-data return, resolvconf servers, upstream server */
|
||||
#define SERV_USE_RESOLV 1 /* forward this domain in the normal way */
|
||||
#define SERV_LITERAL_ADDRESS 2 /* addr is the answer, or NoDATA is the answer, depending on the next four flags */
|
||||
#define SERV_ALL_ZEROS 4 /* return all zeros for A and AAAA */
|
||||
#define SERV_4ADDR 8 /* addr is IPv4 */
|
||||
#define SERV_6ADDR 16 /* addr is IPv6 */
|
||||
@@ -858,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;
|
||||
@@ -1102,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;
|
||||
};
|
||||
@@ -1177,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;
|
||||
@@ -1353,6 +1367,13 @@ void cache_end_insert(void);
|
||||
void cache_start_insert(void);
|
||||
unsigned int cache_remove_uid(const unsigned int uid);
|
||||
int cache_recv_insert(time_t now, int fd);
|
||||
#ifdef HAVE_DNSSEC
|
||||
void cache_update_hwm(void);
|
||||
#endif
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
void cache_send_ipset(unsigned char op, struct ipsets *sets,
|
||||
int flags, union all_addr *addr);
|
||||
#endif
|
||||
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
|
||||
time_t now, unsigned long ttl, unsigned int flags);
|
||||
void cache_reload(void);
|
||||
@@ -1397,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);
|
||||
@@ -1416,6 +1437,7 @@ int add_resource_record(struct dns_header *header, char *limit, int *truncp,
|
||||
int *offset, unsigned short type, unsigned short class, char *format, ...);
|
||||
int in_arpa_name_2_addr(char *namein, union all_addr *addrp);
|
||||
int private_net(struct in_addr addr, int ban_localhost);
|
||||
int private_net6(struct in6_addr *a, int ban_localhost);
|
||||
/* extract_name ops */
|
||||
#define EXTR_NAME_EXTRACT 1
|
||||
#define EXTR_NAME_COMPARE 2
|
||||
@@ -1605,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);
|
||||
@@ -1617,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);
|
||||
@@ -1637,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
|
||||
@@ -1741,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
|
||||
|
||||
11
src/dnssec.c
11
src/dnssec.c
@@ -1020,7 +1020,16 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
{
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
if (option_bool(OPT_BOGUSPRIV) &&
|
||||
(flags = in_arpa_name_2_addr(name, &a)) &&
|
||||
((flags == F_IPV6 && private_net6(&a.addr6, 0)) || (flags == F_IPV4 && private_net(a.addr4, 0))))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming that's OK for a RFC-1918 address."), name);
|
||||
neganswer = 1;
|
||||
nons = 0; /* If we're faking a DS, fake one with an NS. */
|
||||
neg_ttl = DNSSEC_ASSUMED_DS_TTL;
|
||||
}
|
||||
else if (lookup_domain(name, F_DOMAINSRV, NULL, NULL))
|
||||
{
|
||||
my_syslog(LOG_INFO, _("Insecure reply received for DS %s, assuming non-DNSSEC domain-specific server."), name);
|
||||
neganswer = 1;
|
||||
|
||||
@@ -20,7 +20,7 @@ static int order(char *qdomain, size_t qlen, struct server *serv);
|
||||
static int order_qsort(const void *a, const void *b);
|
||||
static int order_servers(struct server *s, struct server *s2);
|
||||
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRES, it lives on the local_domains chain. */
|
||||
/* If the server is USE_RESOLV or LITERAL_ADDRESS, it lives on the local_domains chain. */
|
||||
#define SERV_IS_LOCAL (SERV_USE_RESOLV | SERV_LITERAL_ADDRESS)
|
||||
|
||||
void build_server_array(void)
|
||||
@@ -259,7 +259,6 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Return first server in group of equivalent servers; this is the "master" record. */
|
||||
int server_samegroup(struct server *a, struct server *b)
|
||||
{
|
||||
return order_servers(a, b) == 0;
|
||||
@@ -296,11 +295,13 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Now the servers are on order between low and high, in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, resolvconf servers, send upstream, no-data return.
|
||||
/* Now the matching server records are all between low and high.
|
||||
order_qsort() ensures that they are in the order
|
||||
IPv6 addr, IPv4 addr, return zero for both, no-data return,
|
||||
"use resolvconf" servers, domain-specific upstream servers.
|
||||
|
||||
See which of those match our query in that priority order and narrow (low, high) */
|
||||
|
||||
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_6ADDR); i++);
|
||||
|
||||
if (!(flags & F_SERVER) && i != nlow && (flags & F_IPV6))
|
||||
@@ -325,29 +326,25 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
|
||||
{
|
||||
nlow = i;
|
||||
|
||||
/* Short to resolv.conf servers */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||
/* now look for a NXDOMAIN answer --local=/domain/ */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
|
||||
if (i != nlow)
|
||||
if (!(flags & (F_DOMAINSRV | F_SERVER)) && i != nlow)
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
/* now look for a server */
|
||||
for (i = nlow; i < nhigh && !(daemon->serverarray[i]->flags & SERV_LITERAL_ADDRESS); i++);
|
||||
nlow = i;
|
||||
|
||||
/* return "use resolv.conf servers" if they exist */
|
||||
for (i = nlow; i < nhigh && (daemon->serverarray[i]->flags & SERV_USE_RESOLV); i++);
|
||||
|
||||
if (i != nlow)
|
||||
{
|
||||
/* 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)
|
||||
nlow = nhigh;
|
||||
else
|
||||
nhigh = i;
|
||||
}
|
||||
nhigh = i;
|
||||
else
|
||||
{
|
||||
/* --local=/domain/, only return if we don't need a server. */
|
||||
if (flags & (F_DOMAINSRV | F_SERVER))
|
||||
nhigh = i;
|
||||
/* If we want a server for a particular domain, and this one isn't, return nothing. */
|
||||
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
|
||||
nlow = nhigh;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -402,6 +399,8 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
gotname &= ~F_QUERY;
|
||||
|
||||
if (flags & (F_NXDOMAIN | F_NOERR))
|
||||
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
|
||||
|
||||
@@ -541,12 +540,14 @@ static int order_qsort(const void *a, const void *b)
|
||||
rc = order_servers(s1, s2);
|
||||
|
||||
/* Sort all literal NODATA and local IPV4 or IPV6 responses together,
|
||||
in a very specific order. We flip the SERV_LITERAL_ADDRESS bit
|
||||
so the order is IPv6 literal, IPv4 literal, all-zero literal,
|
||||
unqualified servers, upstream server, NXDOMAIN literal. */
|
||||
in a very specific order IPv6 literal, IPv4 literal, all-zero literal,
|
||||
NXDOMAIN literal. We also include SERV_USE_RESOLV in this, so that
|
||||
use-standard servers sort before ordinary servers. (SERV_USR_RESOLV set
|
||||
implies that none of SERV_LITERAL_ADDRESS,SERV_4ADDR,SERV_6ADDR,SERV_ALL_ZEROS
|
||||
are set) */
|
||||
if (rc == 0)
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_USE_RESOLV | SERV_ALL_ZEROS)) ^ SERV_LITERAL_ADDRESS);
|
||||
rc = ((s2->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV))) -
|
||||
((s1->flags & (SERV_LITERAL_ADDRESS | SERV_4ADDR | SERV_6ADDR | SERV_ALL_ZEROS | SERV_USE_RESOLV)));
|
||||
|
||||
/* Finally, order by appearance in /etc/resolv.conf etc, for --strict-order */
|
||||
if (rc == 0)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
@@ -704,16 +706,19 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
(void)ad_reqd;
|
||||
(void)do_bit;
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (daemon->ipsets && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
|
||||
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
|
||||
if ((daemon->ipsets || daemon->nftsets) && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
# ifdef HAVE_IPSET
|
||||
ipsets = domain_find_sets(daemon->ipsets, daemon->namebuff);
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_NFTSET
|
||||
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NFTSET
|
||||
if (daemon->nftsets && extract_name(header, n, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
nftsets = domain_find_sets(daemon->nftsets, daemon->namebuff);
|
||||
#endif
|
||||
|
||||
|
||||
if ((pheader = find_pseudoheader(header, n, &plen, &sizep, &is_sign, NULL)))
|
||||
{
|
||||
/* Get extended RCODE. */
|
||||
@@ -816,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)
|
||||
{
|
||||
@@ -1816,14 +1821,14 @@ void receive_query(struct listener *listen, time_t now)
|
||||
#endif
|
||||
|
||||
if (OPCODE(header) != QUERY)
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &source_addr, "non-query", 0);
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR : 0) | F_QUERY | F_FORWARD | F_CONFIG, NULL, &source_addr, NULL, OPCODE(header));
|
||||
else if (extract_request(header, (size_t)n, daemon->namebuff, &type, NULL))
|
||||
{
|
||||
#ifdef HAVE_AUTH
|
||||
struct auth_zone *zone;
|
||||
#endif
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&source_addr, auth_dns ? "auth" : "query", type);
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0 ) | F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&source_addr, NULL, type);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
@@ -2459,7 +2464,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
if (OPCODE(header) != QUERY)
|
||||
{
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, "opcode", &peer_addr, "non-query", 0);
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR : 0) | F_QUERY | F_FORWARD | F_CONFIG, NULL, &peer_addr, NULL, OPCODE(header));
|
||||
gotname = 0;
|
||||
flags = F_RCODE;
|
||||
}
|
||||
@@ -2488,8 +2493,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
saved_question = blockdata_alloc((char *)header, (size_t)size);
|
||||
saved_size = size;
|
||||
|
||||
log_query_mysockaddr(F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, auth_dns ? "auth" : "query", qtype);
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, NULL, qtype);
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
/* Find queries for zones we're authoritative for, and answer them directly.
|
||||
|
||||
191
src/inotify.c
191
src/inotify.c
@@ -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 = sizeof(dd->dname);
|
||||
|
||||
/* ah->fname always starts with the string in dd->dname */
|
||||
for (ah = dd->files; ah; ah = ah->next)
|
||||
if (ah->fname[dirlen+2] == '/' &&
|
||||
strcmp(&ah->fname[dirlen+3], 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined(HAVE_IPSET) && defined(HAVE_LINUX_NETWORK)
|
||||
#if defined(HAVE_IPSET)
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
132
src/lease.c
132
src/lease.c
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
@@ -1665,7 +1667,7 @@ void check_servers(int no_loop_check)
|
||||
if (++locals <= LOCALS_LOGGED)
|
||||
my_syslog(LOG_INFO, _("using only locally-known addresses for %s"), serv->domain);
|
||||
}
|
||||
else if (serv->flags & SERV_USE_RESOLV)
|
||||
else if (serv->flags & SERV_USE_RESOLV && serv->domain_len != 0)
|
||||
my_syslog(LOG_INFO, _("using standard nameservers for %s"), serv->domain);
|
||||
}
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#if defined (HAVE_NFTSET) && defined (HAVE_LINUX_NETWORK)
|
||||
#if defined (HAVE_NFTSET)
|
||||
|
||||
#include <nftables/libnftables.h>
|
||||
|
||||
|
||||
23
src/option.c
23
src/option.c
@@ -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;
|
||||
@@ -3435,6 +3454,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
set_option_bool(OPT_EXTRALOG);
|
||||
set_option_bool(OPT_LOG_PROTO);
|
||||
}
|
||||
else if (strcmp(arg, "auth") == 0)
|
||||
set_option_bool(OPT_AUTH_LOG);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
288
src/rfc1035.c
288
src/rfc1035.c
@@ -424,7 +424,7 @@ int private_net(struct in_addr addr, int ban_localhost)
|
||||
((ip_addr & 0xFFFFFFFF) == 0xFFFFFFFF) /* 255.255.255.255/32 (broadcast)*/ ;
|
||||
}
|
||||
|
||||
static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
{
|
||||
/* Block IPv4-mapped IPv6 addresses in private IPv4 address space */
|
||||
if (IN6_IS_ADDR_V4MAPPED(a))
|
||||
@@ -534,9 +534,6 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
if (substring)
|
||||
*substring = name_len;
|
||||
|
||||
if (ttlp)
|
||||
*ttlp = daemon->neg_ttl;
|
||||
|
||||
for (i = 0; i < ntohs(header->nscount); i++)
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
@@ -690,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;
|
||||
@@ -707,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();
|
||||
|
||||
@@ -727,114 +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))
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
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 (!(p1 = skip_questions(header, qlen)))
|
||||
return 2;
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
/* 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);
|
||||
int secflag = 0;
|
||||
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
|
||||
return 2; /* bad packet */
|
||||
|
||||
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
|
||||
if (name_encoding && ttl)
|
||||
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)
|
||||
{
|
||||
flags |= F_REVERSE | name_encoding;
|
||||
if (!have_soa)
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
|
||||
found = 1;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
|
||||
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;
|
||||
@@ -846,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;
|
||||
|
||||
@@ -864,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;
|
||||
|
||||
@@ -882,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)
|
||||
@@ -889,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)
|
||||
{
|
||||
@@ -903,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;
|
||||
@@ -916,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;
|
||||
}
|
||||
@@ -1049,24 +1000,39 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* If we're a child process, send this to the parent,
|
||||
since the ipset and nfset access is not re-entrant. */
|
||||
#ifdef HAVE_IPSET
|
||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
|
||||
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
|
||||
if (ipsets)
|
||||
{
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
cache_send_ipset(PIPE_OP_IPSET, ipsets, flags, &addr);
|
||||
else
|
||||
for (ipsets_cur = ipsets->sets; *ipsets_cur; ipsets_cur++)
|
||||
if (add_to_ipset(*ipsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, ipsets->domain, &addr, *ipsets_cur, 1);
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_NFTSET
|
||||
if (nftsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
|
||||
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
|
||||
if (nftsets)
|
||||
{
|
||||
if (daemon->pipe_to_parent != -1)
|
||||
cache_send_ipset(PIPE_OP_NFTSET, nftsets, flags, &addr);
|
||||
else
|
||||
for (nftsets_cur = nftsets->sets; *nftsets_cur; nftsets_cur++)
|
||||
if (add_to_nftset(*nftsets_cur, &addr, flags, 0) == 0)
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, nftsets->domain, &addr, *nftsets_cur, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -1114,26 +1080,27 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
|
||||
|
||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
if (!ttl)
|
||||
ttl = cttl;
|
||||
|
||||
addr.rrdata.datalen = substring;
|
||||
addr.rrdata.rrtype = qtype;
|
||||
|
||||
if (!have_soa)
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
}
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
if (have_soa || daemon->neg_ttl)
|
||||
{
|
||||
if (have_soa)
|
||||
{
|
||||
addr.rrdata.datalen = substring;
|
||||
addr.rrdata.rrtype = qtype;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
|
||||
ttl = daemon->neg_ttl;
|
||||
flags |= F_NO_RR; /* Marks no SOA found. */
|
||||
}
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1644,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;
|
||||
|
||||
@@ -1654,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;
|
||||
@@ -2324,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)))
|
||||
|
||||
441
src/rfc2131.c
441
src/rfc2131.c
@@ -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,
|
||||
|
||||
475
src/tftp.c
475
src/tftp.c
@@ -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