Compare commits

..

53 Commits

Author SHA1 Message Date
Conrad Kostecki
d8f66f4fda Update German translations. 2025-12-07 13:44:40 +00:00
Simon Kelley
629107bd6d Merge OOB memory read fix. CVE-2025-54318 2025-12-06 14:04:23 +00:00
Sven Geuer
05464a173b Fix some issues with the swedish manual page, some causing lintian warnings
Description: Fix some issues, some causing lintian warnings
 Pointless quotation marks which get displayed in the rendered manual page.
 groff-message troff:<standard input>:868: warning:
  macro 'Om' not defined
  [usr/share/man/sv/man8/dnsmasq.8.gz:1]
 groff-message troff:<standard input>:2846: warning:
  macro 'SH-FILER' not defined (possibly missing space after 'SH')
  [usr/share/man/sv/man8/dnsmasq.8.gz:2]
Author: Sven Geuer <sge@debian.org>
Forwarded: no
Last-Update: 2025-12-04
2025-12-05 15:13:54 +00:00
Sven Geuer
ae3d3d971e Fix typos in the english manual page
Description: Fix typos in the english manual page
 These  typos were reported by Debian's lint tool.
Author: Sven Geuer <sge@debian.org>
Forwarded: no
Last-Update: 2025-12-04
2025-12-05 15:13:45 +00:00
Sven Geuer
d1845782d6 Remove trailing white space from dnsmasq.conf.example
Description: Remove trailing white space
 For now only the file reported in bug #1022706 is handled by this patch while
 upstream has been informed about all existing cases.
Bug-Debian: https://bugs.debian.org/1022706
Forwarded: https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2024q4/017826.html
Author: Sven Geuer <sge@debian.org>
Last-Update: 2024-11-23
2025-12-05 15:12:28 +00:00
Matthias Andree
edb5f85fd1 CHANGELOG: Fix two typos 2025-12-05 15:10:31 +00:00
Simon Kelley
cef74423e2 Log error if we try and use netlink in a child process.
Child processes to handle TCP connections don't
have an open netlink socket. If they call
iface_enumerate() that's a bug.
2025-12-01 09:52:51 +00:00
Simon Kelley
959dead673 More fixes for DHCP ops in Dbus when DHCP not configured.
Following eb60168382
2025-11-30 22:59:27 +00:00
Simon Kelley
eb60168382 Fix SEGV in Dbus code.
A 'AddDhcpLease' DBus message which fails to allocate
a DHCP lease will cause dnsmasq to dereference a NULL pointer
and crash.

Allocation failure can happen if the particular
flavour (IPv4 pr IPv6) of DHCP is not configured in
this dnsmasq instance, or if the configured limit on
DHCP leases is reached or (unlikley) if dnsmasq cannot
allocate memory.

This patch provides error returns for all these situations.

This patch is inspired by a patch from
Stefan Hanreich <s.hanreich@proxmox.com> which was prompted by
a bug report from Lou Lécrivain.
2025-11-28 16:10:37 +00:00
Simon Kelley
57a2f5778c Fix confusion in address checking code.
This was supposed to disallow addresses which are in use by
the DHCP server, and the code works fine when only dhcp-range
is defined per broadcast domain. It's confused when there are
multiple dhcp-ranges which are valid for a particular request.
2025-11-28 15:14:09 +00:00
Simon Kelley
7d5fbe7da3 Fix failure to add client MAC address to queries in TCP mode.
The options which cause dnsmasq to decorate a DNS query with the MAC
address on the originating client can fail when the query is sent
using TCP.

In TCP mode, dnsmasq spawns a new process to handle each TCP connection.
These child processes do not have an open netlink socket, which is
needed to read the kernel ARP table, so the process of adding the
client's MAC address as an EDNS0 option silently fails.

This is fixed by this patch by updating dnsmasq's ARP cache in the
main process just before forking the TCP-handler child process.
This ensures that the copy of the ARP cache inherited by the
TCP-handler contains the information required without the need to
read the kernel ARP table.

Thanks to Bruno Ravara for spotting and characterising this bug.
2025-11-26 21:54:59 +00:00
Petr Menšík
ded935be37 Add captive-portal name support for DHCP options
Adds support into DHCP and DHCPv6 record. Useful for announcing URL for
captive portal API URI.

https://www.rfc-editor.org/rfc/rfc8910.html
2025-11-18 22:41:15 +00:00
Simon Kelley
aa9b71c681 Merge translated strings. 2025-11-18 22:20:42 +00:00
Daniel Nylander
e497f3f2c8 Add Swedish translation.
Thanks to Daniel Nylander.
2025-11-18 22:18:02 +00:00
Simon Kelley
ee09f0655c Optimise tftp.
This change attempts to optimse TFTP perfomance.

It reads the next block immediately after sending a block,
in order to reduce latency between receiving an ACK and sending
the next block. Is also suppresses lseek() calls when reading
successive blocks.

This improves performance when doing transfers without windowing
and when only a single transfer is in progress at one time.
2025-09-01 22:35:02 +01:00
Simon Kelley
1e83316e06 Formatting. 2025-08-21 22:13:43 +01:00
Simon Kelley
9a566c0913 Tweak recently altered TFTP code. 2025-08-21 16:19:58 +01:00
Simon Kelley
06e2d479c9 Stop lookup_frec() returning old records.
get_new_frec() garbage collects struct frec when they are
(by default) 40s old. This means that in an active dnsmasq
which is getting new queries, answers to old queries start to
be ignored soon after they hit 40s old. If there are no new
queries, get_new_frec() doesn't get called, and answers to old
queries will still be accepted for longer.

This patch stops lookup_frec() from returning old frecs which
are ready for GC. This helps avoid yet another variation
on the birthday attack, which is no particularly relevant,
given the need for an idle server.
2025-08-21 14:17:01 +01:00
Simon Kelley
b52d1d2017 Tweak get_new_frec() behaviour in "force" mode.
get_new_frec() is not supposed to ever free an frec when
the force arg is set. Make this so. In theory, this fixes
a bug, but it's practically impossible to provoke and I have
no evidence that it has ever happened IRL.
2025-08-21 13:21:27 +01:00
Simon Kelley
18195e7bb2 Useful error logging in dhcp-split-relay code. 2025-08-21 13:05:24 +01:00
Simon Kelley
d81b1d76a0 Fix TFTP problems with large files.
TFTP fails with a timeout when the transfer exceeds 128K blocks.
The block field in an ACK packet is 16 bits, so large transfers
need to deal with this field wrapping. Testing failure meant
that it worked for the first wrap, but not for subsequent ones.

Having fixed the block number problem, file size calculations
accidentally being done in 32 bits not 64 bits then broke
transfers larger than 2G or maybe 4G.

The first problem was probably introduced in commit
ebef27f321 and has not appeared
in a stable release. The second appears to have been there forever.
Clearly, nobody is mad enough to transfer multi-gigabyte files
over TFTP.

Thanks to Jean-François JUBLIN for spotting this and supplying
useful packet dumps that made it easy to diagnose.
2025-08-21 12:49:09 +01:00
Simon Kelley
1677c6e10b More development on dhcp-split-relay. 2025-08-20 12:36:03 +01:00
Simon Kelley
ff30fa4b91 Fix caching bug with negative records.
To provoke this bug, at least the following must be true.

A reply must be a CNAME.
The target of the CNAME must not exist for the queried RR (a No Data reply).
The No Data reply must include an SOA record in the NS section.
The query must take place over TCP.

The result is the caching of a CNAME whose target is the
name of the SOA record, instead of the No data record.

If there is a RR at this target, then a subsequent query for that
RRtype will get a qrong reply.

Thanks to the testers extraordinaire at Pi-Hole for spotting this
and providing enough information to chase it down.
2025-08-11 16:53:40 +01:00
Simon Kelley
c91c66ee63 Get destination address of received DHCPv4 packets right in dumpfile. 2025-07-27 16:23:59 +01:00
Simon Kelley
fc9f6985ab Add --dhcp-split-relay option.
This makes a DHCPv4 relay which is functional when
client and server networks aren't mutually route-able.
2025-07-27 15:27:40 +01:00
Simon Kelley
ea5b0e64e2 Make DHCP6ADVERTISE messages RFC 8415 compliant.
Include IA_* options even if no addresses are allocated, and
always return options specified in ORO.
2025-07-23 21:05:40 +01:00
Simon Kelley
e775264539 Fix wrong status in reply to DHCP6REQUEST.
If we jump to label request_no_address, address_assigned
has an undefined value, and maybe the wrong status is returned.
2025-07-23 21:04:06 +01:00
Simon Kelley
d976d94e3d Implement RFC 6221 para 4.
If the link address in a relayed DHCPv6 message is zero, don't
use it for addresss selection.
2025-07-22 22:09:49 +01:00
Simon Kelley
3034746748 Allow --dhcp-authoritative to control recreation of missing leases in DHCPv6.
Change the behaviour of the DHVPv6 server when a REBIND message
is received but no lease exists. Under these circumstances a new
lease is created _only_ when the --dhcp-authoritative option is
set. This matches the behavior of the DHCPv4 server.
2025-07-22 21:51:47 +01:00
Simon Kelley
3ceea9e755 Fix packet validation in DHCPv6.
Some messages must be sent as multicast.
2025-07-22 20:43:52 +01:00
Geert Stappers
dfcef19fb4 Added a tail to .hmtl files
HTML-files `doc.html` and `setup.html` were missing closing tags.
Also some empty lines removed.

Signed-off-by: Geert Stappers <stappers@stappers.nl>
2025-07-20 15:29:43 +01:00
Simon Kelley
052aa0fcf3 Fix bounds checking in check_ia()
A malformed DHCP request can cause out-bounds memory reads, and probably SEGV.
2025-07-15 15:18:12 +01:00
Simon Kelley
f74b74e521 Refactor relay_reply6().
Traverse the nested data structures more efficiently,
more understandably, and with watertight checking of
buffer boundaries.
2025-07-15 15:17:46 +01:00
Simon Kelley
c9342cb556 Check for constructed name in --srv-host exceeding length limit.
Thanks to Mikhail Dmitrichenko for spotting this.
2025-07-15 15:15:32 +01:00
Simon Kelley
2b844b8c83 Fix BNF from --srv-host in manpage. 2025-07-14 15:15:26 +01:00
Simon Kelley
65e9a8957c Fix order of reads dhcp-hostsfile and /etc/ethers.
Reading /etc/ethers assumes that dhcp-host cofig has already
been read, and that is the case for dhcp-host, but for unknown
reasons, the analogous congiguration is dhcp-hostfile was being
read after /etc/ethers.

Swap the order.

Thanks to Andreas Kuropka for spotting the problem.
2025-07-10 10:58:14 +01:00
Simon Kelley
da868a2fbe Restore bit-rotted warning about non-recursive upstreams. 2025-07-08 16:18:03 +01:00
Simon Kelley
bceb5287b9 Fix FTBFS on MacOS.
Fallout from 98189ff988

Thanks to Travis Rayan for the bug report.
2025-07-07 17:58:41 +01:00
Simon Kelley
84445dec26 Fix server selection for DS queries.
This was mainly done in 57e582492b
This commit extends the new behaviour to cases where DNSSEC
validation is not compiled-in or not enabled.
2025-07-07 17:16:32 +01:00
Rob Gill via Dnsmasq-discuss
c70b92b2a4 Add CG-NAT range to non-globally routed IP space per RFC7793
The Shared address space (100.64.0.0/10) used by CG-NAT, defined in RFC6598 should
not have reverse DNS queries sent to global DNS infrastructure.

Confirmed in RFC7793 and listed on
https://www.iana.org/assignments/locally-served-dns-zones/locally-served-dns-zones.xhtml

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-06-26 13:10:48 +01:00
Simon Kelley
57e582492b Fix to 57f0489f38
When choosing a server to send a DS query to take
account of the need for DS records for a domain
to come from the parent of that domain.
2025-06-25 15:02:47 +01:00
Simon Kelley
ec8f3e65c1 Tweak domain-specific servers and DNSSEC code to treat
servers for plain names (server=//1.2.3.4) as domain-specific.

This means that if we fail to get a DS record for such a query,
it gets given the benefit of the doubt.

Updates 57f0489f38
2025-06-23 21:48:29 +01:00
Simon Kelley
15841f187d Fix issue with fast file-descriptor close on *BSD.
This fixes a problem introduced in 8a5fe8ce6b

On BSD, fdescfs is normally mounted at /dev/fd. However
if it is NOT mounted, devfs creates a directory at /dev/fd
which contains (only) the file descriptors 0,1 and 2.

Under these conditions, opendir() will succeed, and
if we proceed we will fail to close extant
file descriptors which should be closed.

Check that there is a filesystem mounted at /dev/fd
by checking that the device changes between /dev/fd
and /dev. If if doesn't, fall back to the dumb path.

Thanks to Roman Bogorodskiy for spotting the problem
and helping with diagnosis.
2025-06-22 23:04:36 +01:00
Simon Kelley
ade97495e6 Trvial formatting fix. 2025-06-20 17:12:32 +01:00
Simon Kelley
2b19285724 Fix breakage of ipset on *BSD.
Regression introduced in 98189ff988

Thanks to Harper Andrews for spotting this.
2025-06-20 15:48:47 +01:00
Simon Kelley
a444715bf0 Fix FTBFS on Jurassic gcc versions. 2025-06-20 15:27:43 +01:00
Simon Kelley
14e81b6976 Additional sanity check in filter_servers(). 2025-06-06 23:33:25 +01:00
Simon Kelley
6ce7f2d55a Fix regression in 9e67099ce7
Logging queries lost the RRtype.

Revert to e.g. query[A] example.com
2025-06-06 23:29:31 +01:00
Simon Kelley
287d6bc88d Fix 5846f749e5
Not sure how that bug got in there. Very (un)lucky choice
of test data, or last-minute "it'll be fine" modification I guess.

It was late, I can't remember.

Thanks to Dominik Derrigs for spotting the problem.
2025-06-03 22:16:50 +01:00
Simon Kelley
c378d2c1de Fix crash in filter_servers().
The bug occurs when we ask lookup_domain() for a server for a domain
which is not a general upstream server, by setting F_DOMAINSRV in the flags.

If there are no possible servers, because there are no upstream servers
defined (for instance, at startup) then the code steps off the end of an
array and SEGVs.

The bug has been latent for some time, but
3e659bd4ec added a new call to lookup_domain()
which can actually trigger the bug if DNSSEC is enabled and a certain
amount of bad luck ensues.

Thanks to the testers extraordinaire at PiHole for reporting this.
2025-06-02 22:37:08 +01:00
Simon Kelley
5846f749e5 Rewrite chunks of inotify.c to remove a memory leak.
The code is much easier to follow now, this shouldn't
happen again.
2025-05-29 22:44:15 +01:00
Matthias Andree
c9a4240ec4 Unbreak compilation on non-Linux systems (FreeBSD)
and put the void cast of netlink_warn under the same preprocessor
condition as its definition, #ifdef HAVE_LINUX_NETWORK.
2025-05-25 21:15:41 +01:00
Simon Kelley
e7b87dee85 Tftp code tweaks. 2025-05-24 21:15:02 +01:00
41 changed files with 15749 additions and 7362 deletions

View File

@@ -24,7 +24,7 @@ version 2.92
Fix some edge-cases with domains and --address and --server. There
has been some regressions with this in previous releases. This change
fixes the priority order from loqwer to highest as:
fixes the priority order from lower to highest as:
--address with a IPv4 or IPv6 address (as long as the query matches the type)
--address with # for all-zeros, as long as the query is A or AAAA)
--address with no address, which returns NXDOMAIN or NOERROR for all types.
@@ -61,6 +61,21 @@ version 2.92
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
Change the behaviour of the DHCPv6 server when a REBIND message
is received but no lease exists. Under these circumstances a new
lease is created _only_ when the --dhcp-authoritative option is
set. This matches the behavior of the DHCPv4 server.
Add --dhcp-split-relay option. This makes a DHCPv4 relay which
is functional when client and server networks aren't mutually
route-able.
Fix failure to add client MAC address to queries in TCP mode.
The options which cause dnsmasq to decorate a DNS query with the MAC
address on the originating client can fail when the query is sent
using TCP. Thanks to Bruno Ravara for spotting and
characterising this bug.
version 2.91
Fix spurious "resource limit exceeded messages". Thanks to

View File

@@ -27,8 +27,8 @@
# Replies which are not DNSSEC signed may be legitimate, because the domain
# is unsigned, or may be forgeries. Setting this option tells dnsmasq to
# check that an unsigned reply is OK, by finding a secure proof that a DS
# record somewhere between the root and the domain does not exist.
# check that an unsigned reply is OK, by finding a secure proof that a DS
# record somewhere between the root and the domain does not exist.
# The cost of setting this is that even queries in unsigned domains will need
# one or more extra DNS queries to verify.
#dnssec-check-unsigned
@@ -193,11 +193,11 @@
#dhcp-range=1234::2, 1234::500, 64, 12h
# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only
#dhcp-range=1234::, ra-only
# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC algorithm.
#dhcp-range=1234::, ra-names
@@ -220,9 +220,9 @@
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overridden by ra-stateless, ra-names, et al, the router
# Unless overridden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
#enable-ra
@@ -295,11 +295,11 @@
# any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red
# Give a fixed IPv6 address and name to client with
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also that the [] around the IPv6 address are obligatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
@@ -355,7 +355,7 @@
# Send DHCPv6 option. Note [] around IPv6 addresses.
#dhcp-option=option6:dns-server,[1234::77],[1234::88]
# Send DHCPv6 option for namservers as the machine running
# Send DHCPv6 option for namservers as the machine running
# dnsmasq and another.
#dhcp-option=option6:dns-server,[::],[1234::88]
@@ -560,7 +560,7 @@
# Set the DHCP server to enable DHCPv4 Rapid Commit Option per RFC 4039.
# In this mode it will respond to a DHCPDISCOVER message including a Rapid Commit
# option with a DHCPACK including a Rapid Commit option and fully committed address
# and configuration information. This must only be enabled if either the server is
# and configuration information. This must only be enabled if either the server is
# the only server for the subnet, or multiple servers are present and they each
# commit a binding for all clients.
#dhcp-rapid-commit

View File

@@ -95,7 +95,5 @@ a contribution towards my expenses, please use the donation button below.
<input type="image" src="https://www.paypalobjects.com/en_US/GB/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal The safer, easier way to pay online.">
<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">
</form>
</BODY>
</HTML>

View File

@@ -673,7 +673,7 @@ machine on which dnsmasq is running) for each
local machine. Local machines are those in /etc/hosts or with DHCP
leases.
.TP
.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
.B \-W, --srv-host=<_service>.<_prot>[.<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
Return a SRV DNS record. See RFC2782 for details. If not supplied, the
domain defaults to that given by
.B --domain.
@@ -918,7 +918,7 @@ fast.
Versions of dnsmasq prior to 2.80 defaulted to not checking unsigned replies, and used
.B --dnssec-check-unsigned
to switch this on. Such configurations will continue to work as before, but those which used the default of no checking will need to be altered to explicitly select no checking. The new default is because switching off checking for unsigned replies is inherently dangerous. Not only does it open the possiblity of forged replies, but it allows everything to appear to be working even when the upstream namesevers do not support DNSSEC, and in this case no DNSSEC validation at all is occurring.
to switch this on. Such configurations will continue to work as before, but those which used the default of no checking will need to be altered to explicitly select no checking. The new default is because switching off checking for unsigned replies is inherently dangerous. Not only does it open the possibility of forged replies, but it allows everything to appear to be working even when the upstream namesevers do not support DNSSEC, and in this case no DNSSEC validation at all is occurring.
.TP
.B --dnssec-no-timecheck
DNSSEC signatures are only valid for specified time windows, and should be rejected outside those windows. This generates an
@@ -1197,7 +1197,7 @@ the appropriate network part inserted. For IPv6, an address may include a prefix
which (in this case) specifies four addresses, 1234::50 to 1234::53. This (an the ability
to specify multiple addresses) is useful
when a host presents either a consistent name or hardware-ID, but varying DUIDs, since it allows
dnsmasq to honour the static address allocation but assign a different adddress for each DUID. This
dnsmasq to honour the static address allocation but assign a different address for each DUID. This
typically occurs when chain netbooting, as each stage of the chain gets in turn allocates an address.
Note that in IPv6 DHCP, the hardware address may not be
@@ -1347,7 +1347,7 @@ 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.
(There is only one option with a zero length data field currently defined for DHCPv4, 80:rapid commit, so this feature is not very useful in practice). Options for which dnsmasq normally
provides default values can be ommitted by defining the option with no data. These are
provides default values can be omitted by defining the option with no data. These are
netmask, broadcast, router, DNS server, domainname and hostname. Thus, for DHCPv4
.B --dhcp-option = option:router
will result in no router option being sent, rather than the default of the host on which dnsmasq is running. For DHCPv6, the same is true of the options DNS server and refresh time.
@@ -1487,7 +1487,31 @@ DHCPv4 to a DHCPv6 server or vice-versa.
The DHCP relay function for IPv6 includes the ability to snoop
prefix-delegation from relayed DHCP transactions. See
.B --dhcp-script
for details.
for details.
.TP
.B --dhcp-split-relay=<local address>,[<server address>[#<server port>]],<server-facing-interface>|<server-facing-address>
A usefully enhanced version of DHCPv4 relay. IPv4 DHCP normally uses a single address
for two functions; it is used by the DHCP server to determine which network to allocate
an address on, and it is used as the address of the relay to which the server sends packets.
This version of DHCP relay splits these functions. It uses the address of the server-facing relay
interface or a directly-specified address as the address that the server talks to. The address of the client-facing interface
(the first item in the config) is used as to determine the client's subnet. The
local address is also used as server-ID override so that the client always sends requests
via the relay. The effect of this is that server doesn't require
a route to the client network and the clients don't require a route to the server.
The third parameter is mandatory. If it is an interface name it cannot be a wildcard and the same filtering as described in
--dhcp-relay applies; answers from the server must arrve via the specified interface. If the third parameter
is an IP address it must be an address of a local interface which is routable from the server; In this case no filtering
is done, the reply packets can arrive via any route.
If setting up a network where the client networks have limited routing, be careful
about configuring the DHCP server. Dnsmasq, as DHCP server, will set the default route to the
client-facing relay interface unless explicitly configured: that is a sensible default.
The normal default DNS server (the same address as the DHCP server)
will not be appropriate when there is no route bewteen the
two, so this will have to be explicitly configured.
.TP
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
@@ -1648,7 +1672,7 @@ likely to move IP address; for this reason it should not be generally used.
.TP
.B --dhcp-ignore-clid
Dnsmasq is reading 'client identifier' (RFC 2131) option sent by clients
(if available) to identify clients. This allow to serve same IP address
(if available) to identify clients. This allow one to serve same IP address
for a host using several interfaces. Use this option to disable 'client identifier'
reading, i.e. to always identify a host using the MAC address.
.TP
@@ -1739,7 +1763,9 @@ For DHCPv4, it changes the behaviour from strict RFC compliance so that DHCP req
unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances. It also
allows dnsmasq to rebuild its lease database without each client needing to
reacquire a lease, if the database is lost. For DHCPv6 it sets the
reacquire a lease, if the database is lost. For DHCPv6 it controls the same
behaviour as DHCPv4 with missing leases (except for the RFC uncompliance - the
DHCPv6 RFCs allow this behaviour if configured). It also sets the
priority in replies to 255 (the maximum) instead of 0 (the minimum).
.TP
.B --dhcp-rapid-commit
@@ -1927,7 +1953,7 @@ was sent, and the complete pathname of the file.
The "relay-snoop" action is invoked when dnsmasq is configured as a DHCP
relay for DHCPv6 and it relays a prefx delegation to a client. The arguments
are the name of the interface where the client is conected, its (link-local)
are the name of the interface where the client is connected, its (link-local)
address on that interface and the delegated prefix. This information is
sufficient to install routes to the delegated prefix of a router. See
.B --dhcp-relay
@@ -2278,7 +2304,7 @@ therein is updated when dnsmasq receives SIGHUP.
.B \--conf-script=<file>[ <arg]
Execute <file>, and treat what it emits to stdout as the contents of a configuration file.
If the script exits with a non-zero exit code, dnsmasq treats this as a fatal error.
The script can be passed arguments, space seperated from the filename and each other so, for instance
The script can be passed arguments, space separated from the filename and each other so, for instance
.B --conf-dir="/etc/dnsmasq-uncompress-ads /share/ads-domains.gz"
with /etc/dnsmasq-uncompress-ads containing

2870
man/sv/dnsmasq.8 Normal file

File diff suppressed because it is too large Load Diff

1377
po/de.po

File diff suppressed because it is too large Load Diff

1368
po/es.po

File diff suppressed because it is too large Load Diff

1589
po/fi.po

File diff suppressed because it is too large Load Diff

1372
po/fr.po

File diff suppressed because it is too large Load Diff

1381
po/id.po

File diff suppressed because it is too large Load Diff

1589
po/it.po

File diff suppressed because it is too large Load Diff

1380
po/ka.po

File diff suppressed because it is too large Load Diff

1367
po/no.po

File diff suppressed because it is too large Load Diff

1373
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1367
po/ro.po

File diff suppressed because it is too large Load Diff

2979
po/sv.po Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -229,3 +229,5 @@ contents of the cache to the syslog.
<P>For a complete listing of options please take a look at the manpage
dnsmasq(8).
</BODY>
</HTML>

View File

@@ -111,22 +111,26 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
again:
/* If the database is less then INTERVAL old, look in there */
if (difftime(now, last) < INTERVAL)
/* If the database is less then INTERVAL old, look in there.
If we're a child process, we always rely on the existing cache we
inherited from the parent, since we don't have a netlink socket.
*/
if (difftime(now, last) < INTERVAL || daemon->pipe_to_parent != -1)
{
/* addr == NULL -> just make cache up-to-date */
if (!addr)
return 0;
for (arp = arps; arp; arp = arp->next)
{
if (addr->sa.sa_family != arp->family)
continue;
if (arp->family == AF_INET &&
arp->addr.addr4.s_addr != addr->in.sin_addr.s_addr)
continue;
if (arp->family == AF_INET6 &&
!IN6_ARE_ADDR_EQUAL(&arp->addr.addr6, &addr->in6.sin6_addr))
continue;
@@ -141,6 +145,10 @@ int find_mac(union mysockaddr *addr, unsigned char *mac, int lazy, time_t now)
}
}
/* Not in cache in child, no go. */
if (daemon->pipe_to_parent != -1)
return 0;
/* Not found, try the kernel */
if (!updated)
{

View File

@@ -938,15 +938,14 @@ int cache_recv_insert(time_t now, int fd)
if (newc)
{
newc->addr.cname.is_name_ptr = 0;
newc->addr.cname.target.cache = crecp;
if (!crecp)
newc->addr.cname.target.cache = NULL;
else
if (crecp)
{
next_uid(crecp);
newc->addr.cname.target.cache = crecp;
newc->addr.cname.uid = crecp->uid;
}
crecp = newc;
}
}
else
@@ -2360,6 +2359,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = "reply";
else if (flags & F_AUTH)
source = "auth";
else if (flags & F_QUERY)
source = "query";
else if (flags & F_SECSTAT)
{
if (addr && addr->log.ede != EDE_UNSET && option_bool(OPT_EXTRALOG))
@@ -2400,8 +2401,8 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg,
source = "non-query opcode";
name = opcodestring;
}
else if (!(flags & F_AUTH))
source = "query";
else if (type > 0)
source = querystr(source, type);
verb = "from";
}

View File

@@ -290,6 +290,7 @@ HAVE_SOCKADDR_SA_LEN
#define HAVE_BSD_NETWORK
#define HAVE_GETOPT_LONG
#define HAVE_SOCKADDR_SA_LEN
#define NO_IPSET
/* Define before sys/socket.h is included so we get socklen_t */
#define _BSD_SOCKLEN_T_
/* Select the RFC_3542 version of the IPv6 socket API.
@@ -349,7 +350,6 @@ HAVE_SOCKADDR_SA_LEN
#endif
#if !defined(HAVE_LINUX_NETWORK)
#undef HAVE_IPSET
#undef HAVE_NFTSET
#endif
@@ -357,6 +357,16 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_IPSET
#endif
#if defined(HAVE_IPSET)
# if defined(HAVE_LINUX_NETWORK)
# define HAVE_LINUX_IPSET
# elif defined(HAVE_BSD_NETWORK)
# define HAVE_BSD_IPSET
# else
# undef HAVE_IPSET
# endif
#endif
#ifdef NO_LOOP
#undef HAVE_LOOP
#endif

View File

@@ -530,8 +530,8 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
union all_addr addr;
time_t now = dnsmasq_time();
unsigned char dhcp_chaddr[DHCP_CHADDR_MAX];
DBusMessageIter iter, array_iter;
if (!dbus_message_iter_init(message, &iter))
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"Failed to initialize dbus message iter");
@@ -599,6 +599,10 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
if (inet_pton(AF_INET, ipaddr, &addr.addr4))
{
if (!daemon->dhcp)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"DHCPv4 not configured");
if (ia_id != 0 || is_temporary)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"ia_id and is_temporary must be zero for IPv4 lease");
@@ -609,16 +613,25 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6))
{
if (!daemon->doing_dhcp6)
return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
"DHCPv6 not configured");
if (!(lease = lease6_find_by_addr(&addr.addr6, 128, 0)))
lease = lease6_allocate(&addr.addr6,
is_temporary ? LEASE_TA : LEASE_NA);
lease_set_iaid(lease, ia_id);
if (lease)
lease_set_iaid(lease, ia_id);
}
#endif
else
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s'", ipaddr);
if (!lease)
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"unable to allocate lease for IP address '%s'", ipaddr);
hw_len = parse_hex((char*)hwaddr, dhcp_chaddr, DHCP_CHADDR_MAX, NULL, &hw_type);
if (hw_len < 0)
return dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
@@ -641,7 +654,7 @@ static DBusMessage *dbus_add_lease(DBusMessage* message)
static DBusMessage *dbus_del_lease(DBusMessage* message)
{
struct dhcp_lease *lease;
struct dhcp_lease *lease = NULL;
DBusMessageIter iter;
const char *ipaddr;
DBusMessage *reply;
@@ -659,10 +672,10 @@ static DBusMessage *dbus_del_lease(DBusMessage* message)
dbus_message_iter_get_basic(&iter, &ipaddr);
if (inet_pton(AF_INET, ipaddr, &addr.addr4))
if (inet_pton(AF_INET, ipaddr, &addr.addr4) && daemon->dhcp)
lease = lease_find_by_addr(addr.addr4);
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6))
else if (inet_pton(AF_INET6, ipaddr, &addr.addr6) && daemon->doing_dhcp6)
lease = lease6_find_by_addr(&addr.addr6, 128, 0);
#endif
else

View File

@@ -709,7 +709,8 @@ static const struct opttab_t {
{ "client-machine-id", 97, 0 },
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
{ "captive-portal", 114, OT_NAME }, /* RFC 8910 */
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
@@ -751,6 +752,7 @@ static const struct opttab_t opttab6[] = {
{ "ntp-server", 56, 0 /* OT_ADDR_LIST | OT_RFC1035_NAME */ },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ "captive-portal", 103, OT_NAME }, /* RFC 8910 */
{ NULL, 0, 0 }
};
#endif
@@ -1067,10 +1069,12 @@ void log_relay(int family, struct dhcp_relay *relay)
{
if (broadcast)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface);
else if (relay->split_mode)
my_syslog(MS_DHCP | LOG_INFO, _("DHCP split-relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface);
}
else
else
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff);
}

View File

@@ -73,6 +73,7 @@
#define SUBOPT_REMOTE_ID 2
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
#define SUBOPT_FLAGS 10 /* RFC 5010 */
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */

View File

@@ -32,8 +32,6 @@ static int complete_context(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index, char *label,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz);
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface);
static int make_fd(int port)
{
@@ -144,13 +142,14 @@ void dhcp_packet(time_t now, int pxe_fd)
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0, is_inform = 0, loopback = 0;
int rcvd_iface_index;
int rcvd_iface_index, relay_index;
struct in_addr iface_addr;
struct iface_param parm;
time_t recvtime = now;
#ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req;
struct timeval tv;
struct in_addr dst_addr;
#endif
union {
@@ -176,13 +175,11 @@ void dhcp_packet(time_t now, int pxe_fd)
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
return;
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, NULL, fd);
#endif
#if defined (HAVE_LINUX_NETWORK)
if (ioctl(fd, SIOCGSTAMP, &tv) == 0)
recvtime = tv.tv_sec;
dst_addr.s_addr = 0;
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
@@ -194,7 +191,8 @@ void dhcp_packet(time_t now, int pxe_fd)
} p;
p.c = CMSG_DATA(cmptr);
iface_index = p.p->ipi_ifindex;
if (p.p->ipi_addr.s_addr != INADDR_BROADCAST)
dst_addr = p.p->ipi_addr;
if (dst_addr.s_addr != INADDR_BROADCAST)
unicast_dest = 1;
}
@@ -224,7 +222,25 @@ void dhcp_packet(time_t now, int pxe_fd)
iface_index = *(p.i);
}
#endif
#ifdef HAVE_DUMPFILE
union mysockaddr *sockp = NULL;
# ifdef HAVE_LINUX_NETWORK
union mysockaddr tosock;
sockp = &tosock;
tosock.in.sin_port = htons(daemon->dhcp_server_port);
tosock.in.sin_addr = dst_addr;
tosock.sa.sa_family = AF_INET;
# ifdef HAVE_SOCKADDR_SA_LEN
tosock.in.sin_len = sizeof(struct sockaddr_in);
# endif
# endif
dump_packet_udp(DUMP_DHCP, (void *)daemon->dhcp_packet.iov_base, sz, (union mysockaddr *)&dest, sockp, -1);
#endif
if (!indextoname(daemon->dhcpfd, iface_index, ifr.ifr_name) ||
ioctl(daemon->dhcpfd, SIOCGIFFLAGS, &ifr) != 0)
return;
@@ -286,10 +302,10 @@ void dhcp_packet(time_t now, int pxe_fd)
unicast_dest = 1;
#endif
if ((relay = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, ifr.ifr_name)))
if ((relay_index = relay_reply4((struct dhcp_packet *)daemon->dhcp_packet.iov_base, (size_t)sz, ifr.ifr_name)))
{
/* Reply from server, using us as relay. */
rcvd_iface_index = relay->iface_index;
rcvd_iface_index = relay_index;
if (!indextoname(daemon->dhcpfd, rcvd_iface_index, ifr.ifr_name))
return;
is_relay_reply = 1;
@@ -314,9 +330,12 @@ void dhcp_packet(time_t now, int pxe_fd)
if (tmp->name && (tmp->flags & INAME_4) && wildcard_match(tmp->name, ifr.ifr_name))
return;
/* unlinked contexts/relays are marked by context->current == context */
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
for (relay = daemon->relay4; relay; relay = relay->next)
relay->matchcount = 0;
parm.current = NULL;
parm.ind = iface_index;
@@ -344,7 +363,7 @@ void dhcp_packet(time_t now, int pxe_fd)
if (!iface_enumerate(AF_INET, &parm, (callback_t){.af_inet=complete_context}))
return;
relay_upstream4(iface_index, mess, (size_t)sz);
relay_upstream4(iface_addr, iface_index, mess, (size_t)sz, unicast_dest);
/* May have configured relay, but not DHCP server */
if (!daemon->dhcp)
@@ -648,8 +667,19 @@ static int complete_context(struct in_addr local, int if_index, char *label,
}
for (relay = daemon->relay4; relay; relay = relay->next)
if (relay->local.addr4.s_addr == local.s_addr)
relay->iface_index = if_index;
if (!relay->split_mode && relay->local.addr4.s_addr == local.s_addr)
{
if (if_index == param->ind)
relay->iface_index = if_index;
/* More than one interface with the relay address breaks things. */
if (relay->matchcount++ == 1 && !relay->warned)
{
relay->warned = 1;
inet_ntop(AF_INET, &local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP relay address %s appears on more than one interface"), daemon->addrbuff);
}
}
return 1;
}
@@ -666,7 +696,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
if (taddr.s_addr == context->router.s_addr)
if (taddr.s_addr == tmp->router.s_addr)
return NULL;
for (tmp = context; tmp; tmp = tmp->current)
@@ -1091,122 +1121,4 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
static void relay_upstream4(int iface_index, struct dhcp_packet *mess, size_t sz)
{
struct in_addr giaddr = mess->giaddr;
u8 hops = mess->hops;
struct dhcp_relay *relay;
if (mess->op != BOOTREQUEST)
return;
for (relay = daemon->relay4; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
break;
/* No relay config. */
if (!relay)
return;
for (; relay; relay = relay->next)
if (relay->iface_index != 0 && relay->iface_index == iface_index)
{
union mysockaddr to;
union all_addr from;
mess->hops = hops;
mess->giaddr = giaddr;
if ((mess->hops++) > 20)
continue;
/* source address == relay address */
from.addr4 = relay->local.addr4;
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->local.addr4.s_addr)
continue;
}
else
{
/* plug in our address */
mess->giaddr.s_addr = relay->local.addr4.s_addr;
}
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(relay->port);
#ifdef HAVE_SOCKADDR_SA_LEN
to.in.sin_len = sizeof(struct sockaddr_in);
#endif
/* Broadcasting to server. */
if (relay->server.addr4.s_addr == 0)
{
struct ifreq ifr;
if (relay->interface)
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
if (!relay->interface || strchr(relay->interface, '*') ||
ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface);
continue;
}
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
#ifdef HAVE_DUMPFILE
{
union mysockaddr fromsock;
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
fromsock.in.sin_addr = from.addr4;
fromsock.sa.sa_family = AF_INET;
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
}
#endif
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (relay->server.addr4.s_addr == 0)
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
else
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
}
/* restore in case of a local reply. */
mess->giaddr = giaddr;
}
static struct dhcp_relay *relay_reply4(struct dhcp_packet *mess, char *arrival_interface)
{
struct dhcp_relay *relay;
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
return NULL;
for (relay = daemon->relay4; relay; relay = relay->next)
{
if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
{
if (!relay->interface || wildcard_match(relay->interface, arrival_interface))
return relay->iface_index != 0 ? relay : NULL;
}
}
return NULL;
}
#endif

View File

@@ -89,6 +89,7 @@ void dhcp6_init(void)
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct dhcp_relay *relay;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
@@ -176,6 +177,7 @@ void dhcp6_packet(time_t now)
else
{
struct dhcp_bridge *bridge, *alias;
int multicast_dest = 0;
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
@@ -230,14 +232,24 @@ void dhcp6_packet(time_t now)
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
for (relay = daemon->relay6; relay; relay = relay->next)
relay->matchcount = 0;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &all_servers);
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
multicast_dest = 1;
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
if (IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers))
multicast_dest = 1;
else
{
/* Ignore requests sent to the ALL_SERVERS multicast address for relay when
we're listening there for DHCPv6 server reasons. */
if (relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
return;
}
if (!iface_enumerate(AF_INET6, &parm, (callback_t){.af_inet6=complete_context6}))
return;
@@ -266,7 +278,7 @@ void dhcp6_packet(time_t now)
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
port = dhcp6_reply(parm.current, multicast_dest, if_index, ifr.ifr_name, &parm.fallback,
&parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
/* The port in the source address of the original request should
@@ -444,7 +456,17 @@ static int complete_context6(struct in6_addr *local, int prefix,
if (match)
for (relay = daemon->relay6; relay; relay = relay->next)
if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
relay->iface_index = if_index;
{
relay->iface_index = if_index;
/* More than one interface with the relay address breaks things. */
if (relay->matchcount++ == 1 && !relay->warned)
{
relay->warned = 1;
inet_ntop(AF_INET6, &local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP relay address %s appears on more than one interface"), daemon->addrbuff);
}
}
return 1;
}

View File

@@ -81,7 +81,9 @@ int main (int argc, char **argv)
int tftp_prefix_missing = 0;
#endif
#ifdef HAVE_LINUX_NETWORK
(void)netlink_warn;
#endif
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
setlocale(LC_ALL, "");
@@ -125,6 +127,7 @@ int main (int argc, char **argv)
This might be increased is EDNS packet size if greater than the minimum. */
daemon->packet_buff_sz = daemon->edns_pktsz + MAXDNAME + RRFIXEDSZ;
daemon->packet = safe_malloc(daemon->packet_buff_sz);
daemon->pipe_to_parent = -1;
if (option_bool(OPT_EXTRALOG))
daemon->addrbuff2 = safe_malloc(ADDRSTRLEN);
@@ -1078,8 +1081,6 @@ int main (int argc, char **argv)
pid = getpid();
daemon->pipe_to_parent = -1;
#ifdef HAVE_INOTIFY
/* Using inotify, have to select a resolv file at startup */
poll_resolv(1, 0, now);
@@ -1775,9 +1776,9 @@ void clear_cache_and_reload(time_t now)
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
reread_dhcp();
if (option_bool(OPT_ETHERS))
dhcp_read_ethers();
reread_dhcp();
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
@@ -2031,9 +2032,24 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
if (!option_bool(OPT_DEBUG))
{
/* The code in edns0.c qthat decorates queries with the source MAC address depends
on the code in arp.c, which populates a cache with the contents of the ARP table
using netlink. Since the child process can't use netlink, we pre-populate
the cache with the ARP table entry for our source here, including a negative entry
if there is nothing for our address in the ARP table.
When the edns0 code calls find_mac() in the child process, it will
get the correct answer from the cache inherited from the parent
without having to use netlink to consult the kernel ARP table.
edns0_needs_mac() simply calls find_mac if any EDNS0 options
which need a MAC address are enabled. */
edns0_needs_mac(&tcp_addr, now);
if (pipe(pipefd) == -1)
goto closeconandreturn; /* pipe failed */
if ((p = fork()) == -1)
{
/* fork failed */

View File

@@ -1109,7 +1109,7 @@ struct ping_result {
struct tftp_file {
int refcount, fd;
off_t size;
off_t size, posn;
dev_t dev;
ino_t inode;
char filename[];
@@ -1117,13 +1117,14 @@ struct tftp_file {
struct tftp_transfer {
int sockfd;
u16 block_hi, ackprev;
time_t retransmit, start;
unsigned int lastack, block, blocksize, windowsize, timeout, expansion;
off_t offset;
union mysockaddr peer;
union all_addr source;
int if_index;
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, lastcarrylf, backoff;
struct tftp_file *file;
struct tftp_transfer *next;
};
@@ -1144,10 +1145,12 @@ struct dhcp_relay {
union {
struct in_addr addr4;
struct in6_addr addr6;
} local, server;
} local, server, uplink;
char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */
int iface_index; /* working - interface in which requests arrived, for return */
int port; /* Port of relay we forward to. */
int split_mode; /* Split address allocation and relay address. */
int warned, matchcount;
#ifdef HAVE_SCRIPT
struct snoop_record {
struct in6_addr client, prefix;
@@ -1275,7 +1278,7 @@ extern struct daemon {
struct serverfd *sfds;
struct irec *interfaces;
struct listener *listeners;
struct server *srv_save; /* Used for resend on DoD */
void *srv_save; /* Used for resend on DoD and tftp prefetch */
size_t packet_len; /* " " */
int fd_save; /* " " */
pid_t *tcp_pids;
@@ -1666,6 +1669,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
time_t recvtime, struct in_addr leasequery_source);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
void relay_upstream4(struct in_addr iface_addr, int iface_index,
struct dhcp_packet *mess, size_t sz, int unicast);
unsigned int relay_reply4(struct dhcp_packet *mess, size_t sz, char *arrival_interface);
#endif
/* dnsmasq.c */
@@ -1801,7 +1807,7 @@ void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac,
/* rfc3315.c */
#ifdef HAVE_DHCP6
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now);
int relay_upstream6(int iface_index, ssize_t sz, struct in6_addr *peer_address,
@@ -1912,6 +1918,7 @@ unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned char *limit,
int optno, unsigned char *opt, size_t optlen, int set_do, int replace);
size_t add_do_bit(struct dns_header *header, size_t plen, unsigned char *limit);
void edns0_needs_mac(union mysockaddr *addr, time_t now);
size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *limit,
union mysockaddr *source, time_t now, int *cacheable);
int check_source(struct dns_header *header, size_t plen, unsigned char *pseudoheader, union mysockaddr *peer);
@@ -1938,7 +1945,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
char *name, char *limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp);
#endif
void mark_servers(int flag);
void cleanup_servers(void);

View File

@@ -95,6 +95,7 @@ void build_server_array(void)
A flag of F_SERVER returns an upstream server only.
A flag of F_DNSSECOK disables NODOTS servers from consideration.
A flag of F_DS returns parent domain server.
A flag of F_DOMAINSRV returns a domain-specific server only.
A flag of F_CONFIG returns anything that generates a local
reply of IPv4 or IPV6.
@@ -106,12 +107,23 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
ssize_t qlen;
int try, high, low = 0;
int nlow = 0, nhigh = 0;
char *cp, *qdomain = domain;
char *cp, *qdomain;
/* may be no configured servers. */
if (daemon->serverarraysz == 0)
return 0;
/* DS records should come from the parent domain. */
if (flags & F_DS)
{
if ((cp = strchr(domain, '.')))
domain = cp+1;
else
domain = "";
}
qdomain = domain;
/* find query length and presence of '.' */
for (cp = qdomain, nodots = 1, qlen = 0; *cp; qlen++, cp++)
if (*cp == '.')
@@ -244,7 +256,10 @@ int lookup_domain(char *domain, int flags, int *lowout, int *highout)
if (nodots &&
(daemon->serverarray[daemon->serverarraysz-1]->flags & SERV_FOR_NODOTS) &&
(nlow == nhigh || daemon->serverarray[nlow]->domain_len == 0))
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
{
filter_servers(daemon->serverarraysz-1, flags, &nlow, &nhigh);
qlen = 0;
}
if (lowout)
*lowout = nlow;
@@ -343,10 +358,9 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
else
{
/* If we want a server for a particular domain, and this one isn't, return nothing. */
if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
if (nlow < daemon->serverarraysz && nlow != nhigh && (flags & F_DOMAINSRV) &&
daemon->serverarray[nlow]->domain_len == 0 && !(daemon->serverarray[nlow]->flags & SERV_FOR_NODOTS))
nlow = nhigh;
else
nlow = i;
}
}
}
@@ -401,7 +415,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
setup_reply(header, flags, ede);
gotname &= ~F_QUERY;
gotname &= ~(F_QUERY | F_DS);
if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL, 0);
@@ -461,14 +475,14 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
}
#ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp)
int dnssec_server(struct server *server, char *keyname, int is_ds, int *firstp, int *lastp)
{
int first, last, index;
/* Find server to send DNSSEC query to. This will normally be the
same as for the original query, but may be another if
servers for domains are involved. */
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK, &first, &last))
if (!lookup_domain(keyname, F_SERVER | F_DNSSECOK | (is_ds ? F_DS : 0), &first, &last))
return -1;
for (index = first; index != last; index++)

View File

@@ -265,6 +265,15 @@ static void encoder(unsigned char *in, char *out)
out[3] = char64(in[2]);
}
/* This function needs to call find_mac if any option which requires a MAC address is enabled
and used below. If you add a new MAC consumer, modify this, otherwise your
new EDNS0 option won't work in TCP mode. */
void edns0_needs_mac(union mysockaddr *addr, time_t now)
{
if (option_bool(OPT_MAC_B64) || option_bool(OPT_MAC_HEX) || option_bool(OPT_ADD_MAC))
find_mac(addr, NULL, 0, now);
}
/* OPT_ADD_MAC = MAC is added (if available)
OPT_ADD_MAC + OPT_STRIP_MAC = MAC is replaced, if not available, it is only removed
OPT_STRIP_MAC = MAC is removed */
@@ -562,3 +571,4 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
return plen;
}

View File

@@ -17,7 +17,7 @@
#include "dnsmasq.h"
static struct frec *get_new_frec(time_t now, struct server *serv, int force);
static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask);
static struct frec *lookup_frec(time_t now, char *target, int class, int rrtype, int id, int flags, int flagmask);
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server,
@@ -192,7 +192,7 @@ static void forward_query(int udpfd, union mysockaddr *udpaddr,
old_reply = 1;
fwd_flags = forward->flags;
}
else if (gotname && (forward = lookup_frec(daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
else if (gotname && (forward = lookup_frec(now, daemon->namebuff, (int)rrclass, (int)rrtype, -1, fwd_flags,
FREC_CHECKING_DISABLED | FREC_AD_QUESTION | FREC_DO_QUESTION |
FREC_HAS_PHEADER | FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_NO_CACHE)))
{
@@ -763,6 +763,16 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!is_sign && !option_bool(OPT_DNSSEC_PROXY))
header->hb4 &= ~HB4_AD;
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
header->hb4 |= HB4_RA; /* recursion if available */
if (OPCODE(header) != QUERY)
@@ -778,16 +788,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
return resize_packet(header, n, pheader, plen);
}
/* Complain loudly if the upstream server is non-recursive. */
if (!(header->hb4 & HB4_RA) && rcode == NOERROR &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
(void)prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (header->hb3 & HB3_TC)
log_query(F_UPSTREAM, NULL, NULL, "truncated", 0);
else if (!bogusanswer || (header->hb4 & HB4_CD))
@@ -980,7 +980,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
unsigned int flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
struct frec *old;
if ((old = lookup_frec(daemon->keyname, forward->class, -1, -1, flags, flags)))
if ((old = lookup_frec(now, daemon->keyname, forward->class, -1, -1, flags, flags)))
{
/* This is tricky; it detects loops in the dependency
graph for DNSSEC validation, say validating A requires DS B
@@ -1024,7 +1024,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Make sure we don't expire and free the orig frec during the
allocation of a new one: third arg of get_new_frec() does that. */
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, NULL, NULL)) != -1 &&
if ((serverind = dnssec_server(forward->sentto, daemon->keyname, STAT_ISEQUAL(status, STAT_NEED_DS), NULL, NULL)) != -1 &&
(server = daemon->serverarray[serverind]) &&
(nn = dnssec_generate_query(header, ((unsigned char *) header) + daemon->edns_pktsz,
daemon->keyname, forward->class, get_id(),
@@ -1170,7 +1170,7 @@ void reply_query(int fd, time_t now)
GETSHORT(rrtype, p);
GETSHORT(class, p);
if (!(forward = lookup_frec(daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
if (!(forward = lookup_frec(now, daemon->namebuff, class, rrtype, ntohs(header->id), FREC_ANSWER, 0)))
return;
filter_servers(forward->sentto->arrayposn, F_SERVER, &first, &last);
@@ -2199,7 +2199,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
first = start = server->arrayposn;
last = first + 1;
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, &first, &last)) == -1)
if (!STAT_ISEQUAL(status, STAT_OK) && (start = dnssec_server(server, name, STAT_ISEQUAL(status, STAT_NEED_DS), &first, &last)) == -1)
new_status = STAT_ABANDONED;
else
{
@@ -2307,7 +2307,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0,
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
if ((start = dnssec_server(server, keyname, &first, &last)) == -1)
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1)
{
new_status = STAT_ABANDONED;
break;
@@ -3106,7 +3106,7 @@ static void free_frec(struct frec *f)
/* Impose an absolute
limit of 4*TIMEOUT before we wipe things (for random sockets).
If force is set, always return a result, even if we have
to allocate above the limit, and don'y free any records.
to allocate above the limit, and don't free any records.
This is set when allocating for DNSSEC to avoid cutting off
the branch we are sitting on. */
static struct frec *get_new_frec(time_t now, struct server *master, int force)
@@ -3128,36 +3128,40 @@ static struct frec *get_new_frec(time_t now, struct server *master, int force)
/* Don't free DNSSEC sub-queries here, as we may end up with
dangling references to them. They'll go when their "real" query
is freed. */
if (!f->dependent && !force)
if (!f->dependent)
#endif
{
if (difftime(now, f->time) >= 4*TIMEOUT)
{
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(f);
target = f;
}
else if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
}
if (!force)
{
if (difftime(now, f->time) >= 4*TIMEOUT)
{
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(f);
target = f;
}
else if (!oldest || difftime(f->time, oldest->time) <= 0)
oldest = f;
}
}
if (f->sentto && ((int)difftime(now, f->time)) < TIMEOUT && server_samegroup(f->sentto, master))
count++;
}
if (!force && count >= daemon->ftabsize)
{
query_full(now, master->domain);
return NULL;
}
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* can't find empty one, use oldest if there is one and it's older than timeout */
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(oldest);
target = oldest;
if (!force)
{
if (count >= daemon->ftabsize)
{
query_full(now, master->domain);
return NULL;
}
if (!target && oldest && ((int)difftime(now, oldest->time)) >= TIMEOUT)
{
/* can't find empty one, use oldest if there is one and it's older than timeout */
daemon->metrics[METRIC_DNS_UNANSWERED_QUERY]++;
free_frec(oldest);
target = oldest;
}
}
if (!target && (target = (struct frec *)whine_malloc(sizeof(struct frec))))
@@ -3192,7 +3196,7 @@ static void query_full(time_t now, char *domain)
}
}
static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int flags, int flagmask)
static struct frec *lookup_frec(time_t now, char *target, int class, int rrtype, int id, int flags, int flagmask)
{
struct frec *f;
struct dns_header *header;
@@ -3242,7 +3246,14 @@ static struct frec *lookup_frec(char *target, int class, int rrtype, int id, int
continue;
}
/* frecs older than this will get garbage-collected in
get_new_frec(), so don't return them here, so we have
consistent behaviour from an idle dnsmasq which
is not calling get_new_frec() often. */
if (difftime(now, f->time) >= 4*TIMEOUT)
return NULL;
return f;
}

View File

@@ -133,29 +133,44 @@ void inotify_dnsmasq_init()
}
}
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *file)
{
/* Check if this file is already known in dd->files */
struct hostsfile *ah = NULL;
for(ah = dd->files; ah; ah = ah->next)
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
struct hostsfile *ah;
size_t dirlen = strlen(dd->dname);
/* ah->fname always starts with the string in dd->dname */
for (ah = dd->files; ah; ah = ah->next)
if (ah->fname[dirlen] == '/' &&
strcmp(&ah->fname[dirlen+1], file) == 0)
return ah;
/* Not known, create new hostsfile record for this dyndir */
struct hostsfile *newah = NULL;
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
return NULL;
if ((ah = whine_malloc(sizeof(struct hostsfile))))
{
char *path;
/* Add this file to the tip of the linked list */
newah->next = dd->files;
dd->files = newah;
/* Copy flags, set index and the full file path */
newah->flags = dd->flags;
newah->index = daemon->host_index++;
newah->fname = path;
return newah;
if (!(path = whine_malloc(dirlen + strlen(file) + 2)))
{
free(ah);
return NULL;
}
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, file);
/* Add this file to the tip of the linked list */
ah->next = dd->files;
dd->files = ah;
/* Copy flags, set index and the full file path */
ah->flags = dd->flags;
ah->index = daemon->host_index++;
ah->fname = path;
}
return ah;
}
@@ -204,10 +219,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(dd->dname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
@@ -215,33 +228,36 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
if (dd->flags & AH_HOSTS)
{
struct hostsfile *ah;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
if (!(ah = dyndir_addhosts(dd, path)))
{
free(path);
continue;
}
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (dd->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
if ((ah = dyndir_addhosts(dd, ent->d_name)) &&
stat(ah->fname, &buf) != -1 && S_ISREG(buf.st_mode))
total_size = read_hostsfile(ah->fname, ah->index, total_size, rhash, revhashsz);
}
#ifdef HAVE_DHCP
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
{
char *path;
if ((path = whine_malloc(strlen(dd->dname) + lenfile + 2)))
{
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
option_read_dynfile(path, dd->flags);
#endif
free(path);
}
}
#endif
}
closedir(dir_stream);
}
}
@@ -285,50 +301,27 @@ int inotify_check(time_t now)
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
if (dd->wd == in->wd)
{
size_t lendir = strlen(dd->dname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
if (dd->flags & AH_HOSTS)
{
struct hostsfile *ah = NULL;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if (dd->flags & AH_HOSTS)
struct hostsfile *ah;
if ((ah = dyndir_addhosts(dd, in->name)))
{
if ((ah = dyndir_addhosts(dd, path)))
{
const unsigned int removed = cache_remove_uid(ah->index);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
const unsigned int removed = cache_remove_uid(ah->index);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(path, ah->index, 0, NULL, 0);
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), ah->fname);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), ah->fname);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, ah->fname);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(ah->fname, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
}
#ifdef HAVE_DHCP
else if (dd->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
@@ -336,17 +329,43 @@ int inotify_check(time_t now)
lease_update_file(now);
lease_update_dns(1);
}
}
else if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
if (!ah)
free(path);
}
}
#ifdef HAVE_DHCP
else if (!(in->mask & IN_DELETE))
{
char *path;
if ((path = whine_malloc(strlen(dd->dname) + in->len + 2)))
{
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if ((dd->flags & AH_DHCP_HST) && option_read_dynfile(path, AH_DHCP_HST))
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
free(path);
}
}
#endif
}
}
}
return hit;
}

View File

@@ -16,7 +16,7 @@
#include "dnsmasq.h"
#if defined(HAVE_IPSET)
#if defined(HAVE_LINUX_IPSET)
#include <string.h>
#include <errno.h>

View File

@@ -165,6 +165,13 @@ int iface_enumerate(int family, void *parm, callback_t callback)
struct rtgenmsg g;
} req;
/* The netlink socket is not available in child processes. */
if (daemon->pipe_to_parent != -1)
{
my_syslog(LOG_ERR, _("BUG: called iface_enumerate() in child process"));
return 0;
}
memset(&req, 0, sizeof(req));
memset(&addr, 0, sizeof(addr));

View File

@@ -196,6 +196,7 @@ struct myoption {
#define LOPT_NO_ENCODE 387
#define LOPT_DO_ENCODE 388
#define LOPT_LEASEQUERY 389
#define LOPT_SPLIT_RELAY 390
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -374,6 +375,7 @@ static const struct myoption opts[] =
{ "dnssec-timestamp", 1, 0, LOPT_DNSSEC_STAMP },
{ "dnssec-limits", 1, 0, LOPT_DNSSEC_LIMITS },
{ "dhcp-relay", 1, 0, LOPT_RELAY },
{ "dhcp-split-relay", 1, 0, LOPT_SPLIT_RELAY },
{ "ra-param", 1, 0, LOPT_RA_PARAM },
{ "quiet-dhcp", 0, 0, LOPT_QUIET_DHCP },
{ "quiet-dhcp6", 0, 0, LOPT_QUIET_DHCP6 },
@@ -542,6 +544,7 @@ static struct {
{ LOPT_GEN_NAMES, ARG_DUP, "[=tag:<tag>]", gettext_noop("Generate hostnames based on MAC address for nameless clients."), NULL},
{ LOPT_PROXY, ARG_DUP, "[=<ipaddr>]...", gettext_noop("Use these DHCP relays as full proxies."), NULL },
{ LOPT_RELAY, ARG_DUP, "<local-addr>,<server>[,<iface>]", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_SPLIT_RELAY, ARG_DUP, "<local-addr>,<server>,<iface>", gettext_noop("Relay DHCP requests to a remote server"), NULL},
{ LOPT_CNAME, ARG_DUP, "<alias>,<target>[,<ttl>]", gettext_noop("Specify alias name for LOCAL DNS name."), NULL },
{ LOPT_PXE_PROMT, ARG_DUP, "<prompt>,[<timeout>]", gettext_noop("Prompt to send to PXE clients."), NULL },
{ LOPT_PXE_SERV, ARG_DUP, "<service>", gettext_noop("Boot service for PXE menu."), NULL },
@@ -2917,13 +2920,17 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
#ifdef HAVE_DHCP
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
# if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
# endif
case LOPT_LEASEQUERY:
set_option_bool(OPT_LEASEQUERY);
if (!arg)
break;
#pragma GCC diagnostic pop
# if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
# pragma GCC diagnostic pop
# endif
#endif
case 'B': /* --bogus-nxdomain */
case LOPT_IGNORE_ADDR: /* --ignore-address */
@@ -4712,11 +4719,21 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break;
case LOPT_RELAY: /* --dhcp-relay */
case LOPT_SPLIT_RELAY: /* --dhcp-splt-relay */
{
struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay));
char *two = split(arg);
char *three = split(two);
if (option == LOPT_SPLIT_RELAY)
{
new->split_mode = 1;
/* split mode must have two addresses and a non-wildcard interface name. */
if (!three || strchr(three, '*'))
two = NULL;
}
new->iface_index = 0;
if (two)
@@ -4739,12 +4756,15 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
else
three = two;
}
else if (new->split_mode && inet_pton(AF_INET, three, &new->uplink))
/* Third arg in split mode can be an address. */
three = NULL;
new->next = daemon->relay4;
daemon->relay4 = new;
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, arg, &new->local))
else if (inet_pton(AF_INET6, arg, &new->local) && !new->split_mode)
{
char *hash = split_chr(two, '#');
@@ -4765,7 +4785,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
daemon->relay6 = new;
}
#endif
else
two = NULL;
new->interface = opt_string_alloc(three);
}
@@ -6258,6 +6280,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
strchr(srv->name, '.') &&
strchr(srv->name, '.') == strrchr(srv->name, '.'))
{
if (strlen(srv->name) + 1 + strlen(daemon->domain_suffix) > MAXDNAME)
die(_("srv-host name %s too long after domain appended"), srv->name, EC_MISC);
strcpy(buff, srv->name);
strcat(buff, ".");
strcat(buff, daemon->domain_suffix);

View File

@@ -415,6 +415,7 @@ int private_net(struct in_addr addr, int ban_localhost)
(((ip_addr & 0xFF000000) == 0x7F000000) && ban_localhost) /* 127.0.0.0/8 (loopback) */ ||
(((ip_addr & 0xFF000000) == 0x00000000) && ban_localhost) /* RFC 5735 section 3. "here" network */ ||
((ip_addr & 0xFF000000) == 0x0A000000) /* 10.0.0.0/8 (private) */ ||
((ip_addr & 0xFFC00000) == 0x64400000) /* 100.64.0.0/10 (CG-NAT) RFC6598/RFC7793*/ ||
((ip_addr & 0xFFF00000) == 0xAC100000) /* 172.16.0.0/12 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xC0A80000) /* 192.168.0.0/16 (private) */ ||
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ||
@@ -515,7 +516,7 @@ int do_doctor(struct dns_header *header, size_t qlen, char *namebuff)
Cache said SOA and return the difference in length between name and the name of the
SOA RR so we can look it up again.
*/
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int no_cache, time_t now)
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *substring, unsigned long *ttlp, int cache, time_t now)
{
unsigned char *p, *psave;
int qtype, qclass, rdlen;
@@ -555,7 +556,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
{
int prefix = name_len - soa_len;
if (!no_cache)
if (cache)
{
if (!(addr.rrblock.rrdata = blockdata_alloc(NULL, 0)))
return 0;
@@ -567,12 +568,12 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
{
if (!extract_name(header, qlen, &p, daemon->workspacename, EXTR_NAME_EXTRACT, 0))
{
if (!no_cache)
if (cache)
blockdata_free(addr.rrblock.rrdata);
return 0;
}
if (!no_cache)
if (cache)
{
len = to_wire(daemon->workspacename);
if (!blockdata_expand(addr.rrblock.rrdata, addr.rrblock.datalen, daemon->workspacename, len))
@@ -587,13 +588,13 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *sub
if (!CHECK_LEN(header, p, qlen, 20))
{
if (!no_cache)
if (cache)
blockdata_free(addr.rrblock.rrdata);
return 0;
}
/* rest of RR */
if (!no_cache)
if (cache)
{
int secflag = 0;
@@ -1078,7 +1079,17 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (insert && !option_bool(OPT_NO_NEG))
{
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
/* The order of records going into the cache matters (see cache_recv_insert()).
The target of a CNAME must immediately follow the CNAME.
(CNAME has already gone into the cache at this point)
Here we call find_soa to get the ttl and substring, but
we DON'T LET IT INSERT the SOA into the cache if our negative record is a CNAME target
so that the SOA doesn't come before the CNAME target.
We call find_soa() again after inserting the CNAME target to insert the SOA
if necessary. */
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, cpp == NULL, now);
if (have_soa || daemon->neg_ttl)
{
@@ -1100,6 +1111,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
next_uid(newc);
cpp->addr.cname.target.cache = newc;
cpp->addr.cname.uid = newc->uid;
/* we didn't insert the SOA before a CNAME target above, do it now. */
if (have_soa)
find_soa(header, qlen, name, NULL, NULL, 1, now);
}
}
}
@@ -1244,12 +1259,10 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_IPV4 | F_IPV6;
}
#ifdef HAVE_DNSSEC
/* Make the behaviour for DS and DNSKEY queries we forward the same
as for DS and DNSKEY queries we originate. */
if (option_bool(OPT_DNSSEC_VALID) && (qtype == T_DS || qtype == T_DNSKEY))
return F_DNSSECOK;
#endif
if (qtype == T_DS || qtype == T_DNSKEY)
return F_DNSSECOK | (qtype == T_DS ? F_DS : 0);
return F_QUERY;
}

View File

@@ -204,6 +204,10 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
memcpy(agent_id, opt, total);
}
/* look for RFC5010 flags sub-option */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_FLAGS, INADDRSZ)))
unicast_dest = !!(option_uint(opt, 0, 1) & 0x80);
/* look for RFC3527 Link selection sub-option */
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_SUBNET_SELECT, INADDRSZ)))
subnet_addr = option_addr(sopt);
@@ -289,6 +293,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
{
addr = subnet_addr;
force = 1;
if (mess->giaddr.s_addr)
via_relay = 1;
}
else if (mess->giaddr.s_addr)
{
@@ -357,7 +363,12 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
if (context_tmp->local.s_addr == 0)
context_tmp->local = fallback;
if (context_tmp->router.s_addr == 0 && !share)
context_tmp->router = mess->giaddr;
{
if (override.s_addr)
context_tmp->router = override;
else
context_tmp->router = mess->giaddr;
}
/* fill in missing broadcast addresses for relayed ranges */
if (!(context_tmp->flags & CONTEXT_BRDCAST) && context_tmp->broadcast.s_addr == 0 )
@@ -3044,4 +3055,211 @@ static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid)
}
}
void relay_upstream4(struct in_addr iface_addr, int iface_index, struct dhcp_packet *mess, size_t sz, int unicast)
{
struct in_addr giaddr = mess->giaddr;
u8 hops = mess->hops;
struct dhcp_relay *relay;
size_t orig_sz = sz;
unsigned char *endopt = NULL;
if (mess->op != BOOTREQUEST || (mess->hops++) > 20)
return;
for (relay = daemon->relay4; relay; relay = relay->next)
{
union mysockaddr to;
union all_addr from;
struct ifreq ifr;
/* restore orig packet */
mess->giaddr = giaddr;
if (endopt)
*endopt = OPTION_END;
sz = orig_sz;
if (relay->interface)
{
safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;
}
if (!relay->split_mode && relay->iface_index && relay->iface_index == iface_index)
{
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->local.addr4.s_addr)
continue;
}
else
/* plug in our address */
mess->giaddr = relay->local.addr4;
from.addr4 = relay->local.addr4;
}
else if (relay->split_mode && relay->local.addr4.s_addr == iface_addr.s_addr)
{
/* Split mode. We put our address on the server-facing interface
or a directly specified third address into giaddr for the server to talk back to us on.
Our address on client-facing interface goes into agent-id subnet-selector subopt,
so that the server allocates the correct address. We also send a
remote-id with the interface on which the request arrived,
so that we can send the reply back the same way. */
unsigned int net_index = htonl(iface_index);
if (relay->interface)
{
/* get our address on the server-facing interface. */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot send to server via interface %s: %s"), relay->interface, strerror(errno));
continue;
}
relay->uplink.addr4 = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
/* already gatewayed ? */
if (giaddr.s_addr)
{
/* if so check if by us, to stomp on loops. */
if (giaddr.s_addr == relay->uplink.addr4.s_addr)
continue;
}
else
{
/* giaddr is our address on the outgoing interface in split mode. */
mess->giaddr = relay->uplink.addr4;
if (!endopt)
{
/* Add an RFC3026 relay agent information option (2 bytes) at the very end of the options.
Said option to contain a RFC 3527 link selection sub option (6 bytes) and
RFC 5017 serverid-override option (6 bytes) and RFC5010 flags (3 bytes) and
an RFC3046 remote-id which holds an interface index (6 bytes)
New END option is a 24th byte, so we need 24 bytes free.
We only need to do this once, and poke the address/interface/flags into the same place each time. */
if (!(endopt = option_find1((&mess->options[0] + sizeof(u32)), ((unsigned char *)mess) + sz, OPTION_END, 0)) ||
(endopt + 24 > (unsigned char *)(mess + 1)))
continue;
endopt[1] = 21; /* length */
endopt[2] = SUBOPT_SUBNET_SELECT;
endopt[3] = 4; /* length */
endopt[8] = SUBOPT_SERVER_OR;
endopt[9] = 4;
endopt[14] = SUBOPT_FLAGS;
endopt[15] = 1; /* length */
endopt[17] = SUBOPT_REMOTE_ID;
endopt[18] = 4; /* length */
endopt[23] = OPTION_END;
sz = (endopt - (unsigned char *)mess) + 24;
}
/* IP address is already in network byte order */
memcpy(&endopt[4], &relay->local.addr4.s_addr, INADDRSZ);
memcpy(&endopt[10], &relay->local.addr4.s_addr, INADDRSZ);
endopt[16] = unicast ? 0x80 : 0x00;
memcpy(&endopt[19], &net_index, 4);
endopt[0] = OPTION_AGENT_ID;
}
from.addr4 = relay->uplink.addr4;
}
else
continue;
to.sa.sa_family = AF_INET;
to.in.sin_addr = relay->server.addr4;
to.in.sin_port = htons(relay->port);
#ifdef HAVE_SOCKADDR_SA_LEN
to.in.sin_len = sizeof(struct sockaddr_in);
#endif
/* Broadcasting to server. */
if (relay->server.addr4.s_addr == 0)
{
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1)
{
my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s: %s"), relay->interface, strerror(errno));
continue;
}
to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
}
#ifdef HAVE_DUMPFILE
{
union mysockaddr fromsock;
fromsock.in.sin_port = htons(daemon->dhcp_server_port);
fromsock.in.sin_addr = from.addr4;
fromsock.sa.sa_family = AF_INET;
dump_packet_udp(DUMP_DHCP, (void *)mess, sz, &fromsock, &to, -1);
}
#endif
send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0);
if (option_bool(OPT_LOG_OPTS))
{
inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN);
if (relay->server.addr4.s_addr == 0)
snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface);
else
inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ);
my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2);
}
}
/* restore in case of a local reply. */
mess->hops = hops;
mess->giaddr = giaddr;
if (endopt)
*endopt = OPTION_END;
}
unsigned int relay_reply4(struct dhcp_packet *mess, size_t sz, char *arrival_interface)
{
struct dhcp_relay *relay;
if (mess->giaddr.s_addr == 0 || mess->op != BOOTREPLY)
return 0;
for (relay = daemon->relay4; relay; relay = relay->next)
{
unsigned int return_iface = 0;
if (relay->split_mode)
{
unsigned char *opt, *sopt;
/* giaddr is our address on the returning interface in split mode. */
if (mess->giaddr.s_addr == relay->uplink.addr4.s_addr &&
(opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
{
if ((sopt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_REMOTE_ID, sizeof(unsigned int))))
return_iface = option_uint(sopt, 0, sizeof(unsigned int));
/* delete agent info before return RFC 3046 para 2.1 */
*opt = OPTION_END;
memset(opt + 1, 0, option_len(opt) + 2);
}
}
else if (mess->giaddr.s_addr == relay->local.addr4.s_addr)
return_iface = relay->iface_index;
if (return_iface && (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
return return_iface;
}
return 0;
}
#endif /* HAVE_DHCP */

View File

@@ -21,7 +21,7 @@
struct state {
unsigned char *clid;
int clid_len, ia_type, interface, hostname_auth, lease_allocate;
int multicast_dest, clid_len, ia_type, interface, hostname_auth, lease_allocate;
char *client_hostname, *hostname, *domain, *send_domain;
struct dhcp_context *context;
struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
@@ -68,7 +68,7 @@ static void calculate_times(struct dhcp_context *context, unsigned int *min_time
#define opt6_user_vendor_next(opt, end) (opt6_next(((uint8_t *) opt) - 2, end))
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
unsigned short dhcp6_reply(struct dhcp_context *context, int multicast_dest, int interface, char *iface_name,
struct in6_addr *fallback, struct in6_addr *ll_addr, struct in6_addr *ula_addr,
size_t sz, struct in6_addr *client_addr, time_t now)
{
@@ -87,6 +87,7 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if
reset_counter();
state.context = context;
state.multicast_dest = multicast_dest;
state.interface = interface;
state.iface_name = iface_name;
state.fallback = fallback;
@@ -244,7 +245,11 @@ static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t
struct in6_addr align;
/* the packet data is unaligned, copy to aligned storage */
memcpy(&align, inbuff + 2, IN6ADDRSZ);
state->link_address = &align;
/* RFC6221 para 4 */
if (!IN6_IS_ADDR_UNSPECIFIED(&align))
state->link_address = &align;
/* zero is_unicast since that is now known to refer to the
relayed packet, not the original sent by the client */
if (!dhcp6_maybe_relay(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
@@ -333,20 +338,24 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
else if (msg_type != DHCP6IREQ)
return 0;
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
have a server-id. 3315 para 15.x */
opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1);
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND)
if (msg_type == DHCP6SOLICIT || msg_type == DHCP6CONFIRM || msg_type == DHCP6REBIND || msg_type == DHCP6IREQ)
{
if (opt)
/* Above message types must be multicast 3315 Section 15. */
if (!state->multicast_dest)
return 0;
}
else if (msg_type == DHCP6IREQ)
{
/* If server-id provided, it must match. */
if (opt && (opt6_len(opt) != daemon->duid_len ||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
/* server-id must match except for SOLICIT, CONFIRM and REBIND messages, which MUST NOT
have a server-id. 3315 para 15.x */
if (msg_type == DHCP6IREQ)
{
/* If server-id provided in IREQ, it must match. */
if (opt && (opt6_len(opt) != daemon->duid_len ||
memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
return 0;
}
else if (opt)
return 0;
}
else
@@ -634,7 +643,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
case DHCP6SOLICIT:
{
int address_assigned = 0;
int address_assigned;
/* tags without all prefix-class tags */
struct dhcp_netid *solicit_tags;
struct dhcp_context *c;
@@ -653,6 +662,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
request_no_address:
solicit_tags = tagif;
address_assigned = 0;
if (ignore)
return 0;
@@ -764,15 +774,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
if (address_assigned != 1)
{
/* If the server will not assign any addresses to any IAs in a
subsequent Request from the client, the server MUST send an Advertise
message to the client that doesn't include any IA options. */
if (!state->lease_allocate)
{
save_counter(o);
continue;
}
/* If the server cannot assign any addresses to an IA in the message
from the client, the server MUST include the IA in the Reply message
with no addresses in the IA and a Status Code option in the IA
@@ -799,7 +800,6 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
o = new_opt6(OPTION6_PREFERENCE);
put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
end_opt6(o);
tagif = add_options(state, 0);
}
else
{
@@ -819,7 +819,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
break;
}
}
tagif = add_options(state, 0);
break;
}
@@ -977,8 +978,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *inbu
{
if (msg_type == DHCP6REBIND)
{
/* When rebinding, we can create a lease if it doesn't exist. */
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
/* When rebinding, we can create a lease if it doesn't exist, as long
as --dhcp-authoritative is set. */
if (option_bool(OPT_AUTHORITATIVE))
lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
if (lease)
lease_set_iaid(lease, state->iaid);
else
@@ -1593,9 +1596,14 @@ static void get_context_tag(struct state *state, struct dhcp_context *context)
static int check_ia(struct state *state, void *opt, void **endp, void **ia_option)
{
state->ia_type = opt6_type(opt);
*ia_option = NULL;
/* must be a minimal option to check without stepping outside received packet. */
if (opt6_ptr(opt, 4) > state->end)
return 0;
state->ia_type = opt6_type(opt);
if (state->ia_type != OPTION6_IA_NA && state->ia_type != OPTION6_IA_TA)
return 0;
@@ -1605,7 +1613,10 @@ static int check_ia(struct state *state, void *opt, void **endp, void **ia_optio
if (state->ia_type == OPTION6_IA_TA && opt6_len(opt) < 4)
return 0;
*endp = opt6_ptr(opt, opt6_len(opt));
/* Check we don't overflow the received packet. */
if ((*endp = opt6_ptr(opt, opt6_len(opt))) > state->end)
return 0;
state->iaid = opt6_uint(opt, 0, 4);
*ia_option = opt6_find(opt6_ptr(opt, state->ia_type == OPTION6_IA_NA ? 12 : 4), *endp, OPTION6_IAADDR, 24);
@@ -2276,64 +2287,61 @@ int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
{
void *opt, *opts = inbuff + 34;
void *end = inbuff + sz;
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
{
int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* decapsulate relayed message */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opts; opt; opt = opt6_next(opt, end))
if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12)
{
void *ia_opts = opt6_ptr(opt, 12);
void *ia_end = opt6_ptr(opt, opt6_len(opt));
void *ia_opt;
for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
/* valid lifetime must not be zero. */
if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(ia_opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
if ((opt = opt6_find(opts, end, OPTION6_RELAY_MSG, 4)))
{
int encap_type = opt6_uint(opt, 0, 1);
put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ);
peer->sin6_scope_id = relay->iface_index;
if (encap_type == DHCP6RELAYREPL)
{
peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
return 1;
}
peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
#ifdef HAVE_SCRIPT
if (daemon->lease_change_command && encap_type == DHCP6REPLY)
{
/* skip over message type and transaction-id. to get to options. */
opts = opt6_ptr(opt, 4);
end = opt6_ptr(opt, opt6_len(opt));
if ((opt = opt6_find(opts, end, OPTION6_IA_PD, 12)))
{
opts = opt6_ptr(opt, 12);
end = opt6_ptr(opt, opt6_len(opt));
for (opt = opt6_find(opts, end, OPTION6_IAPREFIX, 25); opt; opt = opt6_find(opt6_next(opt, end), end, OPTION6_IAPREFIX, 25))
/* valid lifetime must not be zero. */
if (opt6_uint(opt, 4, 4) != 0)
{
if (daemon->free_snoops ||
(daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
{
struct snoop_record *snoop = daemon->free_snoops;
daemon->free_snoops = snoop->next;
snoop->client = peer->sin6_addr;
snoop->prefix_len = opt6_uint(opt, 8, 1);
memcpy(&snoop->prefix, opt6_ptr(opt, 9), IN6ADDRSZ);
snoop->next = relay->snoop_records;
relay->snoop_records = snoop;
}
}
}
}
#endif
return 1;
}
}
return 0;
}
#ifdef HAVE_SCRIPT
int do_snoop_script_run(void)
{

View File

@@ -18,7 +18,7 @@
#include "dnsmasq.h"
#if defined(HAVE_IPSET) && defined(HAVE_BSD_NETWORK)
#if defined(HAVE_BSD_IPSET)
#include <string.h>

View File

@@ -18,12 +18,12 @@
#ifdef HAVE_TFTP
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client);
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len);
static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2);
static ssize_t tftp_err_oops(char *packet, const char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static ssize_t get_block(struct tftp_transfer *transfer);
static char *next(char **p, char *end);
static void sanitise(char *buf);
@@ -41,10 +41,9 @@ static void sanitise(char *buf);
#define ERR_ILL 4
#define ERR_TID 5
static void tftp_request(struct listener *listen, time_t now)
static void tftp_request(char *packet, ssize_t plen, struct listener *listen, time_t now)
{
ssize_t len;
char *packet = daemon->packet;
char *filename, *mode, *p, *end;
union mysockaddr addr, peer;
struct msghdr msg;
@@ -87,12 +86,10 @@ static void tftp_request(struct listener *listen, time_t now)
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* packet buff is DNS name workspace. */
iov.iov_base = packet;
iov.iov_len = daemon->packet_buff_sz;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
iov.iov_len = plen;
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
@@ -198,11 +195,11 @@ static void tftp_request(struct listener *listen, time_t now)
name = namebuff;
addra.addr4 = addr.in.sin_addr;
if (family == AF_INET6)
addra.addr6 = addr.in6.sin6_addr;
else
addra.addr4 = addr.in.sin_addr;
if (daemon->tftp_interfaces)
{
/* dedicated tftp interface list */
@@ -267,7 +264,7 @@ static void tftp_request(struct listener *listen, time_t now)
}
else
{
handle_tftp(now, transfer, len);
handle_tftp(packet, now, transfer, len);
return;
}
}
@@ -326,6 +323,8 @@ static void tftp_request(struct listener *listen, time_t now)
transfer->start = now;
transfer->backoff = 1;
transfer->block = 1;
transfer->ackprev = 0;
transfer->block_hi = 0;
transfer->blocksize = 512;
transfer->windowsize = 1;
@@ -499,17 +498,21 @@ static void tftp_request(struct listener *listen, time_t now)
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
if ((transfer->file = check_tftp_fileperm(packet, &len, prefix, daemon->addrbuff)))
{
transfer->lastack = transfer->block;
transfer->retransmit = now + transfer->timeout;
/* This packet is may be the first data packet, but only if windowsize == 1
To get windowsize greater then one requires an option negotiation,
in which case this packet is the OACK. */
if ((len = get_block(packet, transfer)) == -1)
if ((len = get_block(transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
else
is_err = 0;
{
is_err = 0;
/* get_block put the packet to send in a different buffer. */
packet = daemon->packet;
}
}
}
}
@@ -532,9 +535,9 @@ static void tftp_request(struct listener *listen, time_t now)
}
}
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client)
static struct tftp_file *check_tftp_fileperm(char *packet, ssize_t *len, char *prefix, char *client)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
char *namebuff = daemon->namebuff;
struct tftp_file *file;
struct tftp_transfer *t;
uid_t uid = geteuid();
@@ -597,6 +600,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
file->size = statbuf.st_size;
file->dev = statbuf.st_dev;
file->inode = statbuf.st_ino;
file->posn = 0;
file->refcount = 1;
strcpy(file->filename, namebuff);
return file;
@@ -616,12 +620,15 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *c
void check_tftp_listeners(time_t now)
{
/* Use workspace to receive (small) request/ACK, to avoid overwriting precomputed reply */
char *packet = daemon->workspacename;
ssize_t plen = MAXDNAME * 2;
struct listener *listener;
struct tftp_transfer *transfer, *tmp, **up;
for (listener = daemon->listeners; listener; listener = listener->next)
if (listener->tftpfd != -1 && poll_check(listener->tftpfd, POLLIN))
tftp_request(listener, now);
tftp_request(packet, plen, listener, now);
/* In single port mode, all packets come via port 69 and tftp_request() */
if (!option_bool(OPT_SINGLE_PORT))
@@ -632,26 +639,23 @@ void check_tftp_listeners(time_t now)
socklen_t addr_len = sizeof(union mysockaddr);
ssize_t len;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
if ((len = recvfrom(transfer->sockfd, packet, plen, 0, &peer.sa, &addr_len)) > 0)
{
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
dump_packet_udp(DUMP_TFTP, (void *)packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
#endif
if (sockaddr_isequal(&peer, &transfer->peer))
handle_tftp(now, transfer, len);
handle_tftp(packet, now, transfer, len);
else
{
/* Wrong source address. See rfc1350 para 4. */
prettyprint_addr(&peer, daemon->addrbuff);
len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer))));
len = tftp_err(ERR_TID, packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL);
while(retry_send(sendto(transfer->sockfd, packet, len, 0, &peer.sa, sa_len(&peer))));
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
#endif
}
}
@@ -662,7 +666,7 @@ void check_tftp_listeners(time_t now)
int endcon = 0, error = 0, timeout = 0;
tmp = transfer->next;
/* ->start set to zero in handle_tftp() when we recv an error packet. */
if (transfer->start == 0)
endcon = error = 1;
@@ -671,7 +675,7 @@ void check_tftp_listeners(time_t now)
endcon = 1;
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (get_block(daemon->packet, transfer) > 0)
if (get_block(transfer) > 0)
error = timeout = 1;
}
else if (difftime(now, transfer->retransmit) >= 0.0)
@@ -679,46 +683,44 @@ void check_tftp_listeners(time_t now)
/* Do transmission or re-transmission. When we get an ACK, the call to handle_tftp()
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
here. */
unsigned int i, winsize;
ssize_t len;
unsigned int i, winsize;
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
transfer->backoff++;
transfer->block = transfer->lastack;
if ((len = get_block(daemon->packet, transfer)) == 0)
endcon = 1; /* got last ACK */
else
/* send a window'a worth of blocks unless we're retransmitting OACK */
winsize = transfer->block ? transfer->windowsize : 1;
for (i = 0; i < winsize; i++, transfer->block++)
{
/* send a window'a worth of blocks unless we're retransmitting OACK */
winsize = transfer->block ? transfer->windowsize : 1;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
if ((len = get_block(transfer)) == 0)
{
if (i != 0)
len = get_block(daemon->packet, transfer);
if (i == 0)
endcon = 1; /* got last ACK */
if (len == 0)
break;
if (len == -1)
{
len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = error = 1;
}
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
&transfer->peer, &transfer->source, transfer->if_index);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
#endif
break;
}
}
}
if (len == -1)
{
len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = error = 1;
}
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
&transfer->peer, &transfer->source, transfer->if_index);
#ifdef HAVE_DUMPFILE
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
#endif
}
/* prefetch the block we'll probably need when we get an ACK. */
if (!endcon)
get_block(transfer);
}
if (endcon)
{
strcpy(daemon->namebuff, transfer->file->filename);
@@ -747,23 +749,36 @@ void check_tftp_listeners(time_t now)
}
}
/* packet in daemon->packet as this is called. */
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
static void handle_tftp(char *packet, time_t now, struct tftp_transfer *transfer, ssize_t len)
{
struct ack {
unsigned short op, block;
} *mess = (struct ack *)daemon->packet;
} *mess = (struct ack *)packet;
if (len >= (ssize_t)sizeof(struct ack))
{
if (ntohs(mess->op) == OP_ACK)
{
/* try and handle 16-bit blockno wrap-around */
unsigned int block = (unsigned short)ntohs(mess->block);
if (block < transfer->lastack)
block |= transfer->block & 0xffff0000;
/* ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
/* Handle 16-bit block number wrap-around. */
u16 new = ntohs(mess->block);
u32 block;
/* If the last ack received was in the top quarter of a 64k block
and this one is in the bottom quarter, assume it has wrapped.
Since this is UDP and an old packet can in theory wander in we may also
need to drop back to a previous segment. Such an ACK is ignored below;
here we're just getting the most likely 32 bit value from the
16 bits that we have. */
if (new <= 0x4000 && transfer->ackprev >= 0xc000)
transfer->block_hi++;
else if (new >= 0xc000 && transfer->ackprev <= 0x4000 && transfer->block_hi != 0)
transfer->block_hi--;
transfer->ackprev = new;
block = (((u32)transfer->block_hi) << 16) + (u32)new;
/* Ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
if (block >= transfer->lastack &&
block <= transfer->block)
{
@@ -771,20 +786,23 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
transfer->retransmit = transfer->start = now;
transfer->backoff = 0;
transfer->lastack = block + 1;
/* We have no easy function from block no. to file offset when
expanding line breaks in netascii mode, so we update the offset here
as each block is acknowledged. This explains why the window size must be
one for a netascii transfer; to avoid the block no. doing anything
other than incrementing by one. */
if (transfer->netascii && block != 0)
transfer->offset += transfer->blocksize - transfer->expansion;
{
transfer->offset += (off_t)transfer->blocksize - (off_t)transfer->expansion;
transfer->lastcarrylf = transfer->carrylf;
}
}
}
else if (ntohs(mess->op) == OP_ERR)
{
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *p = packet + sizeof(struct ack);
char *end = packet + len;
char *err = next(&p, end);
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
@@ -860,8 +878,7 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
char message[];
} *mess = (struct errmess *)packet;
ssize_t len, ret = 4;
memset(packet, 0, daemon->packet_buff_sz);
if (file)
sanitise(file);
@@ -885,10 +902,11 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
}
/* return -1 for error, zero for done. */
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
static ssize_t get_block(struct tftp_transfer *transfer)
{
memset(packet, 0, daemon->packet_buff_sz);
static off_t saved_offset = 0;
static ssize_t saved_len = 0;
if (transfer->block == 0)
{
/* send OACK */
@@ -896,8 +914,12 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
struct oackmess {
unsigned short op;
char data[];
} *mess = (struct oackmess *)packet;
} *mess = (struct oackmess *)daemon->packet;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
memset(daemon->packet, 0, daemon->packet_buff_sz);
p = mess->data;
mess->op = htons(OP_OACK);
if (transfer->opt_blocksize)
@@ -921,7 +943,7 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
}
return p - packet;
return p - daemon->packet;
}
else
{
@@ -929,44 +951,58 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
struct datamess {
unsigned short op, block;
unsigned char data[];
} *mess = (struct datamess *)packet;
} *mess = (struct datamess *)daemon->packet;
size_t size;
if (!transfer->netascii)
transfer->offset = (transfer->block - 1) * transfer->blocksize;
transfer->offset = (off_t)(transfer->block - 1) * (off_t)transfer->blocksize;
if (transfer->offset > transfer->file->size)
return 0; /* finished */
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
size = transfer->blocksize;
/* We may have a prefetched block already in the buffer. */
if (daemon->srv_save == transfer && saved_offset == transfer->offset)
return saved_len;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((size = transfer->file->size - transfer->offset) > (size_t)transfer->blocksize)
size = (size_t)transfer->blocksize;
mess->op = htons(OP_DATA);
mess->block = htons((unsigned short)(transfer->block));
if (size != 0 &&
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
return -1;
if (size != 0)
{
if (transfer->file->posn != transfer->offset &&
lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1)
return -1;
if (!read_write(transfer->file->fd, mess->data, size, RW_READ))
return -1;
transfer->file->posn = transfer->offset + size;
}
/* Map '\n' to CR-LF in netascii mode */
if (transfer->netascii)
{
size_t i;
int newcarrylf;
/* Map '\n' to CR-LF in netascii mode */
transfer->expansion = transfer->carrylf = 0;
transfer->expansion = 0;
for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
for (i = 0; i < size; i++)
if (mess->data[i] == '\n' && (i != 0 || !transfer->lastcarrylf))
{
transfer->expansion++;
if (size != transfer->blocksize)
size++; /* room in this block */
else if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
transfer->carrylf = 1; /* don't expand LF again if it moves to the next block */
/* make space and insert CR */
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
@@ -974,11 +1010,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
i++;
}
transfer->carrylf = newcarrylf;
}
return size + 4;
daemon->srv_save = transfer;
saved_offset = transfer->offset;
saved_len = size + 4;
return saved_len;
}
}

View File

@@ -34,6 +34,10 @@
#include <sys/utsname.h>
#endif
#ifdef HAVE_BSD_NETWORK
#include <libgen.h>
#endif
/* SURF random number generator */
static u32 seed[32];
@@ -831,9 +835,34 @@ void close_fds(long max_fd, int spare1, int spare2, int spare3)
#endif
#ifdef FDESCFS
DIR *d;
DIR *d = NULL;
if ((d = opendir(FDESCFS)))
# ifdef HAVE_BSD_NETWORK
dev_t dirdev = 0;
char fdescfs[] = FDESCFS; /* string must be writable */
struct stat statbuf;
/* On BSD, fdescfs is normally mounted at /dev/fd. However
if it is NOT mounted, devfs creates a directory at /dev/fd
which contains (only) the file descriptors 0,1 and 2.
Under these conditions, opendir() will succeed, and
if we proceed we will fail to close extant
file descriptors which should be closed.
Check that there is a filesystem mounted at /dev/fd
by checking that the device changes between /dev/fd
and /dev. If if doesn't, fall back to the dumb path. */
if (stat(fdescfs, &statbuf) != -1)
dirdev = statbuf.st_dev;
if (stat(dirname(fdescfs), &statbuf) != -1 &&
dirdev != statbuf.st_dev)
# endif
d = opendir(FDESCFS);
if (d)
{
struct dirent *de;
@@ -855,9 +884,9 @@ void close_fds(long max_fd, int spare1, int spare2, int spare3)
closedir(d);
return;
}
}
#endif
/* fallback, dumb code. */
for (max_fd--; max_fd >= 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO && max_fd != STDIN_FILENO &&