mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10fbc39cf5 | ||
|
|
2463798e03 | ||
|
|
a570a139d5 | ||
|
|
738c049988 | ||
|
|
145abca7f6 | ||
|
|
a627a7847f | ||
|
|
cdbfb97227 | ||
|
|
11493fe218 | ||
|
|
7719737f36 | ||
|
|
3a3e7a049f | ||
|
|
bc80b93cd1 | ||
|
|
03fd0c19ef | ||
|
|
baeb7430e2 | ||
|
|
f90df6c532 | ||
|
|
a9880c595f | ||
|
|
ad9208358e | ||
|
|
baf85a8f38 | ||
|
|
a9cb3971c6 | ||
|
|
f031f5b4b3 | ||
|
|
cf9c9c74e0 | ||
|
|
b518bc425d | ||
|
|
0c177ad607 | ||
|
|
4388c38468 | ||
|
|
0a31ade4db | ||
|
|
a4ebe438df | ||
|
|
8d8ad76d89 | ||
|
|
b50b55e227 | ||
|
|
7a663fd46b | ||
|
|
1f122521bb | ||
|
|
8af20e56ed | ||
|
|
8aa607d322 | ||
|
|
52982cfe8f | ||
|
|
81d78d9c75 | ||
|
|
7ebe7ebb93 | ||
|
|
1da5cc2951 | ||
|
|
5990261a57 | ||
|
|
b6b7e5a6fd | ||
|
|
f8c162b8b0 | ||
|
|
c93e9ce592 | ||
|
|
bf80262874 | ||
|
|
ea4645d52c | ||
|
|
ea9bd30cd4 | ||
|
|
1af6ee6468 | ||
|
|
c310853907 | ||
|
|
3ff66da573 | ||
|
|
d823d8e9cd | ||
|
|
2c78b6db6c | ||
|
|
4bf46ceb37 | ||
|
|
7d42376a3a | ||
|
|
08cc209e7a | ||
|
|
ff4964d01d | ||
|
|
45771dc92c | ||
|
|
af73e48067 | ||
|
|
77d4749a5e | ||
|
|
0375e40824 | ||
|
|
18a139f48f | ||
|
|
d5d2bfb74a | ||
|
|
7f5802c144 | ||
|
|
7280e46aa5 | ||
|
|
754177b368 | ||
|
|
228d7ff964 |
No files matched your search
@@ -1,6 +1,22 @@
|
||||
version 2.94
|
||||
Record that CVE-2026-12969 has been allocated to the
|
||||
buffer OOB read in find_soa() which is fixed in git commit
|
||||
14094e88beca519c53151184cc4553656672b54f, released at 2.93.
|
||||
Thanks to do litli for spotting this.
|
||||
|
||||
Fix erroneous behaviour of --no-dhcpv4-interface with
|
||||
constructed DHCPv6/RA - DHCPv6 and RA are inhibited by
|
||||
--no-dhcpv4-interface when using constructed address ranges.
|
||||
Thanks to David Nichols for discovery, diagnosis and patch.
|
||||
|
||||
Fix heap OOB read in zone_status. Thanks to Yaohui Zhu (朱耀辉)
|
||||
for spotting and reporting this. A crafted reply can crash dnsmasq.
|
||||
CNVD-C-2026-590208 applies.
|
||||
|
||||
|
||||
version 2.93
|
||||
Fix a corner-case in DNSSEC validation with wildcards. If we have
|
||||
a wildcard record *.example.com and recieve a query for
|
||||
a wildcard record *.example.com and receive a query for
|
||||
a.example.com then that's OK, but we have to check that there isn't
|
||||
an actual a.example.com record. The corner case is when we get a
|
||||
query for *.example.com in that case the non-existence check
|
||||
@@ -19,7 +35,7 @@ version 2.93
|
||||
(eg rivcoed.org) will still fail. Thanks to Petr Menšík
|
||||
for the bug report.
|
||||
|
||||
Fix DNSSEC fail with CNAME replies to DS queries. As CNAME reply
|
||||
Fix DNSSEC fail with CNAME replies to DS queries. A CNAME reply
|
||||
to a DNSSEC query was confusing the validation logic. It now
|
||||
accepts a signed CNAME reply to a DS query as proof that no DS
|
||||
exists at the domain. This fixes the DS/zone break detection logic.
|
||||
|
||||
@@ -233,31 +233,55 @@ struct dhcp6_packet create_release_packet(const char* iaid, const char* ip, cons
|
||||
return result;
|
||||
}
|
||||
|
||||
static void copy_option_string(char *dst, size_t dst_len, const char *src, size_t src_len)
|
||||
{
|
||||
if (dst_len == 0)
|
||||
return;
|
||||
|
||||
if (src_len >= dst_len)
|
||||
src_len = dst_len - 1;
|
||||
|
||||
memcpy(dst, src, src_len);
|
||||
dst[src_len] = '\0';
|
||||
}
|
||||
|
||||
uint16_t parse_iana_suboption(char* buf, size_t len)
|
||||
{
|
||||
size_t current_pos = 0;
|
||||
char option_value[1024];
|
||||
while (current_pos < len)
|
||||
{
|
||||
if (len - current_pos < 2 * sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t option_type, option_len;
|
||||
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
|
||||
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
|
||||
option_type = ntohs(option_type);
|
||||
option_len = ntohs(option_len);
|
||||
current_pos += 2 * sizeof(uint16_t);
|
||||
if (option_len > len - current_pos)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
if (option_type == STATUS_CODE)
|
||||
{
|
||||
uint16_t status;
|
||||
if (option_len < sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
memcpy(&status, buf + current_pos, sizeof(uint16_t));
|
||||
status = ntohs(status);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
memcpy(option_value, buf + current_pos + sizeof(uint16_t) , option_len - sizeof(uint16_t));
|
||||
option_value[option_len-sizeof(uint16_t)] ='\0';
|
||||
copy_option_string(option_value, sizeof(option_value),
|
||||
buf + current_pos + sizeof(uint16_t),
|
||||
option_len - sizeof(uint16_t));
|
||||
fprintf(stderr, "Error: %s\n", option_value);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
current_pos += option_len;
|
||||
}
|
||||
|
||||
return -2;
|
||||
@@ -266,6 +290,9 @@ uint16_t parse_iana_suboption(char* buf, size_t len)
|
||||
int16_t parse_packet(char* buf, size_t len)
|
||||
{
|
||||
int16_t ret = -1;
|
||||
if (len < 4)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint8_t type = buf[0];
|
||||
/*skipping tx id. you need it, uncomment following line
|
||||
uint16_t tx_id = ntohs((buf[1] <<16) + (buf[2] <<8) + buf[3]);
|
||||
@@ -277,20 +304,31 @@ int16_t parse_packet(char* buf, size_t len)
|
||||
char option_value[1024];
|
||||
while (current_pos < len)
|
||||
{
|
||||
if (len - current_pos < 2 * sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t option_type, option_len;
|
||||
memcpy(&option_type,buf + current_pos, sizeof(uint16_t));
|
||||
memcpy(&option_len,buf + current_pos + sizeof(uint16_t), sizeof(uint16_t));
|
||||
option_type = ntohs(option_type);
|
||||
option_len = ntohs(option_len);
|
||||
current_pos += 2 * sizeof(uint16_t);
|
||||
if (option_len > len - current_pos)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
if (option_type == STATUS_CODE)
|
||||
{
|
||||
uint16_t status;
|
||||
if (option_len < sizeof(uint16_t))
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
memcpy(&status, buf + current_pos, sizeof(uint16_t));
|
||||
status = ntohs(status);
|
||||
if (status != SUCCESS)
|
||||
{
|
||||
memcpy(option_value, buf + current_pos +sizeof(uint16_t) , option_len -sizeof(uint16_t));
|
||||
copy_option_string(option_value, sizeof(option_value),
|
||||
buf + current_pos + sizeof(uint16_t),
|
||||
option_len - sizeof(uint16_t));
|
||||
fprintf(stderr, "Error: %d %s\n", status, option_value);
|
||||
return status;
|
||||
}
|
||||
@@ -301,6 +339,9 @@ int16_t parse_packet(char* buf, size_t len)
|
||||
|
||||
if (option_type == IA_NA )
|
||||
{
|
||||
if (option_len < 24)
|
||||
return UNSPEC_FAIL;
|
||||
|
||||
uint16_t result = parse_iana_suboption(buf + current_pos +24, option_len -24);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
@@ -331,9 +331,15 @@ static char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
if (len == 0)
|
||||
sprintf(p, "<null>");
|
||||
else
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
|
||||
{
|
||||
/* Server-controlled lengths (e.g. BOOTREPLY hlen) must not be trusted
|
||||
to fit the destination buffer; cap at the actual chaddr field size. */
|
||||
if (len > DHCP_CHADDR_MAX)
|
||||
len = DHCP_CHADDR_MAX;
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
}
|
||||
|
||||
return buff;
|
||||
}
|
||||
|
||||
|
||||
+48
-11
@@ -154,6 +154,9 @@ allows the log file to be rotated without stopping dnsmasq.
|
||||
.B --log-debug
|
||||
Enable extra logging intended for debugging rather than information.
|
||||
.TP
|
||||
.B --log-malloc
|
||||
Enable logging of memory allocation and release, for debugging.
|
||||
.TP
|
||||
.B --log-async[=<lines>]
|
||||
Enable asynchronous logging and optionally set the limit on the
|
||||
number of lines
|
||||
@@ -171,12 +174,18 @@ Specify an alternate path for dnsmasq to record its process-id in. Normally /var
|
||||
.B \-u, --user=<username>
|
||||
Specify the userid to which dnsmasq will change after startup. Dnsmasq must normally be started as root, but it will drop root
|
||||
privileges after startup by changing id to another user. Normally this user is "nobody" but that
|
||||
can be over-ridden with this switch.
|
||||
can be over-ridden with this switch.
|
||||
.TP
|
||||
.B \-g, --group=<groupname>
|
||||
Specify the group which dnsmasq will run
|
||||
as. The default is "dip", if available, to facilitate access to
|
||||
/etc/ppp/resolv.conf which is not normally world readable.
|
||||
/etc/ppp/resolv.conf which is not normally world readable. If "dip" is not available, the default becomes
|
||||
the main group associated with the uid specified by
|
||||
.B --user
|
||||
, or "nobody".
|
||||
Specifying
|
||||
.B --group
|
||||
without an argument inhibits any change to the gid of the process, even to the defaults.
|
||||
.TP
|
||||
.B \-v, --version
|
||||
Print the version number.
|
||||
@@ -865,7 +874,7 @@ will be logged. If this coincides with DNS not functioning, it
|
||||
is necessary to disable the feature. As at version 2.91, 0x20 encoding
|
||||
is disabled by default, and must be enabled with --do-0x20-encode. The default
|
||||
may change in the future, so to be sure of its status after an upgrade, set --do-0x20-encode
|
||||
or --no-0x20-encode in your config. --no-0x20-encode overrides --do-x20-encode or a future default
|
||||
or --no-0x20-encode in your config. --no-0x20-encode overrides --do-0x20-encode or a future default
|
||||
0x20-encode enable.
|
||||
.TP
|
||||
.B --use-stale-cache[=<max TTL excess in s>]
|
||||
@@ -1038,7 +1047,10 @@ options. If the lease time is given, then leases
|
||||
will be given for that length of time. The lease time is in seconds,
|
||||
or minutes (eg 45m) or hours (eg 1h) or days (2d) or weeks (1w) or "infinite". If not given,
|
||||
the default lease time is one hour for IPv4 and one day for IPv6. The
|
||||
minimum lease time is two minutes. For IPv6 ranges, the lease time
|
||||
minimum lease time is two minutes. An explicitly configured lease time also
|
||||
affects IPv6 router advertisements: it acts as a ceiling on the advertised
|
||||
prefix lifetimes. For constructed ranges it also acts as a floor, unless the
|
||||
prefix is being deprecated. For IPv6 ranges, the lease time
|
||||
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
|
||||
lease or router advertisement to zero, which causes clients to use
|
||||
other addresses, if available, for new connections as a prelude to renumbering.
|
||||
@@ -1326,7 +1338,7 @@ have exactly the same effect as
|
||||
options containing the same information. /etc/ethers is re-read when
|
||||
dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers.
|
||||
.TP
|
||||
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
|
||||
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt>|option:<opt-name>|option4:<opt>|option4:<optname>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
|
||||
Send various extra options to DHCP clients. By default,
|
||||
dnsmasq sends some standard options to DHCP clients, the netmask and
|
||||
broadcast address are set to the same as the host running dnsmasq, and
|
||||
@@ -1345,6 +1357,13 @@ For example, to set the default route option to 192.168.4.4, use
|
||||
and to set the time-server address to 192.168.0.4, use
|
||||
\fB--dhcp-option=42,192.168.0.4\fP or
|
||||
\fB--dhcp-option=option:ntp-server,192.168.0.4\fP.
|
||||
|
||||
The above examples are backwards compatible, for consistency with IPV6, the time-server example
|
||||
may also be given as
|
||||
\fB--dhcp-option=option:42,192.168.0.4\fP or
|
||||
\fB--dhcp-option=option4:42,192.168.0.4\fP or
|
||||
\fB--dhcp-option=option4:ntp-server,192.168.0.4\fP
|
||||
|
||||
The special address 0.0.0.0 means "the address of the system running dnsmasq".
|
||||
|
||||
An option without data is valid, and includes just the option without data.
|
||||
@@ -1369,11 +1388,14 @@ described in RFC 3442.
|
||||
IPv6 options are specified using the \fBoption6:\fP
|
||||
keyword, followed by the option number or option name. The IPv6 option
|
||||
name space is disjoint from the IPv4 option name space. IPv6 addresses
|
||||
in options must be bracketed with square brackets, eg.
|
||||
\fB --dhcp-option=option6:ntp-server,[1234::56]\fP.
|
||||
in options must be bracketed with square brackets, eg. \fB --dhcp-option=option6:sntp-server,[1234::56]\fP or
|
||||
--dhcp-option=option6:31,[1234::56].
|
||||
For IPv6, [::] means "the global address of
|
||||
the machine running dnsmasq", whilst [fd00::] is replaced with the
|
||||
ULA, if it exists, and [fe80::] with the link-local address.
|
||||
ULA, if it exists, and [fe80::] with the link-local address. The vendorclass option
|
||||
is special for DHCPv6, since it requires an enterprise number which must appear as a decimal number
|
||||
before the class(es). eg. \fB --dhcp-option=option6:vendor-class,343,HTTPClient\fP
|
||||
|
||||
|
||||
Be careful: data-type suitability for the option number sent is not checked.
|
||||
It is quite possible to persuade dnsmasq to generate illegal DHCP packets with
|
||||
@@ -1899,7 +1921,12 @@ DNSMASQ_CLIENT_ID if the host provided a client-id.
|
||||
|
||||
DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID if a
|
||||
DHCP relay-agent added any of these options.
|
||||
|
||||
|
||||
DNSMASQ_CPEWAN_OUI, DNSMASQ_CPEWAN_SERIAL, DNSMASQ_CPEWAN_CLASS from
|
||||
an RFC3925 Vendor-Identifying Vendor-Specific Information Option
|
||||
containing the Broadband Forum enterprise number and options
|
||||
defined in TR-069.
|
||||
|
||||
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
|
||||
|
||||
For IPv6 only:
|
||||
@@ -1922,7 +1949,17 @@ only supplied for
|
||||
since these data are not held in dnsmasq's lease
|
||||
database.
|
||||
|
||||
|
||||
Please note that the environment options DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn,
|
||||
DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn,
|
||||
DNSMASQ_VENDOR_CLASS,
|
||||
DNSMASQ_CIRCUIT_ID, DNSMASQ_SUBSCRIBER_ID, DNSMASQ_REMOTE_ID,
|
||||
DNSMASQ_CPEWAN_OUI, DNSMASQ_CPEWAN_SERIAL, DNSMASQ_CPEWAN_CLASS,
|
||||
DNSMASQ_MUD_URL all contain values which are derived directly from untrusted data recieved
|
||||
from clients. Be very careful with quoting when using these values, lest an attacker sends
|
||||
a vendor class of "rm -rf /". Note also that the RFCs defining these options tend to simply
|
||||
define them an opaque data: the implication but not the letter of the definition is
|
||||
printable strings. In practise, dnsmasq passes all octets except zero, which is treated
|
||||
as a terminator.
|
||||
|
||||
All file descriptors are
|
||||
closed except stdin, which is open to /dev/null, and stdout and stderr which capture output for logging by dnsmasq.
|
||||
@@ -2701,7 +2738,7 @@ IPv4 and IPv6 addresses from /etc/hosts (and
|
||||
and
|
||||
.B --interface-name
|
||||
and
|
||||
.B ---dynamic-host
|
||||
.B --dynamic-host
|
||||
provided the address falls into one of the subnets specified in the
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
|
||||
+2
-2
@@ -869,7 +869,7 @@ Om detta sammanfaller med att DNS inte fungerar, är det
|
||||
nödvändigt att inaktivera funktionen. I version 2.91 är 0x20-kodning
|
||||
inaktiverad som standard och måste aktiveras med --do-0x20-encode. Standardinställningen
|
||||
kan komma att ändras i framtiden, så för att vara säker på dess status efter en uppgradering, ställ in --do-0x20-encode
|
||||
eller --no-0x20-encode i din konfiguration. --no-0x20-encode åsidosätter --do-x20-encode eller en framtida standardinställning
|
||||
eller --no-0x20-encode i din konfiguration. --no-0x20-encode åsidosätter --do-0x20-encode eller en framtida standardinställning
|
||||
0x20-encode enable.
|
||||
.TP
|
||||
.B --use-stale-cache[=<max TTL excess in s>]
|
||||
@@ -2732,7 +2732,7 @@ IPv4- och IPv6-adresser från /etc/hosts (och
|
||||
och
|
||||
.B --interface-name
|
||||
och
|
||||
.B ---dynamic-host
|
||||
.B --dynamic-host
|
||||
förutsatt att adressen faller inom ett av de subnät som anges i
|
||||
.B --auth-zone.
|
||||
.PP
|
||||
|
||||
+60
-31
@@ -16,7 +16,9 @@
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
static struct blockdata *keyblock_free;
|
||||
static struct blockdata *new_block(void);
|
||||
|
||||
static struct blockdata *keyblock_free, *fallback;
|
||||
static unsigned int blockdata_count, blockdata_hwm, blockdata_alloced;
|
||||
|
||||
static void add_blocks(int n)
|
||||
@@ -48,7 +50,11 @@ void blockdata_init(void)
|
||||
/* Note that daemon->cachesize is enforced to have non-zero size if OPT_DNSSEC_VALID is set */
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
add_blocks(daemon->cachesize);
|
||||
}
|
||||
|
||||
/* make sure we have one block to fall back on. */
|
||||
if (!(fallback = new_block()))
|
||||
die(_("Blockdata init: no memory"), NULL, EC_MISC);
|
||||
}
|
||||
|
||||
void blockdata_report(void)
|
||||
{
|
||||
@@ -84,36 +90,52 @@ static struct blockdata *blockdata_alloc_real(int fd, char *data, size_t len)
|
||||
struct blockdata *block, *ret = NULL;
|
||||
struct blockdata **prev = &ret;
|
||||
size_t blen;
|
||||
int fail;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(block = new_block()))
|
||||
{
|
||||
/* failed to alloc, free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len) > 0)
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
}
|
||||
else if (!read_write(fd, block->key, blen, RW_READ))
|
||||
{
|
||||
/* failed read free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
len -= blen;
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
} while (len != 0);
|
||||
/* Zero length still gives one empty block, blockdata_alloc(NULL, 0)
|
||||
is how callers start a chain to add to with blockdata_expand(). */
|
||||
if (len == 0)
|
||||
return new_block();
|
||||
|
||||
for (fail = 0; len != 0; len -= blen)
|
||||
{
|
||||
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
|
||||
|
||||
if (!fail)
|
||||
{
|
||||
if ((block = new_block()))
|
||||
{
|
||||
*prev = block;
|
||||
prev = &block->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Failed to alloc, use the pre-allocated fall-back.
|
||||
This allows us to always read the specified amount of data from the fd. */
|
||||
fail = 1;
|
||||
block = fallback;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
memcpy(block->key, data, blen);
|
||||
data += blen;
|
||||
}
|
||||
else if (!read_write(fd, block->key, blen, RW_READ))
|
||||
{
|
||||
fail = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail)
|
||||
{
|
||||
/* failed, free partial chain */
|
||||
blockdata_free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -132,7 +154,7 @@ int blockdata_expand(struct blockdata *block, size_t oldlen, char *data, size_t
|
||||
/* find size of current final block */
|
||||
for (b = block; oldlen > KEYBLOCK_LEN && b; b = b->next, oldlen -= KEYBLOCK_LEN);
|
||||
|
||||
/* chain to short for length, something is broken */
|
||||
/* chain too short for length, something is broken */
|
||||
if (oldlen > KEYBLOCK_LEN)
|
||||
{
|
||||
blockdata_free(block);
|
||||
@@ -235,6 +257,13 @@ void blockdata_write(struct blockdata *block, size_t len, int fd)
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: this can fail (NULL return) for two possible reasons.
|
||||
1) end of stream.
|
||||
2) malloc failure when allocating blocks.
|
||||
|
||||
Unless EOF is encountered, len octets will always be read from the stream,
|
||||
even in memory allocation fails.
|
||||
*/
|
||||
struct blockdata *blockdata_read(int fd, size_t len)
|
||||
{
|
||||
return blockdata_alloc_real(fd, NULL, len);
|
||||
|
||||
+66
-32
@@ -217,29 +217,42 @@ static void free_config_crec(struct crec *p)
|
||||
config_spare = p;
|
||||
}
|
||||
|
||||
static struct nameblock *nameblock_spare = NULL;
|
||||
|
||||
static char *store_name(unsigned int namelen, unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
char *ret = NULL;
|
||||
int i;
|
||||
|
||||
if (namelen > NAMEBLOCK_CHARS)
|
||||
return NULL;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
break;
|
||||
|
||||
if (!block && ((block = whine_malloc(sizeof(struct nameblock)))))
|
||||
{
|
||||
block->next = hostblocks;
|
||||
block->index = index;
|
||||
hostblocks = block;
|
||||
}
|
||||
|
||||
if (block && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
/* Check the first few blocks for possible space. This allows efficient memory usage
|
||||
without blowing up O(n^2) CPU usage which the linked list gets long. */
|
||||
for (i = 10, block = hostblocks; i != 0 && block; block = block->next, i--)
|
||||
if (block->index == index && NAMEBLOCK_CHARS - block->last >= namelen)
|
||||
{
|
||||
ret = &block->data[block->last];
|
||||
block->last += namelen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nameblock_spare)
|
||||
{
|
||||
block = nameblock_spare;
|
||||
nameblock_spare = nameblock_spare->next;
|
||||
}
|
||||
else
|
||||
block = whine_malloc(sizeof(struct nameblock));
|
||||
|
||||
if (block)
|
||||
{
|
||||
block->next = hostblocks;
|
||||
block->index = index;
|
||||
block->last = namelen;
|
||||
hostblocks = block;
|
||||
ret = &block->data[0];
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -247,11 +260,20 @@ static char *store_name(unsigned int namelen, unsigned int index)
|
||||
|
||||
static void free_names(unsigned int index)
|
||||
{
|
||||
struct nameblock *block;
|
||||
struct nameblock *block, *tmp, **up;
|
||||
|
||||
for (block = hostblocks; block; block = block->next)
|
||||
if (index == UID_NONE || block->index == index)
|
||||
block->last = 0;
|
||||
for (up = &hostblocks, block = hostblocks; block; block = tmp)
|
||||
{
|
||||
tmp = block->next;
|
||||
if (index == UID_NONE || block->index == index)
|
||||
{
|
||||
*up = tmp;
|
||||
block->next = nameblock_spare;
|
||||
nameblock_spare = block;
|
||||
}
|
||||
else
|
||||
up = &block->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* In most cases, we create the hash table once here by calling this with (hash_table == NULL)
|
||||
@@ -1017,9 +1039,18 @@ int cache_recv_insert(time_t now, int fd)
|
||||
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;
|
||||
/* Note that blockdata_read() can return NULL either
|
||||
because the stream ended, OR memory allocation failed.
|
||||
We continue here rather than returning 0 to finish draining
|
||||
the pipe in the second case. If the stream ended, the read
|
||||
of the next length at the top of the loop will fail
|
||||
and we'll return 0 there. */
|
||||
if (flags & F_RR)
|
||||
{
|
||||
if (!(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)
|
||||
{
|
||||
@@ -1091,10 +1122,12 @@ int cache_recv_insert(time_t now, int fd)
|
||||
if (op == PIPE_OP_KILLED)
|
||||
my_syslog(LOG_INFO, _("TCP process for DNSSEC validation timed out"));
|
||||
|
||||
/* 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)
|
||||
/* There's a chance that the frec may have timed-out and been
|
||||
freed before the TCP process returns, so check that the
|
||||
frec is still marked in use. Even if it is in use, 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 (forward->sentto && uid == forward->uid)
|
||||
{
|
||||
if (op == PIPE_OP_RESULT)
|
||||
{
|
||||
@@ -1407,9 +1440,10 @@ static int eatspace(FILE *f)
|
||||
}
|
||||
}
|
||||
|
||||
static int gettok(FILE *f, char *token)
|
||||
static int gettok(FILE *f, char *token, size_t buffsz)
|
||||
{
|
||||
int c, count = 0;
|
||||
int c;
|
||||
unsigned int count = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
@@ -1422,7 +1456,7 @@ static int gettok(FILE *f, char *token)
|
||||
return eatspace(f);
|
||||
}
|
||||
|
||||
if (count < (MAXDNAMESTR - 1))
|
||||
if (count < (buffsz - 1))
|
||||
{
|
||||
token[count++] = c;
|
||||
token[count] = 0;
|
||||
@@ -1447,7 +1481,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
|
||||
lineno += eatspace(f);
|
||||
|
||||
while ((atnl = gettok(f, token)) != -1)
|
||||
while ((atnl = gettok(f, token, MAXDNAMESTR)) != -1)
|
||||
{
|
||||
if (inet_pton(AF_INET, token, &addr) > 0)
|
||||
{
|
||||
@@ -1465,7 +1499,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
{
|
||||
my_syslog(LOG_ERR, _("bad address at %s line %d"), filename, lineno);
|
||||
while (atnl == 0)
|
||||
atnl = gettok(f, token);
|
||||
atnl = gettok(f, token, MAXDNAMESTR);
|
||||
lineno += atnl;
|
||||
continue;
|
||||
}
|
||||
@@ -1483,7 +1517,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
|
||||
int fqdn, nomem;
|
||||
char *canon;
|
||||
|
||||
if ((atnl = gettok(f, token)) == -1)
|
||||
if ((atnl = gettok(f, token, MAXDNAMESTR)) == -1)
|
||||
break;
|
||||
|
||||
fqdn = !!strchr(token, '.');
|
||||
@@ -2306,7 +2340,7 @@ static char *edestr(int ede)
|
||||
}
|
||||
}
|
||||
|
||||
static int error_occured(unsigned int flags) {
|
||||
static int error_occurred(unsigned int flags) {
|
||||
if (flags & F_RCODE)
|
||||
return 1;
|
||||
else if (flags & F_NEG)
|
||||
@@ -2327,10 +2361,10 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
if(option_bool(OPT_LOG_ONLY_FAILED) && !error_occured(flags))
|
||||
if(option_bool(OPT_LOG_ONLY_FAILED) && !error_occurred(flags))
|
||||
return;
|
||||
|
||||
/* F_NOERR is reused here to indicate logs arrising from auth queries */
|
||||
/* F_NOERR is reused here to indicate logs arising from auth queries */
|
||||
if (!(flags & F_NOERR) && option_bool(OPT_AUTH_LOG))
|
||||
return;
|
||||
|
||||
|
||||
+3
-3
@@ -1055,7 +1055,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
DBusConnection *connection = (DBusConnection *)daemon->dbus;
|
||||
DBusMessage* message = NULL;
|
||||
DBusMessageIter args;
|
||||
char *action_str, *mac = daemon->namebuff;
|
||||
char *action_str, *mac;
|
||||
unsigned char *p;
|
||||
int i;
|
||||
|
||||
@@ -1068,7 +1068,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
#ifdef HAVE_DHCP6
|
||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||
{
|
||||
print_mac(mac, lease->clid, lease->clid_len);
|
||||
mac = print_mac(lease->clid, lease->clid_len);
|
||||
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
else
|
||||
@@ -1076,7 +1076,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
|
||||
{
|
||||
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
|
||||
lease->hwaddr, lease->clid_len, lease->clid, &i);
|
||||
print_mac(mac, p, i);
|
||||
mac = print_mac(p, i);
|
||||
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
}
|
||||
|
||||
|
||||
+35
-4
@@ -734,7 +734,7 @@ static const struct opttab_t opttab6[] = {
|
||||
{ "status", 13, OT_INTERNAL },
|
||||
{ "rapid-commit", 14, OT_INTERNAL },
|
||||
{ "user-class", 15, OT_INTERNAL | OT_CSTRING },
|
||||
{ "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
|
||||
{ "vendor-class", 16, OT_DHCP6_VENDOR },
|
||||
{ "vendor-opts", 17, OT_INTERNAL },
|
||||
{ "sip-server-domain", 21, OT_RFC1035_NAME },
|
||||
{ "sip-server", 22, OT_ADDR_LIST },
|
||||
@@ -909,6 +909,37 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
|
||||
buf[j++] = ',';
|
||||
}
|
||||
}
|
||||
else if ((ot[o].size & OT_DHCP6_VENDOR))
|
||||
{
|
||||
unsigned int enterprise;
|
||||
unsigned char *p = &val[0];
|
||||
|
||||
if (opt_len >= 4)
|
||||
{
|
||||
GETLONG(enterprise, p);
|
||||
snprintf(buf, buf_len, "%u", enterprise);
|
||||
j = strlen(buf);
|
||||
i = 4;
|
||||
while (i + 2 <= opt_len)
|
||||
{
|
||||
int k, len;
|
||||
p = &val[i];
|
||||
GETSHORT(len, p);
|
||||
if (i + 2 + len > opt_len)
|
||||
break;
|
||||
if (j < buf_len - 1)
|
||||
buf[j++] = ',';
|
||||
for (k = 0; k < len && j < buf_len - 1; k++)
|
||||
{
|
||||
char c = *p++;
|
||||
if (isprint((unsigned char)c))
|
||||
buf[j++] = c;
|
||||
}
|
||||
buf[j] = 0;
|
||||
i += len + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||
{
|
||||
@@ -936,11 +967,11 @@ char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len,
|
||||
trunc = 1;
|
||||
opt_len = 14;
|
||||
}
|
||||
print_mac(buf, val, opt_len);
|
||||
|
||||
*buf = 0;
|
||||
strncat(buf, print_mac(val, opt_len), buf_len);
|
||||
if (trunc)
|
||||
strncat(buf, "...", buf_len - strlen(buf));
|
||||
|
||||
|
||||
}
|
||||
|
||||
return ot[o].name ? ot[o].name : "";
|
||||
|
||||
+8
-7
@@ -925,7 +925,8 @@ void dhcp_read_ethers(void)
|
||||
{
|
||||
FILE *f = fopen(ETHERSFILE, "r");
|
||||
unsigned int flags;
|
||||
char *buff = daemon->namebuff;
|
||||
size_t linesz = 0;
|
||||
char *line = NULL;
|
||||
char *ip, *cp;
|
||||
struct in_addr addr;
|
||||
unsigned char hwaddr[ETHER_ADDR_LEN];
|
||||
@@ -958,22 +959,22 @@ void dhcp_read_ethers(void)
|
||||
up = &config->next;
|
||||
}
|
||||
|
||||
while (fgets(buff, MAXDNAMESTR, f))
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
char *host = NULL;
|
||||
|
||||
lineno++;
|
||||
|
||||
while (strlen(buff) > 0 && isspace((unsigned char)buff[strlen(buff)-1]))
|
||||
buff[strlen(buff)-1] = 0;
|
||||
while (strlen(line) > 0 && isspace((unsigned char)line[strlen(line)-1]))
|
||||
line[strlen(line)-1] = 0;
|
||||
|
||||
if ((*buff == '#') || (*buff == '+') || (*buff == 0))
|
||||
if ((*line == '#') || (*line == '+') || (*line == 0))
|
||||
continue;
|
||||
|
||||
for (ip = buff; *ip && !isspace((unsigned char)*ip); ip++);
|
||||
for (ip = line; *ip && !isspace((unsigned char)*ip); ip++);
|
||||
for(; *ip && isspace((unsigned char)*ip); ip++)
|
||||
*ip = 0;
|
||||
if (!*ip || parse_hex(buff, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
|
||||
if (!*ip || parse_hex(line, hwaddr, ETHER_ADDR_LEN, NULL, NULL) != ETHER_ADDR_LEN)
|
||||
{
|
||||
my_syslog(MS_DHCP | LOG_ERR, _("bad line at %s line %d"), ETHERSFILE, lineno);
|
||||
continue;
|
||||
|
||||
+1
-1
@@ -720,7 +720,7 @@ static int construct_worker(struct in6_addr *local, int prefix,
|
||||
return 1;
|
||||
|
||||
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
|
||||
if (tmp->name && wildcard_match(tmp->name, ifrn_name))
|
||||
if (tmp->name && (tmp->flags & INAME_6) && wildcard_match(tmp->name, ifrn_name))
|
||||
return 1;
|
||||
|
||||
for (template = daemon->dhcp6; template; template = template->next)
|
||||
|
||||
+117
-52
@@ -140,6 +140,9 @@ int main (int argc, char **argv)
|
||||
/* one char flag per possible RR in answer section (may get extended). */
|
||||
daemon->rr_status_sz = 64;
|
||||
daemon->rr_status = safe_malloc(sizeof(*daemon->rr_status) * daemon->rr_status_sz);
|
||||
/* Increase TCP process slots by half, and reserve the extra for validation-to-tcp use only. */
|
||||
daemon->dnssec_tcp_slot_reserved = daemon->max_procs/2;
|
||||
daemon->max_procs += daemon->dnssec_tcp_slot_reserved;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -427,7 +430,8 @@ int main (int argc, char **argv)
|
||||
|
||||
daemon->tcp_pids = safe_malloc(daemon->max_procs*sizeof(pid_t));
|
||||
daemon->tcp_pipes = safe_malloc(daemon->max_procs*sizeof(int));
|
||||
|
||||
daemon->tcp_type = safe_malloc(daemon->max_procs);
|
||||
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
}
|
||||
@@ -1652,7 +1656,11 @@ static void async_event(int pipe, time_t now)
|
||||
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pipes[i] == -1)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
{
|
||||
if (daemon->tcp_type[i])
|
||||
daemon->dnssec_tcp_slot_reserved++;
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1888,7 +1896,7 @@ static void set_dns_listeners(void)
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
for (serverfdp = daemon->sfds; serverfdp; serverfdp = serverfdp->next)
|
||||
poll_listen(serverfdp->fd, POLLIN);
|
||||
@@ -1902,8 +1910,8 @@ static void set_dns_listeners(void)
|
||||
poll_listen(rfl->rfd->fd, POLLIN);
|
||||
|
||||
/* check to see if we have free tcp process slots. */
|
||||
for (i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
for (j = 0, i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1 && ++j > daemon->dnssec_tcp_slot_reserved)
|
||||
break;
|
||||
|
||||
for (listener = daemon->listeners; listener; listener = listener->next)
|
||||
@@ -1930,12 +1938,12 @@ static void check_dns_listeners(time_t now)
|
||||
struct serverfd *serverfdp;
|
||||
struct listener *listener;
|
||||
struct randfd_list *rfl;
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
/* Note that handling events here can create or destroy fds and
|
||||
render the result of the last poll() call invalid. Once
|
||||
we find an fd that needs service, do it, then return to go around the
|
||||
poll() loop again. This avoid really, really, wierd bugs. */
|
||||
poll() loop again. This avoid really, really, weird bugs. */
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
for (i = 0; i < daemon->max_procs; i++)
|
||||
@@ -1955,7 +1963,11 @@ static void check_dns_listeners(time_t now)
|
||||
daemon->tcp_pipes[i] = -1;
|
||||
/* tcp_pipes == -1 && tcp_pids == 0 required to free slot */
|
||||
if (daemon->tcp_pids[i] == 0)
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
{
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]--;
|
||||
if (daemon->tcp_type[i])
|
||||
daemon->dnssec_tcp_slot_reserved++;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1995,8 +2007,8 @@ static void check_dns_listeners(time_t now)
|
||||
at least one a poll() time, that we still do.
|
||||
There may be more waiting connections after
|
||||
poll() returns then free process slots. */
|
||||
for (i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1)
|
||||
for (j = 0,i = daemon->max_procs - 1; i >= 0; i--)
|
||||
if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1 && ++j > daemon->dnssec_tcp_slot_reserved)
|
||||
break;
|
||||
|
||||
if (i >= 0)
|
||||
@@ -2015,20 +2027,32 @@ 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);
|
||||
struct server *s;
|
||||
struct server *s;
|
||||
struct listener *l;
|
||||
int flags, auth_dns = 0;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
struct iovec tcpbuff;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
unsigned char a = 0;
|
||||
#endif
|
||||
netmask.s_addr = 0;
|
||||
|
||||
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
|
||||
|
||||
if (confd == -1)
|
||||
return;
|
||||
{
|
||||
/* Transient errors: just return and retry on next poll cycle. */
|
||||
if (errno == EAGAIN || errno == ECONNABORTED ||
|
||||
errno == EMFILE || errno == ENFILE ||
|
||||
errno == ENOMEM || errno == ENOBUFS)
|
||||
return;
|
||||
|
||||
/* Fatal error (EINVAL, EBADF, etc): socket is permanently broken.
|
||||
Close it so poll() no longer selects it. In --bind-dynamic mode
|
||||
the listener will be rebuilt on the next address change event. */
|
||||
close(listener->tcpfd);
|
||||
listener->tcpfd = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)
|
||||
{
|
||||
@@ -2138,27 +2162,22 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
/* fork() done: parent side */
|
||||
close(pipefd[1]); /* parent needs read pipe end. */
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
/* The child process inherits various sockets,
|
||||
whose existence can mess up the function of
|
||||
the parent. See below for details of which
|
||||
sockets, and why they cause problems. The
|
||||
child closes the sockets during startup,
|
||||
but there's a nasty race, since the parent
|
||||
may use them before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
To avoid the race, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
|
||||
is sent when the child has finished the close. */
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
|
||||
daemon->tcp_pids[slot] = p;
|
||||
daemon->tcp_pipes[slot] = pipefd[0];
|
||||
daemon->tcp_type[slot] = 0;
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
@@ -2176,18 +2195,43 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
close the netlink socket in the child. */
|
||||
close(daemon->netlinkfd);
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
close(daemon->routefd);
|
||||
#endif
|
||||
|
||||
/* Close inherited listening sockets in the child process.
|
||||
These are not needed here and holding them prevents the
|
||||
parent from re-binding if an interface is removed and
|
||||
re-added (the child's copy causes EADDRINUSE). */
|
||||
for (l = daemon->listeners; l; l = l->next)
|
||||
{
|
||||
if (l->fd != -1)
|
||||
close(l->fd);
|
||||
if (l->tcpfd != -1)
|
||||
close(l->tcpfd);
|
||||
if (l->tftpfd != -1)
|
||||
close(l->tftpfd);
|
||||
}
|
||||
|
||||
/* unblock parent now we're not holding sockets we shouldn't. */
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
|
||||
/* Arrange for SIGALRM after CHILD_LIFETIME seconds to
|
||||
terminate the process. */
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
close(daemon->netlinkfd);
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* The connected socket inherits non-blocking
|
||||
attribute from the listening socket.
|
||||
Reset that here. */
|
||||
@@ -2232,14 +2276,13 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
ssize_t *plen, char *name, int class, struct server *server, int *keycount, int *validatecount)
|
||||
{
|
||||
struct server *s;
|
||||
struct listener *l;
|
||||
|
||||
if (!option_bool(OPT_DEBUG))
|
||||
{
|
||||
pid_t p;
|
||||
int i, pipefd[2];
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
unsigned char a = 0;
|
||||
#endif
|
||||
|
||||
/* check to see if we have a free tcp process slot. */
|
||||
for (i = daemon->max_procs - 1; i >= 0; i--)
|
||||
@@ -2260,26 +2303,24 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
return STAT_ABANDONED;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent (us)
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
the child closes the netlink socket, but there's
|
||||
a nasty race, since the parent may use netlink
|
||||
before the child has done the close.
|
||||
/* The child process inherits various sockets,
|
||||
whose existence can mess up the function of
|
||||
the parent. See below for details of which
|
||||
sockets, and why they cause problems. The
|
||||
child closes the sockets during startup,
|
||||
but there's a nasty race, since the parent
|
||||
may use them before the child has done the close.
|
||||
|
||||
To avoid this, the parent blocks here until a
|
||||
To avoid the race, the parent blocks here until a
|
||||
single byte comes back up the pipe, which
|
||||
is sent by the child after it has closed the
|
||||
netlink socket. */
|
||||
is sent when the child has finished the close. */
|
||||
read_write(pipefd[0], &a, 1, RW_READ);
|
||||
#endif
|
||||
|
||||
/* i holds index of free slot */
|
||||
daemon->tcp_pids[i] = p;
|
||||
daemon->tcp_pipes[i] = pipefd[0];
|
||||
daemon->tcp_type[i] = 1;
|
||||
daemon->dnssec_tcp_slot_reserved--;
|
||||
daemon->metrics[METRIC_TCP_CONNECTIONS]++;
|
||||
if (daemon->metrics[METRIC_TCP_CONNECTIONS] > daemon->max_procs_used)
|
||||
daemon->max_procs_used = daemon->metrics[METRIC_TCP_CONNECTIONS];
|
||||
@@ -2294,10 +2335,34 @@ int swap_to_tcp(struct frec *forward, time_t now, int status, struct dns_header
|
||||
{
|
||||
/* child starts here. */
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
/* See comment above re: netlink socket. */
|
||||
/* The child process inherits the netlink socket,
|
||||
which it never uses, but when the parent
|
||||
uses it in the future, the answer may go to the
|
||||
child, resulting in the parent blocking
|
||||
forever awaiting the result. To avoid this
|
||||
close the netlink socket in the child. */
|
||||
close(daemon->netlinkfd);
|
||||
#elif defined(HAVE_BSD_NETWORK)
|
||||
close(daemon->routefd);
|
||||
#endif
|
||||
|
||||
/* Close inherited listening sockets in the child process.
|
||||
These are not needed here and holding them prevents the
|
||||
parent from re-binding if an interface is removed and
|
||||
re-added (the child's copy causes EADDRINUSE). */
|
||||
for (l = daemon->listeners; l; l = l->next)
|
||||
{
|
||||
if (l->fd != -1)
|
||||
close(l->fd);
|
||||
if (l->tcpfd != -1)
|
||||
close(l->tcpfd);
|
||||
if (l->tftpfd != -1)
|
||||
close(l->tftpfd);
|
||||
}
|
||||
|
||||
/* unblock parent now we're not holding sockets we shouldn't. */
|
||||
read_write(pipefd[1], &a, 1, RW_WRITE);
|
||||
#endif
|
||||
|
||||
alarm(CHILD_LIFETIME);
|
||||
close(pipefd[0]); /* close read end in child. */
|
||||
daemon->pipe_to_parent = pipefd[1];
|
||||
|
||||
+12
-9
@@ -723,7 +723,7 @@ struct resolvc {
|
||||
#endif
|
||||
};
|
||||
|
||||
/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
|
||||
/* addn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile and dhcp-hostsdir*/
|
||||
#define AH_DIR 1
|
||||
#define AH_INACTIVE 2
|
||||
#define AH_WD_DONE 4
|
||||
@@ -834,6 +834,7 @@ struct frec {
|
||||
#define OT_CSTRING 0x0800
|
||||
#define OT_DEC 0x0400
|
||||
#define OT_TIME 0x0200
|
||||
#define OT_DHCP6_VENDOR 0x0100
|
||||
|
||||
/* actions in the daemon->helper RPC */
|
||||
#define ACTION_DEL 1
|
||||
@@ -1170,7 +1171,7 @@ struct dhcp_relay {
|
||||
};
|
||||
|
||||
extern struct daemon {
|
||||
/* datastuctures representing the command-line and
|
||||
/* datastructures representing the command-line and
|
||||
config file arguments. All set (including defaults)
|
||||
in option.c */
|
||||
|
||||
@@ -1295,6 +1296,7 @@ extern struct daemon {
|
||||
int fd_save; /* " " */
|
||||
pid_t *tcp_pids;
|
||||
int *tcp_pipes;
|
||||
char *tcp_type;
|
||||
int pipe_to_parent;
|
||||
int numrrand;
|
||||
struct randfd *randomsocks;
|
||||
@@ -1354,7 +1356,7 @@ extern struct daemon {
|
||||
/* file for packet dumps. */
|
||||
int dumpfd;
|
||||
#endif
|
||||
int max_procs;
|
||||
int max_procs, dnssec_tcp_slot_reserved;
|
||||
uint max_procs_used;
|
||||
} *daemon;
|
||||
|
||||
@@ -1468,7 +1470,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
|
||||
|
||||
/* dnssec.c */
|
||||
#ifdef HAVE_DNSSEC
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int id, int type);
|
||||
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name, int class, int id, int type);
|
||||
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
char *keyname, int class, int *validate_count);
|
||||
int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name,
|
||||
@@ -1516,6 +1518,7 @@ void *whine_malloc_real(const char *func, unsigned int line, size_t size);
|
||||
void *whine_realloc_real(const char *wrapper, const char *func, unsigned int line, void *ptr, size_t size);
|
||||
int expand_buf_real(const char *func, unsigned int line, struct iovec *iov, size_t size);
|
||||
int expand_workspace_real(const char *func, unsigned int line, unsigned char ***wkspc, int *szp, int new);
|
||||
int get_line_alloc(FILE *f, char **buffp, size_t *sizep);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int sockaddr_isnull(const union mysockaddr *s);
|
||||
@@ -1537,7 +1540,7 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||
unsigned int *wildcard_mask, int *mac_type);
|
||||
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
||||
unsigned int mask);
|
||||
char *print_mac(char *buff, unsigned char *mac, int len);
|
||||
char *print_mac(unsigned char *mac, int len);
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw);
|
||||
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw);
|
||||
void close_fds(long max_fd, int spare1, int spare2, int spare3);
|
||||
@@ -1938,11 +1941,11 @@ void from_wire(char *name);
|
||||
/* edns0.c */
|
||||
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
|
||||
size_t *len, unsigned char **p, int *is_sign, int *is_last);
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen);
|
||||
void edns0_needs_mac(union mysockaddr *addr, time_t now);
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, time_t now, int *cacheable);
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
|
||||
|
||||
@@ -1965,7 +1968,7 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
|
||||
int filter_servers(int seed, int flags, int *lowout, int *highout);
|
||||
int is_local_answer(time_t now, int first, char *name);
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
|
||||
char *name, char *limit, int first, int last, int ede);
|
||||
char *name, size_t limit, int first, int last, int ede);
|
||||
int server_samegroup(struct server *a, struct server *b);
|
||||
#ifdef HAVE_DNSSEC
|
||||
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp);
|
||||
|
||||
+43
-29
@@ -177,7 +177,7 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
if ((state->c = state->end - state->ip) != 0)
|
||||
{
|
||||
state->op = state->ip;
|
||||
state->ip = state->end;;
|
||||
state->ip = state->end;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
@@ -189,24 +189,28 @@ static int get_rdata(struct dns_header *header, size_t plen, struct rdata_state
|
||||
if (d == (u16)0)
|
||||
{
|
||||
/* domain-name, canonicalise */
|
||||
int len;
|
||||
|
||||
/* If the name is malformed, or runs beyond the end of the RR, abort.
|
||||
under no circumstances should state->ip be beyond state->end since
|
||||
that will cause the run-out code above to turn the negative
|
||||
calculted length into a very large postive value for state->c
|
||||
and overrun the buffer. */
|
||||
if (!extract_name(header, plen, &state->ip, state->buff, EXTR_NAME_EXTRACT, 0) ||
|
||||
(len = to_wire(state->buff)) == 0)
|
||||
continue;
|
||||
|
||||
state->c = len;
|
||||
state->ip > state->end)
|
||||
return 0;
|
||||
|
||||
/* to_wire() always returns length > 0 */
|
||||
state->c = to_wire(state->buff);
|
||||
state->op = (unsigned char *)state->buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* plain data preceding a domain-name, don't run off the end of the data */
|
||||
if ((state->end - state->ip) < d)
|
||||
d = state->end - state->ip;
|
||||
|
||||
if (d == 0)
|
||||
continue;
|
||||
|
||||
/* Plain data preceding a domain-name.
|
||||
Handle short RR, if there are no bytes left, return finished. */
|
||||
if ((state->end - state->ip) < d &&
|
||||
(d = state->end - state->ip) == 0)
|
||||
return 0;
|
||||
|
||||
state->op = state->ip;
|
||||
state->c = d;
|
||||
state->ip += d;
|
||||
@@ -383,7 +387,7 @@ static int explore_rrset(struct dns_header *header, size_t plen, int class, int
|
||||
an attacker using signatures made with the key of an unrelated
|
||||
zone he controls. Note that the root key is always allowed.
|
||||
Ignore sigs which aren't valid */
|
||||
if (*daemon->workspacename == 0 || hostname_issubdomain(name, daemon->workspacename) != 0)
|
||||
if (*daemon->workspacename == 0 || hostname_issubdomain(daemon->workspacename, name) != 0)
|
||||
{
|
||||
if (gotkey)
|
||||
{
|
||||
@@ -482,7 +486,6 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
int i, wire_len, sig_len;
|
||||
const struct nettle_hash *hash;
|
||||
void *ctx;
|
||||
char *name_start;
|
||||
u32 nsigttl, ttl, orig_ttl;
|
||||
|
||||
failflags &= ~DNSSEC_FAIL_NOSIG;
|
||||
@@ -572,13 +575,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
if (!extract_name(header, plen, &p, name, EXTR_NAME_EXTRACT, 10))
|
||||
return STAT_BOGUS;
|
||||
|
||||
name_start = name;
|
||||
|
||||
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
|
||||
/* If the name is already the wildcard, we're not going to change it. */
|
||||
if (labels < name_labels &&
|
||||
!(name_labels - labels == 1 && name_start[0] == '*' && name_start[1] == '.'))
|
||||
!(name_labels - labels == 1 && name[0] == '*' && name[1] == '.'))
|
||||
{
|
||||
char *name_start = name;
|
||||
|
||||
for (j = name_labels - labels; j != 0; j--)
|
||||
{
|
||||
while (*name_start != '.' && *name_start != 0)
|
||||
@@ -591,11 +594,13 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
|
||||
*wildcard_offset_out = name_start - name + 1;
|
||||
|
||||
name_start--;
|
||||
*name_start = '*';
|
||||
*name_start = '*';
|
||||
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
|
||||
memmove(name, name_start, strlen(name_start) + 1);
|
||||
}
|
||||
|
||||
wire_len = to_wire(name_start);
|
||||
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
|
||||
|
||||
wire_len = to_wire(name);
|
||||
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
|
||||
hash->update(ctx, 4, p); /* class and type */
|
||||
hash->update(ctx, 4, (unsigned char *)&nsigttl);
|
||||
|
||||
@@ -1018,6 +1023,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
|
||||
|
||||
if (!servfail)
|
||||
{
|
||||
/* If the answer carries no proof of non-existence, we fall back to asking if the
|
||||
zone is unsigned, but that question is answered by the very DS record we're
|
||||
chasing here, so we'd loop getting nowhere. Treat the zone as unsigned and let
|
||||
the checks below decide if that's acceptable. */
|
||||
if (STAT_ISEQUAL(rc, STAT_NEED_DS) && hostname_isequal(name, keyname))
|
||||
rc = STAT_INSECURE;
|
||||
|
||||
if (STAT_ISEQUAL(rc, STAT_INSECURE))
|
||||
{
|
||||
/* A INSECURE DS answer is OK if it's negative and there's a CNAME answer to the DS answer which is
|
||||
@@ -1632,7 +1644,7 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
if (*closest_encloser == '.')
|
||||
closest_encloser++;
|
||||
|
||||
if (wild_offset != 0 && name - closest_encloser == wild_offset)
|
||||
if (wild_offset != 0 && closest_encloser - name == wild_offset)
|
||||
break;
|
||||
|
||||
if (dec_counter(validate_counter, NULL))
|
||||
@@ -1683,11 +1695,13 @@ static int prove_non_existence_nsec3(struct dns_header *header, size_t plen, uns
|
||||
|
||||
wildcard--;
|
||||
*wildcard = '*';
|
||||
/* move our name to the start of the buffer to avoid pathalogical cases over-running the end in to_wire(). */
|
||||
memmove(name, wildcard, strlen(wildcard) + 1);
|
||||
|
||||
if (dec_counter(validate_counter, NULL))
|
||||
return DNSSEC_FAIL_WORK;
|
||||
|
||||
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
|
||||
if ((digest_len = hash_name(name, &digest, hash, salt, salt_len, iterations)) == 0)
|
||||
return DNSSEC_FAIL_NONSEC;
|
||||
|
||||
if (!check_nsec3_coverage(header, plen, digest_len, digest, type, workspace1, workspace2, nsecs, nsec_count, NULL, 1))
|
||||
@@ -1923,7 +1937,7 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
|
||||
return STAT_INSECURE;
|
||||
}
|
||||
|
||||
if (name_start == 0)
|
||||
if (name_start <= 1)
|
||||
break;
|
||||
|
||||
for (p = &name[name_start-2]; (*p != '.') && (p != name); p--);
|
||||
@@ -2095,8 +2109,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
|
||||
CNAME must be <subdomain>.<dname>
|
||||
CNAME target must be <subdomain>.<dname_target>
|
||||
<subdomain>s must match for name and target. */
|
||||
if (hostname_issubdomain(daemon->cname, name) == 1 &&
|
||||
hostname_issubdomain(daemon->workspacename, keyname) == 1 &&
|
||||
if (hostname_issubdomain(name, daemon->cname) == 1 &&
|
||||
hostname_issubdomain(keyname, daemon->workspacename) == 1 &&
|
||||
name_prefix_len == strlen(daemon->workspacename) - strlen(keyname))
|
||||
{
|
||||
char save = daemon->cname[name_prefix_len];
|
||||
@@ -2332,7 +2346,7 @@ int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
|
||||
}
|
||||
}
|
||||
|
||||
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name,
|
||||
size_t dnssec_generate_query(struct dns_header *header, size_t outlen, char *name,
|
||||
int class, int id, int type)
|
||||
{
|
||||
unsigned char *p;
|
||||
@@ -2356,7 +2370,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
|
||||
PUTSHORT(type, p);
|
||||
PUTSHORT(class, p);
|
||||
|
||||
return add_do_bit(header, p - (unsigned char *)header, end);
|
||||
return add_do_bit(header, p - (unsigned char *)header, outlen);
|
||||
}
|
||||
|
||||
int errflags_to_ede(int status)
|
||||
|
||||
+4
-3
@@ -408,12 +408,13 @@ int is_local_answer(time_t now, int first, char *name)
|
||||
return rc;
|
||||
}
|
||||
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
|
||||
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, size_t limit, int first, int last, int ede)
|
||||
{
|
||||
int trunc = 0, anscount = 0;
|
||||
unsigned char *p;
|
||||
int start;
|
||||
union all_addr addr;
|
||||
char *out_end = ((char *)header) + limit;
|
||||
|
||||
setup_reply(header, flags, ede);
|
||||
|
||||
@@ -443,7 +444,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
else
|
||||
addr.addr4 = srv->addr;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
|
||||
if (add_resource_record(header, out_end, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr))
|
||||
anscount++;
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0);
|
||||
}
|
||||
@@ -458,7 +459,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
|
||||
else
|
||||
addr.addr6 = srv->addr;
|
||||
|
||||
if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
|
||||
if (add_resource_record(header, out_end, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr))
|
||||
anscount++;
|
||||
log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0);
|
||||
}
|
||||
|
||||
+26
-24
@@ -129,6 +129,23 @@ void dump_packet_icmp(int mask, void *packet, size_t len,
|
||||
do_dump_packet(mask, packet, len, src, dst, -1, IPPROTO_ICMP);
|
||||
}
|
||||
|
||||
static u32 calc_data_checksum(u32 sum, void *packet, size_t len)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < len/2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
if (len & 1) /* last byte, in case length is odd. */
|
||||
sum += ((unsigned char *)packet)[len - 1];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
if (sum != 0xffff)
|
||||
sum = ~sum;
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
union mysockaddr *src, union mysockaddr *dst, int port, int proto)
|
||||
{
|
||||
@@ -226,13 +243,9 @@ static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
udp.uh_dport = dst->in.sin_port;
|
||||
}
|
||||
|
||||
ip.ip_sum = 0;
|
||||
for (sum = 0, i = 0; i < sizeof(struct ip) / 2; i++)
|
||||
sum += ((u16 *)&ip)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
ip.ip_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
ip.ip_sum = 0; /* for the calculation */
|
||||
ip.ip_sum = calc_data_checksum(0, &ip, sizeof(struct ip));
|
||||
|
||||
/* start UDP/ICMP checksum */
|
||||
sum = ip.ip_src.s_addr & 0xffff;
|
||||
sum += (ip.ip_src.s_addr >> 16) & 0xffff;
|
||||
@@ -240,9 +253,6 @@ static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
sum += (ip.ip_dst.s_addr >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
if (len & 1)
|
||||
((unsigned char *)packet)[len] = 0; /* for checksum, in case length is odd. */
|
||||
|
||||
if (proto == IPPROTO_UDP)
|
||||
{
|
||||
/* Add Remaining part of the pseudoheader. Note that though the
|
||||
@@ -252,17 +262,12 @@ static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
sum += htons(IPPROTO_UDP);
|
||||
sum += htons(sizeof(struct udphdr) + len);
|
||||
|
||||
udp.uh_sum = 0;
|
||||
udp.uh_sum = 0; /* for the calculation */
|
||||
udp.uh_ulen = htons(sizeof(struct udphdr) + len);
|
||||
|
||||
for (i = 0; i < sizeof(struct udphdr)/2; i++)
|
||||
sum += ((u16 *)&udp)[i];
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
udp.uh_sum = (sum == 0xffff) ? sum : ~sum;
|
||||
|
||||
udp.uh_sum = calc_data_checksum(sum, packet, len);
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + sizeof(udp) + len;
|
||||
}
|
||||
else
|
||||
@@ -274,12 +279,9 @@ static void do_dump_packet(int mask, void *packet, size_t len,
|
||||
sum += htons(proto);
|
||||
sum += htons(len);
|
||||
|
||||
icmp->icmp6_cksum = 0;
|
||||
for (i = 0; i < (len + 1) / 2; i++)
|
||||
sum += ((u16 *)packet)[i];
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
icmp->icmp6_cksum = (sum == 0xffff) ? sum : ~sum;
|
||||
icmp->icmp6_cksum = 0; /* for the calculation */
|
||||
icmp->icmp6_cksum = calc_data_checksum(sum, packet, len);
|
||||
|
||||
|
||||
pcap_header.incl_len = pcap_header.orig_len = ipsz + len;
|
||||
}
|
||||
|
||||
+25
-22
@@ -100,13 +100,14 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t
|
||||
replace == 1 ->replace existing or add option
|
||||
replace == 2 ->relpace existing option only.
|
||||
*/
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_pseudoheader(struct dns_header *header, size_t plen, size_t out_size,
|
||||
int optno, unsigned char *opt, size_t optlen, int set_do, int replace)
|
||||
{
|
||||
unsigned char *lenp = NULL, *datap = NULL, *p, *udp_len, *buff = NULL;
|
||||
int rdlen = 0, is_sign, is_last;
|
||||
unsigned short flags = set_do ? 0x8000 : 0, rcode = 0;
|
||||
|
||||
unsigned char *limit = ((unsigned char *)header) + out_size;
|
||||
|
||||
p = find_pseudoheader(header, plen, NULL, &udp_len, &is_sign, &is_last);
|
||||
|
||||
if (is_sign)
|
||||
@@ -247,9 +248,9 @@ size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *l
|
||||
return p - (unsigned char *)header;
|
||||
}
|
||||
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit)
|
||||
size_t add_do_bit(struct dns_header *header, size_t plen, size_t outlen)
|
||||
{
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, 0, NULL, 0, 1, 0);
|
||||
return add_pseudoheader(header, plen, outlen, 0, NULL, 0, 1, 0);
|
||||
}
|
||||
|
||||
static unsigned char char64(unsigned char c)
|
||||
@@ -276,14 +277,16 @@ void edns0_needs_mac(union mysockaddr *addr, time_t now)
|
||||
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
OPT_STRIP_MAC = MAC is removed
|
||||
Handle only six byte MACs. */
|
||||
static size_t add_dns_client(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int replace = 0, maclen = 0;
|
||||
unsigned char mac[DHCP_CHADDR_MAX];
|
||||
char encode[18]; /* handle 6 byte MACs ONLY */
|
||||
|
||||
char encode[9] = { 0 };
|
||||
char *encoded = encode;
|
||||
|
||||
if ((option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX)) && (maclen = find_mac(l3, mac, 1, now)) == 6)
|
||||
{
|
||||
if (option_bool(OPT_STRIP_MAC))
|
||||
@@ -291,7 +294,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
*cacheablep = 0;
|
||||
|
||||
if (option_bool(OPT_MAC_HEX))
|
||||
print_mac(encode, mac, maclen);
|
||||
encoded = print_mac(mac, maclen);
|
||||
else
|
||||
{
|
||||
encoder(mac, encode);
|
||||
@@ -303,7 +306,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
replace = 2;
|
||||
|
||||
if (replace != 0 || maclen == 6)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encode, strlen(encode), 0, replace);
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMDEVICEID, (unsigned char *)encoded, strlen(encoded), 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
@@ -312,7 +315,7 @@ static size_t add_dns_client(struct dns_header *header, size_t plen, unsigned ch
|
||||
/* OPT_ADD_MAC = MAC is added (if available)
|
||||
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
|
||||
OPT_STRIP_MAC = MAC is removed */
|
||||
static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
static size_t add_mac(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *l3, time_t now, int *cacheablep)
|
||||
{
|
||||
int maclen = 0, replace = 0;
|
||||
@@ -328,7 +331,7 @@ static size_t add_mac(struct dns_header *header, size_t plen, unsigned char *lim
|
||||
replace = 2;
|
||||
|
||||
if (replace != 0 || maclen != 0)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_MAC, mac, maclen, 0, replace);
|
||||
|
||||
return plen;
|
||||
}
|
||||
@@ -409,7 +412,7 @@ static size_t calc_subnet_opt(struct subnet_opt *opt, union mysockaddr *source,
|
||||
/* OPT_CLIENT_SUBNET = client subnet is added
|
||||
OPT_CLIENT_SUBNET + OPT_STRIP_ECS = client subnet is replaced
|
||||
OPT_STRIP_ECS = client subnet is removed */
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
static size_t add_source_addr(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
/* http://tools.ietf.org/html/draft-vandergaast-edns-client-subnet-02 */
|
||||
@@ -439,7 +442,7 @@ static size_t add_source_addr(struct dns_header *header, size_t plen, unsigned c
|
||||
return plen;
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_CLIENT_SUBNET, (unsigned char *)&opt, len, 0, replace);
|
||||
}
|
||||
|
||||
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer)
|
||||
@@ -516,7 +519,7 @@ struct umbrella_opt {
|
||||
u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
|
||||
};
|
||||
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
|
||||
static size_t add_umbrella_opt(struct dns_header *header, size_t plen, size_t outlen, union mysockaddr *source, int *cacheable)
|
||||
{
|
||||
*cacheable = 0;
|
||||
|
||||
@@ -548,28 +551,28 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned
|
||||
PUTLONG(daemon->umbrella_asset, u);
|
||||
}
|
||||
|
||||
return add_pseudoheader(header, plen, (unsigned char *)limit, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
|
||||
return add_pseudoheader(header, plen, outlen, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1);
|
||||
}
|
||||
|
||||
/* Set *check_subnet if we add a client subnet option, which needs to checked
|
||||
in the reply. Set *cacheable to zero if we add an option which the answer
|
||||
may depend on. */
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
|
||||
size_t add_edns0_config(struct dns_header *header, size_t plen, size_t outlen,
|
||||
union mysockaddr *source, time_t now, int *cacheable)
|
||||
{
|
||||
*cacheable = 1;
|
||||
|
||||
plen = add_mac(header, plen, limit, source, now, cacheable);
|
||||
plen = add_dns_client(header, plen, limit, source, now, cacheable);
|
||||
plen = add_mac(header, plen, outlen, source, now, cacheable);
|
||||
plen = add_dns_client(header, plen, outlen, source, now, cacheable);
|
||||
|
||||
if (daemon->dns_client_id)
|
||||
plen = add_pseudoheader(header, plen, limit, EDNS0_OPTION_NOMCPEID,
|
||||
plen = add_pseudoheader(header, plen, outlen, EDNS0_OPTION_NOMCPEID,
|
||||
(unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
|
||||
|
||||
if (option_bool(OPT_UMBRELLA))
|
||||
plen = add_umbrella_opt(header, plen, limit, source, cacheable);
|
||||
plen = add_umbrella_opt(header, plen, outlen, source, cacheable);
|
||||
|
||||
plen = add_source_addr(header, plen, limit, source, cacheable);
|
||||
plen = add_source_addr(header, plen, outlen, source, cacheable);
|
||||
|
||||
return plen;
|
||||
}
|
||||
|
||||
+90
-56
@@ -177,7 +177,10 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
int forwarded = 0;
|
||||
int ede = EDE_UNSET;
|
||||
unsigned short rrtype, rrclass;
|
||||
|
||||
unsigned short id = ntohs(header->id); /* Retrieve the id from the new query before we overwrite it. */
|
||||
unsigned int casediff = 0;
|
||||
unsigned int *bitvector = NULL;
|
||||
|
||||
gotname = extract_request(header, plen, daemon->namebuff, &rrtype, &rrclass);
|
||||
|
||||
/* Check for retry on existing query.
|
||||
@@ -197,16 +200,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
|
||||
{
|
||||
struct frec_src *src;
|
||||
unsigned int casediff = 0;
|
||||
unsigned int *bitvector = NULL;
|
||||
unsigned short id = ntohs(header->id); /* Retrieve the id from the new query before we overwrite it. */
|
||||
|
||||
/* Get the case-scambled version of the query to resend. This is important because we
|
||||
may fall through below and forward the query in the packet buffer again and we
|
||||
want to use the same case scrambling as the first time. */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
plen = forward->stash_len;
|
||||
|
||||
|
||||
for (src = &forward->frec_src; src; src = src->next)
|
||||
if (src->orig_id == id &&
|
||||
sockaddr_isequal(&src->source, udpaddr))
|
||||
@@ -217,6 +211,11 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
old_src = 1;
|
||||
/* If a query is retried, use the log_id for the retry when logging the answer. */
|
||||
src->log_id = daemon->log_id;
|
||||
/* Get the case-scambled version of the query to resend. This is important because we
|
||||
may fall through below and forward the query in the packet buffer again and we
|
||||
want to use the same case scrambling as the first time. */
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
plen = forward->stash_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -242,9 +241,11 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
and never resetting until the frec gets deleted by
|
||||
aging followed by the receipt of a different query. This
|
||||
is a bit of a DoS vuln. Avoid by explicitly deleting the
|
||||
frec once it expires. */
|
||||
if (difftime(now, forward->time) >= TIMEOUT)
|
||||
free_frec(forward);
|
||||
frec once it expires. The deletion is done at the end of
|
||||
this function, unless we set forward to NULL here. */
|
||||
if (difftime(now, forward->time) < TIMEOUT)
|
||||
forward = NULL;
|
||||
|
||||
goto reply;
|
||||
}
|
||||
|
||||
@@ -263,7 +264,10 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
|
||||
The original query we sent is now in packet buffer and the query name in the
|
||||
new instance is on daemon->namebuff. */
|
||||
|
||||
|
||||
blockdata_retrieve(forward->stash, forward->stash_len, (void *)header);
|
||||
plen = forward->stash_len;
|
||||
|
||||
if (extract_name(header, forward->stash_len, NULL, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
|
||||
{
|
||||
unsigned int i, gobig = 0;
|
||||
@@ -312,7 +316,6 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
src->fd = udpfd;
|
||||
src->encode_bitmap = casediff;
|
||||
src->encode_bigmap = bitvector;
|
||||
|
||||
src->udp_pkt_size = (unsigned short)replylimit;
|
||||
|
||||
/* closely spaced identical queries cannot be a try and a retry, so
|
||||
@@ -375,7 +378,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
plen = add_do_bit(header, plen, ((unsigned char *) header) + daemon->edns_pktsz);
|
||||
plen = add_do_bit(header, plen, daemon->edns_pktsz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
@@ -388,18 +391,15 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forward->frec_src.orig_id = ntohs(header->id);
|
||||
forward->new_id = get_id();
|
||||
header->id = ntohs(forward->new_id);
|
||||
forward->frec_src.encode_bitmap = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0;
|
||||
forward->frec_src.encode_bitmap = casediff = (!option_bool(OPT_NO_0x20) && option_bool(OPT_DO_0x20)) ? rand32() : 0;
|
||||
forward->frec_src.encode_bigmap = NULL;
|
||||
|
||||
if (!extract_name(header, plen, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
|
||||
|
||||
if (casediff != 0 && !extract_name(header, plen, NULL, (char *)&casediff, EXTR_NAME_FLIP, 1))
|
||||
goto reply;
|
||||
|
||||
/* Keep copy of query for retries and move to TCP */
|
||||
if (!(forward->stash = blockdata_alloc((char *)header, plen)))
|
||||
{
|
||||
free_frec(forward);
|
||||
goto reply; /* no mem. return REFUSED */
|
||||
}
|
||||
goto reply; /* no mem. return REFUSED */
|
||||
|
||||
forward->stash_len = plen;
|
||||
forward->frec_src.log_id = daemon->log_id;
|
||||
@@ -514,7 +514,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
forwarded = 0;
|
||||
|
||||
/* Advertise the size of UDP reply we can accept. */
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + daemon->edns_pktsz), 0, NULL, 0, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, daemon->edns_pktsz, 0, NULL, 0, 0, 0);
|
||||
|
||||
/* check for send errors here (no route to host)
|
||||
if we fail to send to all nameservers, send back an error
|
||||
@@ -582,14 +582,26 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
|
||||
/* could not send on, prepare to return */
|
||||
header->id = htons(forward->frec_src.orig_id);
|
||||
free_frec(forward); /* cancel */
|
||||
ede = EDE_NETERR;
|
||||
|
||||
reply:
|
||||
if (udpfd != -1)
|
||||
{
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, (char *)(header + replylimit), first, last, ede)))
|
||||
/* The query now in the buffer can have changed from what arrived during
|
||||
the preparation for forwarding in two respects.
|
||||
1) The header->id field may have changed.
|
||||
2) The case of letters in the query may have been changed.
|
||||
|
||||
Restore both of these changes before using it to build an error return for the client.
|
||||
The original id is held in local variable id, and the difference in case
|
||||
is encoded in casediff and also bitvector if the difference spreads beyond the first
|
||||
32 characters.
|
||||
*/
|
||||
header->id = htons(id);
|
||||
if (casediff != 0)
|
||||
extract_name(header, plen, NULL, (char *)(bitvector ? bitvector : &casediff), EXTR_NAME_FLIP, bitvector ? casediff : 1);
|
||||
|
||||
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, replylimit, first, last, ede)))
|
||||
return;
|
||||
|
||||
if (fwd_flags & FREC_HAS_PHEADER)
|
||||
@@ -597,9 +609,9 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, replylimit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, 0, 0);
|
||||
else
|
||||
plen = add_pseudoheader(header, plen, (unsigned char *)(header + replylimit), 0, NULL, 0, 0, 0);
|
||||
plen = add_pseudoheader(header, plen, replylimit, 0, NULL, 0, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
@@ -619,6 +631,11 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
|
||||
/* This has to be after last use of bitvector. */
|
||||
if (forward)
|
||||
free_frec(forward);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -695,7 +712,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
|
||||
|
||||
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,
|
||||
union mysockaddr *query_source, unsigned char *limit, int ede)
|
||||
union mysockaddr *query_source, size_t outlen, int ede)
|
||||
{
|
||||
unsigned char *pheader, *sizep;
|
||||
struct ipsets *ipsets = NULL, *nftsets = NULL;
|
||||
@@ -879,7 +896,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
|
||||
if (pheader && ede != EDE_UNSET)
|
||||
{
|
||||
u16 swap = htons((u16)ede);
|
||||
n = add_pseudoheader(header, n, limit, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
|
||||
n = add_pseudoheader(header, n, outlen, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
|
||||
}
|
||||
|
||||
if (RCODE(header) == NXDOMAIN)
|
||||
@@ -1027,7 +1044,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
allocation of a new one: third arg of get_new_frec() does that. */
|
||||
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
|
||||
(server = daemon->serverarray[serverind]) &&
|
||||
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
(nn = dnssec_generate_query(header, daemon->edns_pktsz,
|
||||
daemon->keyname, forward->class, get_id(),
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS)) &&
|
||||
(fd = allocate_rfd(&rfds, server)) != -1 &&
|
||||
@@ -1267,7 +1284,7 @@ void reply_query(int fd, time_t now)
|
||||
had replies from all to avoid filling the forwarding table when
|
||||
everything is broken */
|
||||
|
||||
/* decrement count of replies recieved if we sent to more than one server. */
|
||||
/* decrement count of replies received if we sent to more than one server. */
|
||||
if (forward->forwardall && (--forward->forwardall > 1) && RCODE(header) == REFUSED)
|
||||
return;
|
||||
|
||||
@@ -1289,7 +1306,8 @@ void reply_query(int fd, time_t now)
|
||||
server->query_latency = server->mma_latency/128;
|
||||
|
||||
/* Flip the bits back in the query name. */
|
||||
if (!extract_name(header, n, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
|
||||
if (forward->frec_src.encode_bitmap != 0 &&
|
||||
!extract_name(header, n, NULL, (char *)&forward->frec_src.encode_bitmap, EXTR_NAME_FLIP, 1))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
@@ -1442,7 +1460,7 @@ void return_reply(time_t now, struct frec *forward, struct dns_header *header, s
|
||||
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
|
||||
!(forward->flags & FREC_HAS_PHEADER), &forward->frec_src.source,
|
||||
((unsigned char *)header) + daemon->edns_pktsz, ede)))
|
||||
daemon->edns_pktsz, ede)))
|
||||
{
|
||||
struct frec_src *src, *prev;
|
||||
int do_trunc;
|
||||
@@ -1945,7 +1963,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
int cacheable;
|
||||
|
||||
n = add_edns0_config(header, n, ((unsigned char *)header) + daemon->edns_pktsz, &source_addr, now, &cacheable);
|
||||
n = add_edns0_config(header, n, daemon->edns_pktsz, &source_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *) header, (size_t)n);
|
||||
|
||||
if (!cacheable)
|
||||
@@ -1979,12 +1997,12 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
u16 swap = htons(ede);
|
||||
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(header, m, daemon->edns_pktsz, EDNS0_OPTION_EDE,
|
||||
(unsigned char *)&swap, 2, do_bit, 0);
|
||||
}
|
||||
else
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + daemon->edns_pktsz,
|
||||
0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(header, m, daemon->edns_pktsz, 0,
|
||||
NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_DUMPFILE
|
||||
@@ -2105,7 +2123,7 @@ static ssize_t tcp_talk(int first, int last, int start, struct dns_header *heade
|
||||
#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO)
|
||||
/* TCP connections by default take ages to time out.
|
||||
Set shorter timeouts more appropriate for a DNS server.
|
||||
We set the recieve timeout as twice the send timeout; we
|
||||
We set the receive timeout as twice the send timeout; we
|
||||
want to fail quickly on a non-responsive server, but give it time to get an
|
||||
answer. */
|
||||
tv.tv_sec = TCP_TIMEOUT;
|
||||
@@ -2326,7 +2344,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
query_header = (struct dns_header *)daemon->packet;
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
m = dnssec_generate_query(query_header, ((unsigned char *)query_header) + daemon->edns_pktsz, keyname, class, 0,
|
||||
m = dnssec_generate_query(query_header, daemon->edns_pktsz, keyname, class, 0,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 ||
|
||||
@@ -2388,7 +2406,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
unsigned char *pheader;
|
||||
unsigned int mark = 0;
|
||||
int have_mark = 0;
|
||||
int first, last, filtered, do_stale = 0;
|
||||
int first, last, filtered, do_stale = 0, closed = 0;
|
||||
struct iovec out_iov[2];
|
||||
|
||||
bigbuff->iov_base = NULL;
|
||||
@@ -2479,7 +2497,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
out_header = bigbuff->iov_base;
|
||||
|
||||
/* Add edns0 pheader to query */
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + daemon->packet_buff_sz, &peer_addr, now, &cacheable);
|
||||
size = add_edns0_config(header, size, daemon->packet_buff_sz, &peer_addr, now, &cacheable);
|
||||
|
||||
/* Clear buffer to avoid risk of information disclosure. */
|
||||
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
|
||||
@@ -2620,7 +2638,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + daemon->edns_pktsz);
|
||||
size = add_do_bit(header, size, daemon->edns_pktsz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
@@ -2715,7 +2733,7 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
|
||||
m = process_reply(out_header, now, serv, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede);
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, 65536, ede);
|
||||
|
||||
/* process_reply() adds pheader itself */
|
||||
have_pseudoheader = 0;
|
||||
@@ -2724,14 +2742,22 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
}
|
||||
}
|
||||
|
||||
/* Fresh data is in the cache now. Go on with the queries the client
|
||||
had already sent, if the connection is still open. */
|
||||
if (do_stale)
|
||||
break;
|
||||
{
|
||||
if (closed)
|
||||
break;
|
||||
do_stale = 0;
|
||||
daemon->log_source_addr = &peer_addr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
{
|
||||
if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff,
|
||||
((char *) out_header) + 65536, first, last, ede)))
|
||||
65536, first, last, ede)))
|
||||
break;
|
||||
}
|
||||
else if (ede == EDE_UNSET)
|
||||
@@ -2747,9 +2773,9 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
else
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, 65536, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
check_log_writer(1);
|
||||
@@ -2774,19 +2800,27 @@ void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
/* If we answered with stale data, this process will now try and get fresh data into
|
||||
the cache and cannot therefore accept new queries. Close the incoming
|
||||
connection to signal that to the client. Then set do_stale and loop round
|
||||
once more to try and get fresh data, after which we exit. */
|
||||
once more to try and get fresh data, after which we exit. A client which
|
||||
has already sent its next query is waiting for the answer and would lose
|
||||
it, so keep the connection open then and answer it after the refresh. */
|
||||
if (stale)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
char c;
|
||||
|
||||
if (recv(confd, &c, 1, MSG_PEEK | MSG_DONTWAIT) != 1)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
closed = 1;
|
||||
}
|
||||
do_stale = 1;
|
||||
/* Don't mark the query with the source when refreshing stale data. */
|
||||
daemon->log_source_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
if (!do_stale)
|
||||
/* If we ran once to get fresh data, confd may be closed already. */
|
||||
if (!closed)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
|
||||
+7
-19
@@ -533,18 +533,12 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
|
||||
close(pipeout[0]);
|
||||
else
|
||||
{
|
||||
while (fgets(daemon->packet, daemon->packet_buff_sz, fp))
|
||||
{
|
||||
/* do not include new lines, log will append them */
|
||||
size_t len = strlen(daemon->packet);
|
||||
if (len > 0)
|
||||
{
|
||||
--len;
|
||||
if (daemon->packet[len] == '\n')
|
||||
daemon->packet[len] = 0;
|
||||
}
|
||||
send_event(event_fd, EVENT_SCRIPT_LOG, 0, daemon->packet);
|
||||
}
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
|
||||
while (get_line_alloc(fp, &line, &linesz))
|
||||
send_event(event_fd, EVENT_SCRIPT_LOG, 0, line);
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
@@ -718,13 +712,7 @@ static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, ch
|
||||
break;
|
||||
|
||||
if (next && (next != buf))
|
||||
{
|
||||
char *p;
|
||||
/* No "=" in value */
|
||||
if ((p = strchr((char *)buf, '=')))
|
||||
*p = 0;
|
||||
val = (char *)buf;
|
||||
}
|
||||
val = (char *)buf;
|
||||
}
|
||||
|
||||
my_setenv(env, val, err);
|
||||
|
||||
+13
-11
@@ -527,7 +527,7 @@ void lease_update_slaac(time_t now)
|
||||
|
||||
/* Find interfaces associated with leases at start-up. This gets updated as
|
||||
we do DHCP transactions, but information about directly-connected subnets
|
||||
is useful from scrips and necessary for determining SLAAC addresses from
|
||||
is useful from scripts and necessary for determining SLAAC addresses from
|
||||
start-time. */
|
||||
void lease_find_interfaces(time_t now)
|
||||
{
|
||||
@@ -1027,6 +1027,13 @@ static void kill_name(struct dhcp_lease *lease)
|
||||
lease->old_hostname = lease->hostname;
|
||||
|
||||
lease->hostname = lease->fqdn = NULL;
|
||||
|
||||
#ifdef HAVE_DHCP6
|
||||
/* A lease which had a hostname when slaac_add_addrs() was called on it
|
||||
may have slaac-address records. Since it no longer has a hostname,
|
||||
they are no-longer valid, so update to remove them */
|
||||
slaac_add_addrs(lease, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
void lease_calc_fqdns(void)
|
||||
@@ -1151,6 +1158,10 @@ void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, ch
|
||||
file_dirty = 1;
|
||||
dns_dirty = 1;
|
||||
lease->flags |= LEASE_CHANGED; /* run script on change */
|
||||
#ifdef HAVE_DHCP6
|
||||
slaac_add_addrs(lease, dnsmasq_time(), 0);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
|
||||
@@ -1250,15 +1261,6 @@ int do_script_run(time_t now)
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef HAVE_DHCP6
|
||||
struct slaac_address *slaac, *tmp;
|
||||
for (slaac = lease->slaac_address; slaac; slaac = tmp)
|
||||
{
|
||||
tmp = slaac->next;
|
||||
free(slaac);
|
||||
}
|
||||
#endif
|
||||
kill_name(lease);
|
||||
#ifdef HAVE_SCRIPT
|
||||
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
|
||||
#endif
|
||||
@@ -1267,7 +1269,7 @@ int do_script_run(time_t now)
|
||||
#endif
|
||||
old_leases = lease->next;
|
||||
|
||||
free(lease->hostname);
|
||||
kill_name(lease); /* frees slaac_addr, hostname and fqdn */
|
||||
free(lease->clid);
|
||||
free(lease->extradata);
|
||||
free(lease->agent_id);
|
||||
|
||||
@@ -97,27 +97,30 @@ int log_start(struct passwd *ent_pw, int errfd)
|
||||
entries_alloced = 1;
|
||||
}
|
||||
|
||||
/* If we're running as root and going to change uid later,
|
||||
change the ownership here so that the file is always owned by
|
||||
the dnsmasq user. Then logrotate can just copy the owner.
|
||||
Failure of the chown call is OK, (for instance when started as non-root).
|
||||
|
||||
If we've created a file with group-id root, we also make
|
||||
the file group-writable. This gives processes in the root group
|
||||
write access to the file and avoids the problem that on some systems,
|
||||
once the file is owned by the dnsmasq user, it can't be written
|
||||
whilst dnsmasq is running as root during startup.
|
||||
*/
|
||||
if (log_to_file && !log_stderr && ent_pw && ent_pw->pw_uid != 0)
|
||||
{
|
||||
struct stat ls;
|
||||
if (getgid() == 0 && fstat(log_fd, &ls) == 0 && ls.st_gid == 0 &&
|
||||
(ls.st_mode & S_IWGRP) == 0)
|
||||
(void)fchmod(log_fd, ls.st_mode | S_IWGRP);
|
||||
if (fchown(log_fd, ent_pw->pw_uid, -1) != 0)
|
||||
ret = errno;
|
||||
|
||||
/* Only mess with permissions for regular files, not (eg) /dev/null */
|
||||
if (fstat(log_fd, &ls) == 0 && S_ISREG(ls.st_mode))
|
||||
{
|
||||
/* If we're running as root and going to change uid later,
|
||||
change the ownership here so that the file is always owned by
|
||||
the dnsmasq user. Then logrotate can just copy the owner. */
|
||||
if (fchown(log_fd, ent_pw->pw_uid, -1) != 0)
|
||||
ret = errno;
|
||||
|
||||
/* If we've created a file with group-id root, we also make
|
||||
the file group-writable. This gives processes in the root group
|
||||
write access to the file and avoids the problem that on some systems,
|
||||
once the file is owned by the dnsmasq user, it can't be written
|
||||
whilst dnsmasq is running as root during startup.
|
||||
Failure of the chown call is OK, (for instance when started as non-root). */
|
||||
if (getgid() == 0 && ls.st_gid == 0 && (ls.st_mode & S_IWGRP) == 0)
|
||||
(void)fchmod(log_fd, ls.st_mode | S_IWGRP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+13
-18
@@ -1254,19 +1254,14 @@ void warn_bound_listeners(void)
|
||||
int advice = 0;
|
||||
|
||||
for (iface = daemon->interfaces; iface; iface = iface->next)
|
||||
if (!iface->dns_auth)
|
||||
if (!iface->dns_auth && !iface->warned &&
|
||||
iface->addr.sa.sa_family == AF_INET && !private_net(iface->addr.in.sin_addr, 1))
|
||||
{
|
||||
if (iface->addr.sa.sa_family == AF_INET)
|
||||
{
|
||||
if (!private_net(iface->addr.in.sin_addr, 1))
|
||||
{
|
||||
inet_ntop(AF_INET, &iface->addr.in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
iface->warned = advice = 1;
|
||||
my_syslog(LOG_WARNING,
|
||||
_("LOUD WARNING: listening on %s may accept requests via interfaces other than %s"),
|
||||
daemon->addrbuff, iface->name);
|
||||
}
|
||||
}
|
||||
inet_ntop(AF_INET, &iface->addr.in.sin_addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
iface->warned = advice = 1;
|
||||
my_syslog(LOG_WARNING,
|
||||
_("LOUD WARNING: listening on %s may accept requests via interfaces other than %s"),
|
||||
daemon->addrbuff, iface->name);
|
||||
}
|
||||
|
||||
if (advice)
|
||||
@@ -1462,7 +1457,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname, unsi
|
||||
|
||||
/* when using random ports, servers which would otherwise use
|
||||
the INADDR_ANY/port0 socket have sfd set to NULL, this is
|
||||
anything without an explictly set source port. */
|
||||
anything without an explicitly set source port. */
|
||||
if (!daemon->osport)
|
||||
{
|
||||
errno = 0;
|
||||
@@ -1699,19 +1694,19 @@ void check_servers(int no_loop_check)
|
||||
int reload_servers(char *fname)
|
||||
{
|
||||
FILE *f;
|
||||
char *line;
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
int gotone = 0;
|
||||
|
||||
/* buff happens to be MAXDNAMESTR long... */
|
||||
if (!(f = fopen(fname, "r")))
|
||||
{
|
||||
my_syslog(LOG_ERR, _("failed to read %s: %s"), fname, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
mark_servers(SERV_FROM_RESOLV);
|
||||
|
||||
while ((line = fgets(daemon->namebuff, MAXDNAMESTR, f)))
|
||||
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
union mysockaddr addr, source_addr;
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
|
||||
+113
-74
@@ -745,7 +745,7 @@ static int numeric_check(char *a)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!a)
|
||||
if (!a || *a == 0)
|
||||
return 0;
|
||||
|
||||
unhide_metas(a);
|
||||
@@ -771,11 +771,14 @@ static int strtoul_check(char *a, u32 *res)
|
||||
|
||||
if (!numeric_check(a))
|
||||
return 0;
|
||||
|
||||
x = strtoul(a, NULL, 10);
|
||||
if (errno || x > UINT32_MAX) {
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
if (errno || x > UINT32_MAX)
|
||||
{
|
||||
errno = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*res = (u32)x;
|
||||
return 1;
|
||||
}
|
||||
@@ -1383,7 +1386,7 @@ static void dhcp_netid_list_free(struct dhcp_netid_list *netid)
|
||||
netid = netid->next;
|
||||
/* Note: don't use dhcp_netid_free() here, since that
|
||||
frees a list linked on netid->next. Where a netid_list
|
||||
is used that's because the the ->next pointers in the
|
||||
is used that's because the ->next pointers in the
|
||||
netids are being used to temporarily construct
|
||||
a list of valid tags. */
|
||||
free(tmplist->list->net);
|
||||
@@ -1471,58 +1474,45 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
|
||||
while (arg)
|
||||
{
|
||||
char *start = arg;
|
||||
comma = split(arg);
|
||||
|
||||
for (cp = arg; *cp; cp++)
|
||||
|
||||
if (strstr(arg, "option:") == arg)
|
||||
start = arg+7;
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (strstr(arg, "option6:") == arg)
|
||||
{
|
||||
start = arg+8;
|
||||
is6 = 1;
|
||||
}
|
||||
#endif
|
||||
else if (strstr(arg, "option4:") == arg)
|
||||
start = arg+8;
|
||||
|
||||
for (cp = start; *cp; cp++)
|
||||
if (*cp < '0' || *cp > '9')
|
||||
break;
|
||||
|
||||
if (!*cp)
|
||||
{
|
||||
new->opt = atoi(arg);
|
||||
new->opt = atoi(start);
|
||||
opt_len = 0;
|
||||
option_ok = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strstr(arg, "option:") == arg)
|
||||
|
||||
/* option*:<opt>|<optname> must follow tag and vendor string. */
|
||||
if (start != arg)
|
||||
{
|
||||
if ((new->opt = lookup_dhcp_opt(AF_INET, arg+7)) != -1)
|
||||
if ((new->opt = lookup_dhcp_opt(is6 ? AF_INET6: AF_INET, start)) != -1)
|
||||
{
|
||||
opt_len = lookup_dhcp_len(AF_INET, new->opt);
|
||||
opt_len = lookup_dhcp_len(is6 ? AF_INET6: AF_INET, new->opt);
|
||||
/* option:<optname> must follow tag and vendor string. */
|
||||
if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
|
||||
option_ok = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_DHCP6
|
||||
else if (strstr(arg, "option6:") == arg)
|
||||
{
|
||||
for (cp = arg+8; *cp; cp++)
|
||||
if (*cp < '0' || *cp > '9')
|
||||
break;
|
||||
|
||||
if (!*cp)
|
||||
{
|
||||
new->opt = atoi(arg+8);
|
||||
opt_len = 0;
|
||||
option_ok = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((new->opt = lookup_dhcp_opt(AF_INET6, arg+8)) != -1)
|
||||
{
|
||||
opt_len = lookup_dhcp_len(AF_INET6, new->opt);
|
||||
if (!(opt_len & OT_INTERNAL) || flags == DHOPT_MATCH)
|
||||
option_ok = 1;
|
||||
}
|
||||
}
|
||||
/* option6:<opt>|<optname> must follow tag and vendor string. */
|
||||
is6 = 1;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
else if (strstr(arg, "vendor:") == arg)
|
||||
{
|
||||
new->u.vendor_class = (unsigned char *)opt_string_alloc(arg+7);
|
||||
@@ -1701,7 +1691,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
parse_hex(comma, new->val, digs, (flags & DHOPT_MATCH) ? &new->u.wildcard_mask : NULL, NULL);
|
||||
new->flags |= DHOPT_HEX;
|
||||
}
|
||||
else if (is_dec)
|
||||
else if (is_dec && !(opt_len & OT_DHCP6_VENDOR))
|
||||
{
|
||||
int i, val = atoi(comma);
|
||||
/* assume numeric arg is 1 byte except for
|
||||
@@ -1902,6 +1892,48 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
comma = split(arg);
|
||||
}
|
||||
|
||||
new->val = newp;
|
||||
new->len = p - newp;
|
||||
}
|
||||
else if (comma && (opt_len & OT_DHCP6_VENDOR))
|
||||
{
|
||||
/* First arg is Enterprise ID (4 bytes)
|
||||
subsequent are length fields (2 bytes each) + string */
|
||||
int enterprise, i, commas = 1;
|
||||
unsigned char *p, *newp;
|
||||
|
||||
for (i = 0; comma[i]; i++)
|
||||
if (comma[i] == ',')
|
||||
commas++;
|
||||
|
||||
newp = opt_malloc(strlen(comma)+(2*commas)+4);
|
||||
p = newp;
|
||||
arg = comma;
|
||||
comma = split(arg);
|
||||
|
||||
if (atoi_check(arg, &enterprise))
|
||||
PUTLONG(enterprise, p);
|
||||
else
|
||||
goto_err(_("bad or missing enterprise ID in dhcp-option"));
|
||||
|
||||
arg = comma;
|
||||
comma = split(arg);
|
||||
|
||||
if (!arg || !*arg)
|
||||
goto_err(_("missing vendor class in dhcp-option"));
|
||||
|
||||
while (arg && *arg)
|
||||
{
|
||||
u16 len = strlen(arg);
|
||||
unhide_metas(arg);
|
||||
PUTSHORT(len, p);
|
||||
memcpy(p, arg, len);
|
||||
p += len;
|
||||
|
||||
arg = comma;
|
||||
comma = split(arg);
|
||||
}
|
||||
|
||||
new->val = newp;
|
||||
new->len = p - newp;
|
||||
}
|
||||
@@ -1965,7 +1997,7 @@ static int parse_dhcp_opt(char *errstr, char *arg, int flags)
|
||||
|
||||
if (flags == DHOPT_PXE_OPT && (new->flags & DHOPT_VENDOR))
|
||||
goto_err(_("No vendor-encap options allowed in dhcp-option-pxe"));
|
||||
|
||||
|
||||
if (flags == DHOPT_MATCH)
|
||||
{
|
||||
if ((new->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR)) ||
|
||||
@@ -2800,7 +2832,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_CPE_ID: /* --add-dns-client */
|
||||
case LOPT_CPE_ID: /* --add-cpe-id */
|
||||
if (arg)
|
||||
daemon->dns_client_id = opt_string_alloc(arg);
|
||||
break;
|
||||
@@ -5014,7 +5046,7 @@ err:
|
||||
new->target = target;
|
||||
new->ttl = ttl;
|
||||
|
||||
for (arg += arglen+1; *arg && isspace((unsigned char)*arg); arg++);
|
||||
for (arg += arglen+1; arg != last && (!*arg || isspace((unsigned char)*arg)); arg++);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -5471,9 +5503,20 @@ err:
|
||||
static void read_file(char *file, FILE *f, int hard_opt, int from_script)
|
||||
{
|
||||
volatile int lineno = 0;
|
||||
char *buff = daemon->namebuff;
|
||||
char *buff;
|
||||
size_t buffsz;
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (hard_opt != 0)
|
||||
{
|
||||
setjmp(mem_jmp);
|
||||
mem_recover = 1;
|
||||
}
|
||||
|
||||
while (fgets(buff, MAXDNAMESTR, f))
|
||||
buff = NULL;
|
||||
buffsz = 0;
|
||||
|
||||
while (get_line_alloc(f, &buff, &buffsz))
|
||||
{
|
||||
int white, i;
|
||||
volatile int option;
|
||||
@@ -5481,15 +5524,7 @@ static void read_file(char *file, FILE *f, int hard_opt, int from_script)
|
||||
size_t len;
|
||||
|
||||
option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
|
||||
|
||||
/* Memory allocation failure longjmps here if mem_recover == 1 */
|
||||
if (option != 0 || hard_opt == LOPT_REV_SERV)
|
||||
{
|
||||
if (setjmp(mem_jmp))
|
||||
continue;
|
||||
mem_recover = 1;
|
||||
}
|
||||
|
||||
|
||||
arg = NULL;
|
||||
lineno++;
|
||||
errmess = NULL;
|
||||
@@ -5949,16 +5984,15 @@ void reread_dhcp(void)
|
||||
void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
size_t argbuf_size = 300;
|
||||
char *argbuf = opt_malloc(argbuf_size);
|
||||
char *buff = opt_malloc(MAXDNAMESTR+1);
|
||||
char *argbuf = safe_malloc(argbuf_size);
|
||||
int option, testmode = 0;
|
||||
char *arg, *conffile = NULL;
|
||||
|
||||
opterr = 0;
|
||||
|
||||
|
||||
daemon = opt_malloc(sizeof(struct daemon));
|
||||
memset(daemon, 0, sizeof(struct daemon));
|
||||
daemon->namebuff = buff;
|
||||
daemon->namebuff = safe_malloc(MAXDNAMESTR+1);
|
||||
daemon->workspacename = safe_malloc(MAXDNAMESTR+1);
|
||||
daemon->addrbuff = safe_malloc(ADDRSTRLEN);
|
||||
|
||||
@@ -6197,9 +6231,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
/* create default, if not specified */
|
||||
if (daemon->authserver && !daemon->hostmaster)
|
||||
{
|
||||
strcpy(buff, "hostmaster.");
|
||||
strcat(buff, daemon->authserver);
|
||||
daemon->hostmaster = opt_string_alloc(buff);
|
||||
strcpy(daemon->namebuff, "hostmaster.");
|
||||
strncat(daemon->namebuff, daemon->authserver, MAXDNAMESTR - strlen(daemon->namebuff));
|
||||
daemon->hostmaster = opt_string_alloc(daemon->namebuff);
|
||||
}
|
||||
|
||||
if (!daemon->dhcp_pxe_vendors)
|
||||
@@ -6214,11 +6248,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
struct mx_srv_record *mx;
|
||||
|
||||
if (gethostname(buff, MAXDNAMESTR) == -1)
|
||||
if (gethostname(daemon->namebuff, MAXDNAMESTR) == -1)
|
||||
die(_("cannot get host-name: %s"), NULL, EC_MISC);
|
||||
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (!mx->issrv && hostname_isequal(mx->name, buff))
|
||||
if (!mx->issrv && hostname_isequal(mx->name, daemon->namebuff))
|
||||
break;
|
||||
|
||||
if ((daemon->mxtarget || option_bool(OPT_LOCALMX)) && !mx)
|
||||
@@ -6227,12 +6261,12 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
mx->next = daemon->mxnames;
|
||||
mx->issrv = 0;
|
||||
mx->target = NULL;
|
||||
mx->name = opt_string_alloc(buff);
|
||||
mx->name = opt_string_alloc(daemon->namebuff);
|
||||
daemon->mxnames = mx;
|
||||
}
|
||||
|
||||
if (!daemon->mxtarget)
|
||||
daemon->mxtarget = opt_string_alloc(buff);
|
||||
daemon->mxtarget = opt_string_alloc(daemon->namebuff);
|
||||
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (!mx->issrv && !mx->target)
|
||||
@@ -6247,7 +6281,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if (option_bool(OPT_RESOLV_DOMAIN))
|
||||
{
|
||||
char *line;
|
||||
char *line = NULL;
|
||||
size_t linesz = 0;
|
||||
FILE *f;
|
||||
|
||||
if (option_bool(OPT_NO_RESOLV) ||
|
||||
@@ -6258,7 +6293,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
if (!(f = fopen((daemon->resolv_files)->name, "r")))
|
||||
die(_("failed to read %s: %s"), (daemon->resolv_files)->name, EC_FILE);
|
||||
|
||||
while ((line = fgets(buff, MAXDNAMESTR, f)))
|
||||
while (get_line_alloc(f, &line, &linesz))
|
||||
{
|
||||
char *token = strtok(line, " \t\n\r");
|
||||
|
||||
@@ -6267,7 +6302,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if ((token = strtok(NULL, " \t\n\r")) &&
|
||||
(daemon->domain_suffix = canonicalise_opt(token)))
|
||||
break;
|
||||
{
|
||||
/* We don't call get_line_alloc() until it returns false and frees the automatically */
|
||||
get_line_alloc(NULL, &line, &linesz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
@@ -6278,7 +6317,7 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
|
||||
if (daemon->domain_suffix)
|
||||
{
|
||||
/* add domain for any srv record without one. */
|
||||
/* add domain for any srv record without one. */
|
||||
struct mx_srv_record *srv;
|
||||
|
||||
for (srv = daemon->mxnames; srv; srv = srv->next)
|
||||
@@ -6288,11 +6327,11 @@ void read_opts(int argc, char **argv, char *compile_opts)
|
||||
{
|
||||
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAMESTR)
|
||||
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
|
||||
strcpy(buff, srv->name);
|
||||
strcat(buff, ".");
|
||||
strcat(buff, daemon->domain_suffix);
|
||||
strcpy(daemon->namebuff, srv->name);
|
||||
strcat(daemon->namebuff, ".");
|
||||
strcat(daemon->namebuff, daemon->domain_suffix);
|
||||
free(srv->name);
|
||||
if (!(srv->name = canonicalise_opt(buff)))
|
||||
if (!(srv->name = canonicalise_opt(daemon->namebuff)))
|
||||
die(_("bad srv-host name %s after domain appended"), srv->name, EC_MISC);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -100,13 +100,13 @@ void poll_listen(int fd, short event)
|
||||
{
|
||||
/* Array too small. Extend. */
|
||||
struct pollfd *new;
|
||||
nfds_t newsize = (arrsize == 0) ? 64 : arrsize * 2;
|
||||
|
||||
arrsize = (arrsize == 0) ? 64 : arrsize * 2;
|
||||
|
||||
if (!(new = whine_realloc(pollfds, arrsize * sizeof(struct pollfd))))
|
||||
if (!(new = whine_realloc(pollfds, newsize * sizeof(struct pollfd))))
|
||||
return;
|
||||
|
||||
pollfds = new;
|
||||
arrsize = newsize;
|
||||
}
|
||||
|
||||
memmove(&pollfds[i+1], &pollfds[i], (nfds - i) * sizeof(struct pollfd));
|
||||
|
||||
+19
-7
@@ -58,7 +58,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
unsigned int preferred, unsigned int valid, void *vparam);
|
||||
static int iface_search(struct in6_addr *local, int prefix,
|
||||
int scope, int if_index, int flags,
|
||||
unsigned int prefered, unsigned int valid, void *vparam);
|
||||
unsigned int preferred, unsigned int valid, void *vparam);
|
||||
static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
|
||||
static void new_timeout(struct dhcp_context *context, char *iface_name, time_t now);
|
||||
static unsigned int calc_lifetime(struct ra_interface *ra);
|
||||
@@ -215,11 +215,8 @@ void icmp6_packet(time_t now)
|
||||
if (opt_sz == 0 || opt_sz > rem)
|
||||
return; /* Bad packet */
|
||||
|
||||
if (p[0] == ICMP6_OPT_SOURCE_MAC && ((opt_sz - 2) * 3 - 1 < MAXDNAMESTR))
|
||||
{
|
||||
print_mac(daemon->namebuff, &p[2], opt_sz - 2);
|
||||
mac = daemon->namebuff;
|
||||
}
|
||||
if (p[0] == ICMP6_OPT_SOURCE_MAC)
|
||||
mac = print_mac(&p[2], opt_sz - 2);
|
||||
}
|
||||
|
||||
if (!option_bool(OPT_QUIET_RA))
|
||||
@@ -611,6 +608,7 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
int do_slaac = 0;
|
||||
int deprecate = 0;
|
||||
int constructed = 0;
|
||||
int have_lease = 0;
|
||||
int adv_router = 0;
|
||||
int off_link = 0;
|
||||
unsigned int time = 0xffffffff;
|
||||
@@ -663,6 +661,8 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
if (time < ((unsigned int)(3 * param->adv_interval)))
|
||||
time = 3 * param->adv_interval;
|
||||
}
|
||||
if (context->flags & CONTEXT_SETLEASE)
|
||||
have_lease = 1;
|
||||
|
||||
if (context->flags & CONTEXT_DEPRECATE)
|
||||
deprecate = 1;
|
||||
@@ -700,7 +700,19 @@ static int add_prefixes(struct in6_addr *local, int prefix,
|
||||
/* configured time is ceiling */
|
||||
if (!constructed || valid > time)
|
||||
valid = time;
|
||||
|
||||
|
||||
/* For CONSTRUCTED prefixes an explicitly configured lease time is
|
||||
also a floor: an upstream delegating with very short lifetimes
|
||||
(mobile links) would otherwise churn the addresses of any client
|
||||
that sleeps through a few RAs. Deprecation still wins below. */
|
||||
if (constructed && have_lease && !deprecate && !(flags & IFACE_DEPRECATED))
|
||||
{
|
||||
if (valid < time)
|
||||
valid = time;
|
||||
if (preferred < time)
|
||||
preferred = time;
|
||||
}
|
||||
|
||||
if (flags & IFACE_DEPRECATED)
|
||||
preferred = 0;
|
||||
|
||||
|
||||
+12
-26
@@ -448,7 +448,7 @@ int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
(IN6_IS_ADDR_LOOPBACK(a) && ban_localhost) || /* RFC 6303 4.3 */
|
||||
IN6_IS_ADDR_LINKLOCAL(a) || /* RFC 6303 4.5 */
|
||||
IN6_IS_ADDR_SITELOCAL(a) ||
|
||||
((unsigned char *)a)[0] == 0xfd || /* RFC 6303 4.4 */
|
||||
(((unsigned char *)a)[0] & 0xfe) == 0xfc || /* RFC 4196 3.1 */
|
||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||
}
|
||||
|
||||
@@ -560,7 +560,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
soa_len = strlen(daemon->workspacename);
|
||||
|
||||
/* SOA must be for the name we're interested in. */
|
||||
if (soa_len <= name_len && memcmp(daemon->workspacename, name + name_len - soa_len, soa_len) == 0)
|
||||
if (hostname_issubdomain(daemon->workspacename, name) != 0)
|
||||
{
|
||||
int prefix = name_len - soa_len;
|
||||
|
||||
@@ -585,11 +585,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
{
|
||||
len = to_wire(daemon->workspacename);
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
addr.rrblock.datalen += len;
|
||||
}
|
||||
}
|
||||
@@ -607,11 +604,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
|
||||
int secflag = 0;
|
||||
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p, 20))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
addr.rrblock.datalen += 20;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
@@ -942,10 +936,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
{
|
||||
/* Copy the rest of the RR and end. */
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, endrr - p1))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
addr.rrblock.datalen += endrr - p1;
|
||||
}
|
||||
else if (desc == 0)
|
||||
@@ -962,11 +954,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
|
||||
len = to_wire(name);
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, name, len))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
addr.rrblock.datalen += len;
|
||||
}
|
||||
else
|
||||
@@ -976,11 +965,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
desc = endrr - p1;
|
||||
|
||||
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, (char *)p1, desc))
|
||||
{
|
||||
blockdata_free(addr.rrblock.rrdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
addr.rrblock.datalen += desc;
|
||||
p1 += desc;
|
||||
}
|
||||
|
||||
+12
-9
@@ -1313,7 +1313,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
unsigned char *mac = extended_hwaddr(ltmp->hwaddr_type, ltmp->hwaddr_len,
|
||||
ltmp->hwaddr, ltmp->clid_len, ltmp->clid, &len);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("not using configured address %s because it is leased to %s"),
|
||||
daemon->addrbuff, print_mac(daemon->namebuff, mac, len));
|
||||
daemon->addrbuff, print_mac(mac, len));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1537,7 +1537,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
{
|
||||
inet_ntop(AF_INET, <mp->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("abandoning lease to %s of %s"),
|
||||
print_mac(daemon->namebuff, ltmp->hwaddr, ltmp->hwaddr_len),
|
||||
print_mac(ltmp->hwaddr, ltmp->hwaddr_len),
|
||||
daemon->addrbuff);
|
||||
lease = ltmp;
|
||||
}
|
||||
@@ -1611,7 +1611,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
lease->extradata_size = lease->extradata_len = 0;
|
||||
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_VENDOR_ID, 1));
|
||||
add_extradata_opt(lease, option_find(mess, sz, OPTION_HOSTNAME, 1));
|
||||
lease_add_extradata(lease, (unsigned char *)client_hostname,
|
||||
client_hostname ? strlen(client_hostname) : 0, 0);
|
||||
add_extradata_opt(lease, oui);
|
||||
add_extradata_opt(lease, serial);
|
||||
add_extradata_opt(lease, class);
|
||||
@@ -1918,16 +1919,18 @@ static void add_extradata_opt(struct dhcp_lease *lease, unsigned char *opt)
|
||||
static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
int mac_len, char *interface, char *string, char *err, u32 xid)
|
||||
{
|
||||
char *mac = "";
|
||||
|
||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||
return;
|
||||
|
||||
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
|
||||
daemon->addrbuff[0] = 0;
|
||||
|
||||
if (addr)
|
||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
if (ext_mac)
|
||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||
mac = print_mac(ext_mac, mac_len);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s%s%s%s",
|
||||
@@ -1936,7 +1939,7 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
mac,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
@@ -1946,16 +1949,16 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
interface,
|
||||
daemon->addrbuff,
|
||||
addr ? " " : "",
|
||||
daemon->namebuff,
|
||||
mac,
|
||||
ext_mac ? " " : "",
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
ubus_event_bcast("dhcp.ack", mac, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
ubus_event_bcast("dhcp.release", mac, addr ? daemon->addrbuff : NULL, string, interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+23
-21
@@ -255,10 +255,10 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
|
||||
/* the packet data is unaligned, copy to aligned storage */
|
||||
memcpy(&align, inbuff + 2, IN6ADDRSZ);
|
||||
|
||||
/* RFC6221 para 4 says if link_address in encapulation
|
||||
/* RFC6221 para 4 says if link_address in encapsulation
|
||||
is zero, ignore it, and, by implication, use the link
|
||||
address of any enclosing encapsulation or, failing that
|
||||
of the arrival interface on the the server. */
|
||||
of the arrival interface on the server. */
|
||||
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr,
|
||||
IN6_IS_ADDR_UNSPECIFIED(&align) ? link_address : &align, now))
|
||||
return 0;
|
||||
@@ -474,10 +474,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
if (state->mac_len != 0)
|
||||
{
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
{
|
||||
print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
|
||||
}
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid,
|
||||
print_mac(state->mac, state->mac_len));
|
||||
|
||||
for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
|
||||
if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
|
||||
@@ -504,7 +502,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
|
||||
|
||||
state->fqdn_flags &= ~0x04;
|
||||
|
||||
if (len != 0 && len < 255)
|
||||
if (len >= 1 && len < 255)
|
||||
{
|
||||
unsigned char *pp, *op = opt6_ptr(opt, 1);
|
||||
char *pq = daemon->dhcp_buff;
|
||||
@@ -2077,14 +2075,6 @@ static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, c
|
||||
|
||||
static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
|
||||
{
|
||||
int clid_len = state->clid_len;
|
||||
|
||||
/* avoid buffer overflow */
|
||||
if (clid_len > 100)
|
||||
clid_len = 100;
|
||||
|
||||
print_mac(daemon->namebuff, state->clid, clid_len);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
|
||||
@@ -2099,14 +2089,14 @@ static void log6_packet(struct state *state, char *type, struct in6_addr *addr,
|
||||
type,
|
||||
state->iface_name,
|
||||
daemon->dhcp_buff2,
|
||||
daemon->namebuff,
|
||||
print_mac(state->clid, state->clid_len),
|
||||
string ? string : "");
|
||||
else
|
||||
my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s %s",
|
||||
type,
|
||||
state->iface_name,
|
||||
daemon->dhcp_buff2,
|
||||
daemon->namebuff,
|
||||
print_mac(state->clid, state->clid_len),
|
||||
string ? string : "");
|
||||
}
|
||||
|
||||
@@ -2164,12 +2154,12 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return 0 if we're not configured to relay. */
|
||||
int relay_upstream6(int iface_index, ssize_t sz,
|
||||
struct in6_addr *peer_address, u32 scope_id, time_t now)
|
||||
{
|
||||
unsigned char *header;
|
||||
unsigned char *inbuff = daemon->dhcp_packet.iov_base;
|
||||
int msg_type = *inbuff;
|
||||
int hopcount, o;
|
||||
struct in6_addr multicast;
|
||||
unsigned int maclen, mactype;
|
||||
@@ -2186,10 +2176,22 @@ int relay_upstream6(int iface_index, ssize_t sz,
|
||||
|
||||
inet_pton(AF_INET6, ALL_SERVERS, &multicast);
|
||||
get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
|
||||
|
||||
/* We need at least four bytes for valid client/server message
|
||||
(type, and transaction ID) A relay-originated message needs more. */
|
||||
if (sz < 4)
|
||||
return 1;
|
||||
|
||||
/* Get hop count from nested relayed message */
|
||||
if (msg_type == DHCP6RELAYFORW)
|
||||
hopcount = *((unsigned char *)inbuff+1) + 1;
|
||||
if (inbuff[0] == DHCP6RELAYFORW)
|
||||
{
|
||||
/* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
|
||||
which is 1 + 1 + 16 + 16 + 2 + 2 = 38 */
|
||||
if (sz < 38)
|
||||
return 1;
|
||||
|
||||
/* Get hop count from nested relayed message. */
|
||||
hopcount = inbuff[1] + 1;
|
||||
}
|
||||
else
|
||||
hopcount = 0;
|
||||
|
||||
|
||||
+67
-67
@@ -27,87 +27,87 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
|
||||
struct slaac_address *slaac, *old, **up;
|
||||
struct dhcp_context *context;
|
||||
int dns_dirty = 0;
|
||||
|
||||
if (!(lease->flags & LEASE_HAVE_HWADDR) ||
|
||||
(lease->flags & (LEASE_TA | LEASE_NA)) ||
|
||||
lease->last_interface == 0 ||
|
||||
!lease->hostname)
|
||||
return ;
|
||||
|
||||
|
||||
old = lease->slaac_address;
|
||||
lease->slaac_address = NULL;
|
||||
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME) &&
|
||||
!(context->flags & CONTEXT_OLD) &&
|
||||
lease->last_interface == context->if_index)
|
||||
{
|
||||
struct in6_addr addr = context->start6;
|
||||
if (lease->hwaddr_len == 6 &&
|
||||
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
|
||||
if ((lease->flags & LEASE_HAVE_HWADDR) &&
|
||||
!(lease->flags & (LEASE_TA | LEASE_NA)) &&
|
||||
lease->last_interface != 0 &&
|
||||
lease->hostname)
|
||||
{
|
||||
for (context = daemon->dhcp6; context; context = context->next)
|
||||
if ((context->flags & CONTEXT_RA_NAME) &&
|
||||
!(context->flags & CONTEXT_OLD) &&
|
||||
lease->last_interface == context->if_index)
|
||||
{
|
||||
/* convert MAC address to EUI-64 */
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
|
||||
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
|
||||
addr.s6_addr[11] = 0xff;
|
||||
addr.s6_addr[12] = 0xfe;
|
||||
}
|
||||
struct in6_addr addr = context->start6;
|
||||
if (lease->hwaddr_len == 6 &&
|
||||
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
|
||||
{
|
||||
/* convert MAC address to EUI-64 */
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
|
||||
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
|
||||
addr.s6_addr[11] = 0xff;
|
||||
addr.s6_addr[12] = 0xfe;
|
||||
}
|
||||
#if defined(ARPHRD_EUI64)
|
||||
else if (lease->hwaddr_len == 8 &&
|
||||
lease->hwaddr_type == ARPHRD_EUI64)
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
|
||||
else if (lease->hwaddr_len == 8 &&
|
||||
lease->hwaddr_type == ARPHRD_EUI64)
|
||||
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
|
||||
#endif
|
||||
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
|
||||
else if (lease->clid_len == 9 &&
|
||||
lease->clid[0] == ARPHRD_EUI64 &&
|
||||
lease->hwaddr_type == ARPHRD_IEEE1394)
|
||||
/* FireWire has EUI-64 identifier as clid */
|
||||
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
|
||||
else if (lease->clid_len == 9 &&
|
||||
lease->clid[0] == ARPHRD_EUI64 &&
|
||||
lease->hwaddr_type == ARPHRD_IEEE1394)
|
||||
/* FireWire has EUI-64 identifier as clid */
|
||||
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
|
||||
#endif
|
||||
else
|
||||
continue;
|
||||
|
||||
addr.s6_addr[8] ^= 0x02;
|
||||
|
||||
/* check if we already have this one */
|
||||
for (up = &old, slaac = old; slaac; slaac = slaac->next)
|
||||
{
|
||||
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
|
||||
{
|
||||
*up = slaac->next;
|
||||
/* recheck when DHCPv4 goes through init-reboot */
|
||||
if (force)
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
dns_dirty = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
up = &slaac->next;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
/* No, make new one */
|
||||
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
slaac->addr = addr;
|
||||
/* Do RA's to prod it */
|
||||
ra_start_unsolicited(now, context);
|
||||
addr.s6_addr[8] ^= 0x02;
|
||||
|
||||
/* check if we already have this one */
|
||||
for (up = &old, slaac = old; slaac; slaac = slaac->next)
|
||||
{
|
||||
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
|
||||
{
|
||||
*up = slaac->next;
|
||||
/* recheck when DHCPv4 goes through init-reboot */
|
||||
if (force)
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
dns_dirty = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
up = &slaac->next;
|
||||
}
|
||||
|
||||
/* No, make new one */
|
||||
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
|
||||
{
|
||||
slaac->ping_time = now;
|
||||
slaac->backoff = 1;
|
||||
slaac->addr = addr;
|
||||
/* Do RA's to prod it */
|
||||
ra_start_unsolicited(now, context);
|
||||
}
|
||||
|
||||
if (slaac)
|
||||
{
|
||||
slaac->next = lease->slaac_address;
|
||||
lease->slaac_address = slaac;
|
||||
}
|
||||
}
|
||||
|
||||
if (slaac)
|
||||
{
|
||||
slaac->next = lease->slaac_address;
|
||||
lease->slaac_address = slaac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (old || dns_dirty)
|
||||
lease_update_dns(1);
|
||||
|
||||
/* Free any no reused */
|
||||
/* Free any not reused */
|
||||
for (; old; old = slaac)
|
||||
{
|
||||
slaac = old->next;
|
||||
|
||||
+23
-12
@@ -251,15 +251,14 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
|
||||
for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next)
|
||||
{
|
||||
tftp_cnt++;
|
||||
|
||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||
{
|
||||
if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
|
||||
{
|
||||
/* Handle repeated RRQ or abandoned transfer from same host and port
|
||||
by unlinking and reusing the struct transfer. */
|
||||
by abandoning old transfer. */
|
||||
*up = transfer->next;
|
||||
free_transfer(transfer);
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -268,12 +267,14 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tftp_cnt++;
|
||||
}
|
||||
|
||||
/* Enforce simultaneous transfer limit. In non-single-port mode
|
||||
this is done by not listening on the server socket when
|
||||
too many transfers are in progress. */
|
||||
if (!transfer && tftp_cnt >= daemon->tftp_max)
|
||||
if (tftp_cnt >= daemon->tftp_max)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -302,12 +303,9 @@ static void tftp_request(char *packet, ssize_t plen, struct listener *listen, ti
|
||||
#endif
|
||||
}
|
||||
|
||||
/* May reuse struct transfer from abandoned transfer in single port mode. */
|
||||
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
|
||||
if (!(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)
|
||||
@@ -547,10 +545,17 @@ static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *p
|
||||
/* trick to ban moving out of the subtree */
|
||||
if (prefix)
|
||||
{
|
||||
char *suspect = strstr(namebuff, "/..");
|
||||
char *suspect = namebuff;
|
||||
|
||||
if (suspect && (suspect[3] == '/' || suspect[3] == 0))
|
||||
goto perm;
|
||||
/* Every occurrence has to be checked: a harmless leading match
|
||||
such as "/..data" otherwise hides a later "/../" from here. */
|
||||
while ((suspect = strstr(suspect, "/..")))
|
||||
{
|
||||
if (suspect[3] == '/' || suspect[3] == 0)
|
||||
goto perm;
|
||||
|
||||
suspect += 3;
|
||||
}
|
||||
}
|
||||
|
||||
if ((fd = open(namebuff, O_RDONLY)) == -1)
|
||||
@@ -838,6 +843,12 @@ static void free_transfer(struct tftp_transfer *transfer)
|
||||
close(transfer->file->fd);
|
||||
free(transfer->file);
|
||||
}
|
||||
|
||||
/* freeing this transfer and then allocating a new one
|
||||
may result in the same memory, but the contents of
|
||||
the transfer is no longer valid to cache. */
|
||||
if (daemon->srv_save == transfer)
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
free(transfer);
|
||||
}
|
||||
@@ -857,7 +868,7 @@ static char *next(char **p, char *end)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If we don't do anything, don't write the the input/ouptut
|
||||
/* If we don't do anything, don't write the input/output
|
||||
buffer. This allows us to pass in safe read-only strings constants. */
|
||||
static void sanitise(char *buf)
|
||||
{
|
||||
|
||||
+79
-18
@@ -439,10 +439,14 @@ int hostname_issubdomain(char *a, char *b)
|
||||
for (ap = a; *ap; ap++);
|
||||
for (bp = b; *bp; bp++);
|
||||
|
||||
/* a shorter than b */
|
||||
if ((ap - a) < (bp - b))
|
||||
/* anything is a subdomain of the root domain. */
|
||||
if (ap == a)
|
||||
return (bp == b) ? 2 : 1;
|
||||
|
||||
/* b shorter than a */
|
||||
if ((bp - b) < (ap - a))
|
||||
return 0;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
c1 = (unsigned char) *(--ap);
|
||||
@@ -455,18 +459,17 @@ int hostname_issubdomain(char *a, char *b)
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
} while (bp != b);
|
||||
} while (ap != a);
|
||||
|
||||
if (ap == a)
|
||||
if (bp == b)
|
||||
return 2;
|
||||
|
||||
if (*(--ap) == '.')
|
||||
if (*(--bp) == '.')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
time_t dnsmasq_time(void)
|
||||
{
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
@@ -689,18 +692,24 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask
|
||||
return count;
|
||||
}
|
||||
|
||||
char *print_mac(char *buff, unsigned char *mac, int len)
|
||||
char *print_mac(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) ? "" : ":");
|
||||
static struct iovec buff = { NULL, 0 };
|
||||
|
||||
return buff;
|
||||
/* each byte is two digits plus ':' except that last
|
||||
which is two digits plus terminator. */
|
||||
if (len == 0 || !expand_buf(&buff, len*3))
|
||||
return "<null>";
|
||||
else
|
||||
{
|
||||
char *p = buff.iov_base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||
}
|
||||
|
||||
return buff.iov_base;
|
||||
}
|
||||
|
||||
/* rc is return from sendto and friends.
|
||||
@@ -938,7 +947,7 @@ int kernel_version(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#define hash_ptr(x) (((unsigned int)(((char *)(x)) - ((char *)NULL))) & 0xffffff)
|
||||
#define hash_ptr(x) (((uintptr_t)(x)) & 0xffffff)
|
||||
|
||||
void *whine_malloc_real(const char *func, unsigned int line, size_t size)
|
||||
{
|
||||
@@ -1027,3 +1036,55 @@ void free_real(const char *func, unsigned int line, void *ptr)
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/* get lines from f, expand buffer as required. Buffer is freed when
|
||||
EOF reached and we return zero. Buffer also freed if f == NULL. */
|
||||
int get_line_alloc(FILE *f, char **buffp, size_t *sizep)
|
||||
{
|
||||
size_t cnt = 0;
|
||||
char *buff = *buffp;
|
||||
|
||||
while (1) {
|
||||
int c = f ? getc(f) : EOF;
|
||||
|
||||
if (cnt == *sizep)
|
||||
{
|
||||
void *new;
|
||||
|
||||
if ((new = whine_realloc(buff, cnt + 1024)))
|
||||
{
|
||||
buff = *buffp = new;
|
||||
*sizep = cnt + 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* allocation failed, ignore line.
|
||||
Whine_realloc will have complained. */
|
||||
while (c != '\n' && c != EOF)
|
||||
c = getc(f);
|
||||
|
||||
cnt = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
buff[cnt] = 0;
|
||||
|
||||
if (c == '\n')
|
||||
return 1;
|
||||
|
||||
if (c == EOF)
|
||||
{
|
||||
/* handle last line without '\n' */
|
||||
if (cnt != 0)
|
||||
return 1;
|
||||
|
||||
free(buff);
|
||||
*buffp = NULL;
|
||||
*sizep = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buff[cnt++] = c;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user