Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
D
Dnsmasq
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
Dnsmasq
Commits
44a2a316
Commit
44a2a316
authored
Mar 10, 2004
by
Simon Kelley
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
import of dnsmasq-2.3.tar.gz
parent
b49644f3
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1674 additions
and
907 deletions
+1674
-907
CHANGELOG
CHANGELOG
+109
-5
Makefile
Makefile
+1
-1
dnsmasq-mdk.spec
dnsmasq-mdk.spec
+1
-1
dnsmasq-rh.spec
dnsmasq-rh.spec
+1
-1
dnsmasq-suse.spec
dnsmasq-suse.spec
+1
-1
dnsmasq.8
dnsmasq.8
+44
-16
dnsmasq.conf.example
dnsmasq.conf.example
+43
-2
src/cache.c
src/cache.c
+15
-6
src/config.h
src/config.h
+44
-12
src/dhcp.c
src/dhcp.c
+374
-139
src/dnsmasq.c
src/dnsmasq.c
+103
-136
src/dnsmasq.h
src/dnsmasq.h
+66
-47
src/forward.c
src/forward.c
+240
-18
src/lease.c
src/lease.c
+62
-28
src/network.c
src/network.c
+159
-286
src/option.c
src/option.c
+166
-69
src/rfc1035.c
src/rfc1035.c
+1
-1
src/rfc2131.c
src/rfc2131.c
+226
-133
src/util.c
src/util.c
+18
-5
No files found.
CHANGELOG
View file @
44a2a316
...
...
@@ -667,6 +667,9 @@ release 2.0
helping to track that one down.
release 2.1
Thanks to Matt Swift and Dag Wieers for many suggestions
which went into this release.
Tweak include files to allow compilation on FreeBSD 5
Fix unaligned access warnings on BSD/Alpha.
...
...
@@ -687,10 +690,11 @@ release 2.1
Fixed long-existing strangeness in Linux IPv6 interface
discovery code. The flags field in /proc/net/if_inet6 is
_not_ the interface flags.
_not_ the interface flags.
Fail gracefully when getting an ENODEV error when trying
to bind an IPv6 socket, rather than bailing out.
to bind an IPv6 socket, rather than bailing out. Thanks
to Jan Ischebeck for feedback on that.
Allow the name->address mapping for static DHCP leases to
be set by /etc/hosts. It's now possible to have
...
...
@@ -710,12 +714,112 @@ release 2.1
Fix lease time spec when specified in dhcp-range and not
in dhcp-host, previously this was always one hour.
Fix problem with setting domains as "local only".
Fix problem with setting domains as "local only". -
thanks to Chris Schank.
Added support for max message size DHCP option.
release 2.2
Fix total lack for DHCP functionality on
Linux systems with IPv6 enabled.
Linux systems with IPv6 enabled. - thanks to
Jonathon Hudson for spotting that.
Move default config file under FreeBSD - patch from
Steven Honson
release 2.3
Fix "install" makefile target. (reported by Rob Stevens)
Ensure that "local=/domain/" flag is obeyed for all
queries on a domain, not just A and AAAA. (Reported by
Peter Fichtner.)
Handle DHCPDECLINE messages and provide an error message
in DHCPNAK messages.
Add "domain" setting example to
dnsmasq.conf.example. Thanks to K P Kirchdorfer for
spotting that it was missing.
Subtle change to the DHCPREQUEST handling code to work
around a bug in the DHCP client in HP Jetdirect printers.
Thanks to Marko Stolle for finding this problem.
Return DHCP T1 and T2 times, with "fuzz" to desychronise lease
renewals, as specified in the RFC.
Ensure that the END option is always present in DHCP
packets , even if the packet is too small to fit all
the requested options.
Handle larger-than-default DHCP packets if required, up
to the ethernet MTU.
Fix a couple of places where the return code from
malloc() was not checked.
Cope with a machine taking a DHCP lease and then moving
network so that the lease address is no longer valid.
The DHCP server will now work via a BOOTP relay - remote
networks are configured with the dhcp-range option the
same as directly connected ones, but they need an
additional netmask parameter. Eg
--dhcp-range=192.168.4.10,192.168.4.50,255.255,255.0
will enable DHCP service via a BOOTP relay on the
192.168.4.0 network.
Add a limit on the number of available DHCP leases,
otherwise the daemon could be DOSed by a malicious
host. The default is 150, but it can be changed by the
dhcp-lease-max option.
Fixed compilation on OpenBSD (thanks to Frederic Brodbeck
for help with that.)
Reworked the DHCP network handling code for two good
effects: (1) The limit of one network only for DHCP on
FreeBSD is now gone, (2) The DHCP server copes with
dynamically created interfaces. The one-interface
limitation remains for OpenBSD, which is missing
extensions to the socket API which have been in Linux
since version 2.2 and FreeBSD since version 4.8.
Reworked the DNS network code to also cope with
dynamically created interfaces. dnsmasq will now listen
to the wildcard address and port 53 by default, so if no
--interface or --address options are given it will handle
dynamically created interfaces. The old behaviour can be
restored with --bind-interfaces for people running BIND
on one interface and dnsmasq on another. Note that
--interface and --address options still work, but the
filtering is done by dnsmasq, rather then the kernel.
This works on Linux, and FreeBSD>=5.0. On systems which
don't support the required API extensions, the old
behaviour is used, just as if --bind-interfaces had been set.
Allow IPv6 support to be disabled at compile time. To do
that, add -DNO_IPV6 to the CFLAGS. Thanks to Oleg
I. Vdovikin for the suggestion to do that.
Add ability to set DHCP options per network. This is done
by giving a network an identifier like this:
dhcp-range=red-net,192.168.0.10,192.168.0.50
and then labeling options intended for that network only
like this:
dhcp-option=red-net,6,1.1.1.1
Thanks to Oleg Vdovikin for arguing that one through.
Made errors in the configuration file non-fatal: dnsmasq
will now complain bitterly, but continue.
Added --read-ethers option, to allow dnsmasq to pull
static DHCP information from that file.
Thanks to Andi Cambeis for that suggestion.
Added HAVE_BROKEN_RTC compilation option to support
embedded systems without a stable RTC. Oleg Vdovikin
helped work out how to make that work.
Move default config file under FreeBSD.
Makefile
View file @
44a2a316
...
...
@@ -12,7 +12,7 @@ all :
clean
:
rm
-f
*
~
*
/
*
~
$(SRC)
/
*
.o
$(SRC)
/dnsmasq core build
install
:
$(SRC)/dnsmasq
install
:
all
install
-d
$(DESTDIR)$(BINDIR)
-d
$(DESTDIR)$(MANDIR)
/man8
install
-m
644 dnsmasq.8
$(DESTDIR)$(MANDIR)
/man8
install
-m
755
$(SRC)
/dnsmasq
$(DESTDIR)$(BINDIR)
...
...
dnsmasq-mdk.spec
View file @
44a2a316
...
...
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.
2
Version: 2.
3
Release: 1
Copyright: GPL
Group: System Environment/Daemons
...
...
dnsmasq-rh.spec
View file @
44a2a316
...
...
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.
2
Version: 2.
3
Release: 1
Copyright: GPL
Group: System Environment/Daemons
...
...
dnsmasq-suse.spec
View file @
44a2a316
...
...
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
Version: 2.
2
Version: 2.
3
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
...
...
dnsmasq.8
View file @
44a2a316
...
...
@@ -87,7 +87,7 @@ flags.
.B \-I, --except-interface=<interface name>
Do not listen on the specified interface.
.TP
.B \-a, --listen-address
.B \-a, --listen-address
=<ipaddr>
Listen only on the given IP address. As with
.B \-i
more than one address may be specified. Unlike
...
...
@@ -101,6 +101,15 @@ or
.B \-I
flags.
.TP
.B \-z, --bind-interfaces
On systems which support it, dnsmasq binds the wildcard address,
even when it is listening on only some interfaces. It then discards
requests that it shouldn't reply to. This has the advantage of
working even when interfaces come and go and change address. This
option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when
running another nameserver on the same machine.
.TP
.B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
which are not found in /etc/hosts or the DHCP leases file are resolved to the IP address in dotted-quad form.
...
...
@@ -226,15 +235,26 @@ Disable negative caching. Negative caching allows dnsmasq to remember
identical queries without forwarding them again. This flag disables
negative caching.
.TP
.B \-F, --dhcp-range=
<start-addr>,<end-addr>
[,<default lease time>]
.B \-F, --dhcp-range=
[network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>]
[,<default lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr>, both of which must be on the network
attached to a local interface. If the lease time is given, then leases
<start-addr> to <end-addr> and from statically defined addresses given
in
.B dhcp-host
options. If the lease time is given, then leases
will be given for that length of time. The lease time is on seconds,
or minutes (eg 45m) or hours (eg 1h) or the literal "infinite". This
option may be repeated, with different addresses, to enable DHCP
service on more than one local interface. (Use of more than one
interface currently only works under Linux.)
service to more than one network. For directly connected networks (ie,
networks on which the machine running dnsmasq has an interface) the
netmask is optional. It is, however, required for networks which
recieve DHCP service via a relay agent. The broadcast address is
always optional. On some broken systems, dnsmasq can listen on only
one interface when using DHCP, and the name of that interface must be
given using the
.B interface
option. This limitation currently affects OpenBSD. The optional
network-id is a alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis.
.TP
.B \-G, --dhcp-host=[[<hwaddr>]|[id:<client_id>]][,<ipaddr>][,<hostname>][,<lease_time>]
Specify per host parameters for the DHCP server. This allows a machine
...
...
@@ -263,8 +283,16 @@ If a name appears in /etc/hosts, the associated address can be
allocated to a DHCP lease, but only if a
.B --dhcp-host
option specifying the name also exists.
.TP
.B \-Z, --read-ethers
Read /etc/ethers for information about hosts for the DHCP server. The
format of /etc/ethers is a hardware address, followed by either a
hostname or dotted-quad IP address. When read by dnsmasq these lines
have exactly the same effect as
.B --dhcp-host
options containing the same information.
.TP
.B \-O, --dhcp-option=<opt>,[<value>[,<value>]]
.B \-O, --dhcp-option=
[network-id,]
<opt>,[<value>[,<value>]]
Specfify different or extra options to DHCP clients. By default,
dnsmasq sends some standard options to DHCP clients, the netmask and
broadcast address are set to the same as the host running dnsmasq, and
...
...
@@ -278,7 +306,9 @@ specfied in RFC2132. For example, to set the default route option to
and to set the time-server address to 192.168.0.4, do
.B dhcp-option=42,192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the
machine running dnsmasq".
machine running dnsmasq". If the optional network-id is given then
this option is only sent to machines on the network whose dhcp-range
contains a matching network-id.
Be careful: no checking is done that the correct type of data for the
option number is sent, and there are option numbers for which it is not
possible to generate the correct data type; it is quite possible to
...
...
@@ -290,6 +320,12 @@ Set BOOTP options to be returned by the DHCP server. These are needed
for machines which network boot, and tell the machine where to collect
its initial configuration.
.TP
.B \-X, --dhcp-lease-max=<number>
Limits dnsmasq to the specified maximum number of DHCP leases. The
default is 150. This limit is to prevent DoS attacks from hosts which
create thousands of leases and use lots of memory in the dnsmasq
process.
.TP
.B \-l, --dhcp-leasefile=<path>
Use the specified file to store DHCP lease information.
.TP
...
...
@@ -332,14 +368,6 @@ of names that have been inserted into the cache. In
.B --no-daemon
mode or when full logging is enabled (-q), a complete dump of the contents of the cache is made.
.PP
When it receives a SIGUSR2,
.B dnsmasq
re-scans network interfaces. This is required if it is to listen for
queries on newly created interfaces or interfaces which have changed IP
address. For this facility to work, dnsmasq must be told to continue
running as user root, using
.B --user=root
.PP
Dnsmasq is a DNS query forwarder: it it not capable of recursively
answering arbitrary queries starting from the root servers but
forwards such queries to a fully recursive upstream DNS server which is
...
...
dnsmasq.conf.example
View file @
44a2a316
...
...
@@ -76,6 +76,15 @@ filterwin2k
# you use this.)
#listen-address=
# On systems which support it, dnsmasq binds the wildcard address,
# even when it is listening on only some interfaces. It then discards
# requests that it shouldn't reply to. This has the advantage of
# working even when interfaces come and go and change address. If you
# want dnsmasq to really bind only the interfaces it is listening on,
# uncomment this option. About the only time you may need this is when
# running another nameserver on the same machine.
#bind-interfaces
# If you don't want dnsmasq to read /etc/hosts, uncomment the
# following line.
#no-hosts
...
...
@@ -87,13 +96,32 @@ filterwin2k
# automatically added to simple names in a hosts-file.
#expand-hosts
# Set the domain for dnsmasq. this is optional, but if it is set, it
# does the following things.
# 1) Allows DHCP hosts to have fully qualified domain names, as long
# as the domain part matches this setting.
# 2) Sets the "domain" DHCP option thereby potentially setting the
# domain of all systems configured by DHCP
# 3) Provides the domain part for "expand-hosts"
#domain=thekelleys.org.uk
# Uncomment this to enable the integrated DHCP server, you need
# to supply the range of addresses available for lease and optionally
# a lease time. If you have more than one
interface
, you will need to
# repeat this for each
interface
on which you want to supply DHCP
# a lease time. If you have more than one
network
, you will need to
# repeat this for each
network
on which you want to supply DHCP
# service.
#dhcp-range=192.168.0.50,192.168.0.150,12h
# This is an example of a DHCP range where the netmask is given. This
# is needed for networks we reach the dnsmasq DHCP server via a relay
# agent. If you don't know what a DHCP relay agent is, you probably
# don't need to worry about this.
#dhcp-range=192.168.0.50,192.168.0.150,255.255.255.0,12h
# This is an example of a DHCP range with a network-id, so that
# some DHCP options may be set only for this network.
#dhcp-range=red,192.168.0.50,192.168.0.150
# Supply parameters for specified hosts using DHCP. There are lots
# of valid alternatives, so we will give examples of each. Note that
# IP addresses DO NOT have to be in the range given above, they just
...
...
@@ -129,6 +157,12 @@ filterwin2k
# it asks for a DHCP lease.
#dhcp-host=judge
# If this line is uncommented, dnsmasq will read /etc/ethers and act
# on the ethernet-address/IP pairs found there just as if they had
# been given as --dhcp-host options. Useful if you keep
# MAC-address/host mappings there for other purposes.
#read-ethers
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Note that all the common settings, such as netmask and
...
...
@@ -159,6 +193,10 @@ filterwin2k
# Set the "all subnets are local" flag
#dhcp-option=27,1
# Specify an option which will only be sent to the "red" network
# (see dhcp-range for the declaration of the "red" network)
#dhcp-option=red,42,192.168.1.1
# The following DHCP options set up dnsmasq in the same way as is specified
# for the ISC dhcpcd in
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
...
...
@@ -177,6 +215,9 @@ filterwin2k
# boot machines over the network.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
# The DHCP server needs somewhere on disk to keep its lease database.
# This defaults to a sane location, but if you want to change it, use
# the line below.
...
...
src/cache.c
View file @
44a2a316
...
...
@@ -710,7 +710,12 @@ void dump_cache(int debug, int cache_size)
else
strcpy
(
addrbuff
,
inet_ntoa
(
cache
->
addr
.
addr
.
addr4
));
#endif
syslog
(
LOG_DEBUG
,
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s"
,
syslog
(
LOG_DEBUG
,
#ifdef HAVE_BROKEN_RTC
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %ld
\n
"
,
#else
"%-40.40s %-30.30s %s%s%s%s%s%s%s%s%s%s %s"
,
#endif
cache_get_name
(
cache
),
addrbuff
,
cache
->
flags
&
F_IPV4
?
"4"
:
""
,
cache
->
flags
&
F_IPV6
?
"6"
:
""
,
...
...
@@ -722,7 +727,11 @@ void dump_cache(int debug, int cache_size)
cache
->
flags
&
F_NXDOMAIN
?
"X"
:
" "
,
cache
->
flags
&
F_HOSTS
?
"H"
:
" "
,
cache
->
flags
&
F_ADDN
?
"A"
:
" "
,
#ifdef HAVE_BROKEN_RTC
cache
->
flags
&
F_IMMORTAL
?
0
:
(
unsigned
long
)
cache
->
ttd
)
;
#else
cache
->
flags
&
F_IMMORTAL
?
"
\n
"
:
ctime
(
&
(
cache
->
ttd
)))
;
#endif
}
}
...
...
@@ -749,14 +758,14 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr)
#endif
if
(
flags
&
F_NXDOMAIN
)
strcpy
(
addrbuff
,
"<NXDOMAIN>
-
"
);
strcpy
(
addrbuff
,
"<NXDOMAIN>"
);
else
strcpy
(
addrbuff
,
"<NODATA>
-
"
);
strcpy
(
addrbuff
,
"<NODATA>"
);
if
(
flags
&
F_IPV4
)
strcat
(
addrbuff
,
"IPv4"
);
else
strcat
(
addrbuff
,
"IPv6"
);
strcat
(
addrbuff
,
"
-
IPv4"
);
else
if
(
flags
&
F_IPV6
)
strcat
(
addrbuff
,
"
-
IPv6"
);
}
else
#ifdef HAVE_IPV6
...
...
src/config.h
View file @
44a2a316
...
...
@@ -12,21 +12,23 @@
/* Author's email: simon@thekelleys.org.uk */
#define VERSION "2.
2
"
#define VERSION "2.
3
"
#define FTABSIZ 150
/* max number of outstanding requests */
#define TIMEOUT
4
0
/* drop queries after TIMEOUT seconds */
#define TIMEOUT
2
0
/* drop queries after TIMEOUT seconds */
#define LOGRATE 120
/* log table overflows every LOGRATE seconds */
#define CACHESIZ 150
/* default cache size */
#define MAXLEASES 150
/* maximum number of DHCP leases */
#define SMALLDNAME 40
/* most domain names are smaller than this */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
#ifdef __uClinux__
# define RESOLVFILE "/etc/config/resolv.conf"
#else
# define RESOLVFILE "/etc/resolv.conf"
#endif
#define RUNFILE "/var/run/dnsmasq.pid"
#if
def __FreeBSD__
#if
defined(__FreeBSD__) || defined (__OpenBSD__)
# define LEASEFILE "/var/db/dnsmasq.leases"
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
#else
...
...
@@ -37,6 +39,7 @@
#define CHUSER "nobody"
#define CHGRP "dip"
#define IP6INTERFACES "/proc/net/if_inet6"
#define UPTIME "/proc/uptime"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
...
...
@@ -55,13 +58,29 @@
#endif
/* determine if we can find the destination address of recieved packets
and set the source address of sent ones. If so, we can use one socket
bound to INADDR_ANY and cope with dynamically created interfaces.
Linux does this differently to FreeBSD. */
#if defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
# define HAVE_UDP_SRC_DST
#else
# undef HAVE_UDP_SRC_DST
#endif
/* Decide if we're going to support IPv6 */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN)
#if defined(INET6_ADDRSTRLEN)
&& !defined(NO_IPV6)
# define HAVE_IPV6
# define ADDRSTRLEN INET6_ADDRSTRLEN
# if defined(SOL_IPV6)
# define IPV6_LEVEL SOL_IPV6
# else
# define IPV6_LEVEL IPPROTO_IPV6
# endif
#elif defined(INET_ADDRSTRLEN)
# undef HAVE_IPV6
# define ADDRSTRLEN INET_ADDRSTRLEN
...
...
@@ -85,6 +104,23 @@ HAVE_LINUX_IPV6_PROC
define this to do IPv6 interface discovery using
proc/net/if_inet6 ala LINUX.
HAVE_BROKEN_RTC
define this on embeded systems which don't have an RTC
which keeps time over reboots. Causes dnsmasq to use uptime()
for timing, and keep relative time values in its leases file.
Also enables "Flash disk mode". Normally, dnsmasq tries very hard to
keep the on-disk leases file up-to-date: rewriting it after every change.
When HAVE_BROKEN_RTC is in effect, a different regime is used:
The leases file is written when dnsmasq terminates, when it receives
SIGALRM, when a brand new lease is allocated, or every n seconds,
where n is one third of the smallest time configured for leases
in a --dhcp-range or --dhcp-host option.
NOTE: when enabling or disabling this, be sure to delete any old
leases file, otherwise dnsmasq may get very confused.
This configuration currently only works on Linux, but could be made to
work on other systems by teaching dnsmasq_time() in utils.c how to
read the system uptime.
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
...
...
@@ -111,9 +147,6 @@ HAVE_SOCKADDR_SA_LEN
HAVE_PSELECT
If your C library implements pselect, define this.
HAVE_PF_PACKET
If your OS implements packet sockets, define this.
HAVE_BPF
If your OS implements Berkeley PAcket filter, define this.
...
...
@@ -124,8 +157,7 @@ NOTES:
HAVE_RANDOM
HAVE_DEV_RANDOM
HAVE_DEV_URANDOM
HAVE_PF_PACKET
you should NOT define
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
...
...
@@ -151,7 +183,6 @@ NOTES:
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#define HAVE_PF_PACKET
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Don't fork into background on uClinux */
...
...
@@ -175,7 +206,6 @@ NOTES:
#define HAVE_RANDOM
#define HAVE_DEV_URANDOM
#define HAVE_DEV_RANDOM
#undef HAVE_PF_PACKET
#undef HAVE_SOCKADDR_SA_LEN
#undef HAVE_PSELECT
/* Fix various misfeatures of libc5 headers */
...
...
@@ -193,7 +223,6 @@ typedef size_t socklen_t;
#define HAVE_DEV_RANDOM
#undef HAVE_SOCKADDR_SA_LEN
#define HAVE_PSELECT
#define HAVE_PF_PACKET
/* glibc < 2.2 has broken Sockaddr_in6 so we have to use our own. */
/* glibc < 2.2 doesn't define in_addr_t */
#if defined(__GLIBC__) && (__GLIBC__ == 2) && \
...
...
@@ -204,6 +233,9 @@ typedef unsigned long in_addr_t;
#endif
#endif
/* #elif defined(__OpenBSD__)
#error The sockets API in OpenBSD does not provide facilities required by dnsmasq
*/
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#undef HAVE_LINUX_IPV6_PROC
#undef HAVE_GETOPT_LONG
...
...
src/dhcp.c
View file @
44a2a316
...
...
@@ -14,156 +14,320 @@
#include "dnsmasq.h"
void
dhcp_packet
(
struct
dhcp_context
*
context
,
char
*
packet
,
void
dhcp_init
(
int
*
fdp
,
int
*
rfdp
)
{
int
fd
=
socket
(
PF_INET
,
SOCK_DGRAM
,
IPPROTO_UDP
);
struct
sockaddr_in
saddr
;
int
opt
=
1
;
if
(
fd
==
-
1
)
die
(
"Cannot create DHCP socket : %s"
,
NULL
);
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#if defined(IP_PKTINFO)
setsockopt
(
fd
,
SOL_IP
,
IP_PKTINFO
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#elif defined(IP_RECVIF)
setsockopt
(
fd
,
IPPROTO_IP
,
IP_RECVIF
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#endif
setsockopt
(
fd
,
SOL_SOCKET
,
SO_BROADCAST
,
&
opt
,
sizeof
(
opt
))
==
-
1
)
die
(
"failed to set options on DHCP socket: %s"
,
NULL
);
saddr
.
sin_family
=
AF_INET
;
saddr
.
sin_port
=
htons
(
DHCP_SERVER_PORT
);
saddr
.
sin_addr
.
s_addr
=
INADDR_ANY
;
if
(
bind
(
fd
,
(
struct
sockaddr
*
)
&
saddr
,
sizeof
(
struct
sockaddr_in
)))
die
(
"failed to bind DHCP server socket: %s"
,
NULL
);
*
fdp
=
fd
;
#ifdef HAVE_BPF
opt
=
0
;
while
(
1
)
{
char
filename
[
50
];
sprintf
(
filename
,
"/dev/bpf%d"
,
opt
++
);
if
((
fd
=
open
(
filename
,
O_RDWR
,
0
))
!=
-
1
)
break
;
if
(
errno
!=
EBUSY
)
die
(
"Cannot create DHCP BPF socket: %s"
,
NULL
);
}
#else
if
((
fd
=
socket
(
PF_PACKET
,
SOCK_DGRAM
,
htons
(
ETHERTYPE_IP
)))
==
-
1
)
die
(
"Cannot create DHCP packet socket: %s"
,
NULL
);
#endif
*
rfdp
=
fd
;
}
void
dhcp_packet
(
struct
dhcp_context
*
contexts
,
char
*
packet
,
struct
dhcp_opt
*
dhcp_opts
,
struct
dhcp_config
*
dhcp_configs
,
time_t
now
,
char
*
namebuff
,
char
*
domain_suffix
,
char
*
dhcp_file
,
char
*
dhcp_sname
,
struct
in_addr
dhcp_next_server
)
struct
in_addr
dhcp_next_server
,
int
dhcp_fd
,
int
raw_fd
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
)
{
struct
udp_dhcp_packet
*
rawpacket
=
(
struct
udp_dhcp_packet
*
)
packet
;
struct
udp_dhcp_packet
*
rawpacket
=
(
struct
udp_dhcp_packet
*
)
packet
;
struct
dhcp_packet
*
mess
=
(
struct
dhcp_packet
*
)
&
rawpacket
->
data
;
int
sz
,
newlen
;
sz
=
recvfrom
(
context
->
fd
,
&
rawpacket
->
data
,
PACKETSZ
-
(
sizeof
(
struct
ip
)
+
sizeof
(
struct
udphdr
)),
0
,
NULL
,
0
);
if
((
unsigned
int
)
sz
>
(
sizeof
(
*
mess
)
-
sizeof
(
mess
->
options
)))
{
lease_prune
(
NULL
,
now
);
/* lose any expired leases */
newlen
=
dhcp_reply
(
context
,
mess
,
sz
,
now
,
namebuff
,
dhcp_opts
,
dhcp_configs
,
domain_suffix
,
dhcp_file
,
dhcp_sname
,
dhcp_next_server
);
lease_update_dns
(
0
);
if
(
newlen
!=
0
)
{
int
broadcast
=
ntohs
(
mess
->
flags
)
&
0x8000
;
/* newlen -ve forces broadcast */
if
(
newlen
<
0
)
{
broadcast
=
1
;
newlen
=
-
newlen
;
}
if
(
mess
->
giaddr
.
s_addr
||
mess
->
ciaddr
.
s_addr
)
{
/* To send to BOOTP relay or configured client, use
the IP packet */
struct
sockaddr_in
dest
;
dest
.
sin_family
=
AF_INET
;
if
(
mess
->
giaddr
.
s_addr
)
{
dest
.
sin_port
=
htons
(
DHCP_SERVER_PORT
);
dest
.
sin_addr
=
mess
->
giaddr
;
}
else
{
dest
.
sin_port
=
htons
(
DHCP_CLIENT_PORT
);
dest
.
sin_addr
=
mess
->
ciaddr
;
}
sendto
(
context
->
fd
,
mess
,
newlen
,
0
,
(
struct
sockaddr
*
)
&
dest
,
sizeof
(
dest
));
}
else
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
u32
i
,
sum
;
#ifdef HAVE_PF_PACKET
struct
sockaddr_ll
dest
;
dest
.
sll_family
=
AF_PACKET
;
dest
.
sll_halen
=
ETHER_ADDR_LEN
;
dest
.
sll_ifindex
=
context
->
ifindex
;
dest
.
sll_protocol
=
htons
(
ETHERTYPE_IP
);
if
(
broadcast
)
{
memset
(
dest
.
sll_addr
,
255
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
INADDR_BROADCAST
;
}
else
{
memcpy
(
dest
.
sll_addr
,
mess
->
chaddr
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
mess
->
yiaddr
.
s_addr
;
}
struct
dhcp_context
*
context
;
struct
iname
*
tmp
;
struct
ifreq
ifr
;
struct
msghdr
msg
;
struct
iovec
iov
[
2
];
struct
cmsghdr
*
cmptr
;
int
sz
,
newlen
,
iface_index
=
0
;
struct
in_addr
source
,
real_netmask
,
iface_addr
,
netmask_save
,
broadcast_save
;
#ifdef HAVE_BPF
unsigned
char
iface_hwaddr
[
ETHER_ADDR_LEN
];
#endif
#ifdef HAVE_BPF
struct
ether_header
header
;
struct
iovec
iov
[
2
];
header
.
ether_type
=
htons
(
ETHERTYPE_IP
);
memcpy
(
header
.
ether_shost
,
context
->
hwaddr
,
ETHER_ADDR_LEN
);
if
(
broadcast
)
{
memset
(
header
.
ether_dhost
,
255
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
INADDR_BROADCAST
;
}
else
{
memcpy
(
header
.
ether_dhost
,
mess
->
chaddr
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
mess
->
yiaddr
.
s_addr
;
}
union
{
struct
cmsghdr
align
;
/* this ensures alignment */
#ifdef IP_PKTINFO
char
control
[
CMSG_SPACE
(
sizeof
(
struct
in_pktinfo
))];
#else
char
control
[
CMSG_SPACE
(
sizeof
(
struct
sockaddr_dl
))];
#endif
}
control_u
;
iov
[
0
].
iov_base
=
(
char
*
)
&
rawpacket
->
data
;
iov
[
0
].
iov_len
=
DNSMASQ_PACKETSZ
-
(
sizeof
(
struct
ip
)
+
sizeof
(
struct
udphdr
));
rawpacket
->
ip
.
ip_p
=
IPPROTO_UDP
;
rawpacket
->
ip
.
ip_src
.
s_addr
=
context
->
serv_addr
.
s_addr
;
rawpacket
->
ip
.
ip_len
=
htons
(
sizeof
(
struct
ip
)
+
sizeof
(
struct
udphdr
)
+
newlen
)
;
rawpacket
->
ip
.
ip_hl
=
sizeof
(
struct
ip
)
/
4
;
rawpacket
->
ip
.
ip_v
=
IPVERSION
;
rawpacket
->
ip
.
ip_tos
=
0
;
rawpacket
->
ip
.
ip_id
=
htons
(
0
);
rawpacket
->
ip
.
ip_off
=
htons
(
0x4000
);
/* don't fragment */
rawpacket
->
ip
.
ip_ttl
=
IPDEFTTL
;
rawpacket
->
ip
.
ip_sum
=
0
;
for
(
sum
=
0
,
i
=
0
;
i
<
sizeof
(
struct
ip
)
/
2
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
ip
)[
i
];
while
(
sum
>>
16
)
sum
=
(
sum
&
0xffff
)
+
(
sum
>>
16
);
rawpacket
->
ip
.
ip_sum
=
(
sum
==
0xffff
)
?
sum
:
~
sum
;
rawpacket
->
udp
.
uh_sport
=
htons
(
DHCP_SERVER_PORT
);
rawpacket
->
udp
.
uh_dport
=
htons
(
DHCP_CLIENT_PORT
);
((
u8
*
)
&
rawpacket
->
data
)[
newlen
]
=
0
;
/* for checksum, in case length is odd. */
rawpacket
->
udp
.
uh_sum
=
0
;
rawpacket
->
udp
.
uh_ulen
=
sum
=
htons
(
sizeof
(
struct
udphdr
)
+
newlen
);
sum
+=
htons
(
IPPROTO_UDP
);
for
(
i
=
0
;
i
<
4
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
ip
.
ip_src
)[
i
];
for
(
i
=
0
;
i
<
(
sizeof
(
struct
udphdr
)
+
newlen
+
1
)
/
2
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
udp
)[
i
];
while
(
sum
>>
16
)
sum
=
(
sum
&
0xffff
)
+
(
sum
>>
16
);
rawpacket
->
udp
.
uh_sum
=
(
sum
==
0xffff
)
?
sum
:
~
sum
;
msg
.
msg_control
=
control_u
.
control
;
msg
.
msg_controllen
=
sizeof
(
control_u
);
msg
.
msg_flags
=
0
;
msg
.
msg_name
=
NULL
;
msg
.
msg_namelen
=
0
;
msg
.
msg_iov
=
iov
;
msg
.
msg_iovlen
=
1
;
sz
=
recvmsg
(
dhcp_fd
,
&
msg
,
0
);
if
(
sz
<
(
int
)(
sizeof
(
*
mess
)
-
sizeof
(
mess
->
options
)))
return
;
#if defined (IP_PKTINFO)
if
(
msg
.
msg_controllen
<
sizeof
(
struct
cmsghdr
))
return
;
for
(
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
cmptr
;
cmptr
=
CMSG_NXTHDR
(
&
msg
,
cmptr
))
if
(
cmptr
->
cmsg_level
==
SOL_IP
&&
cmptr
->
cmsg_type
==
IP_PKTINFO
)
iface_index
=
((
struct
in_pktinfo
*
)
CMSG_DATA
(
cmptr
))
->
ipi_ifindex
;
if
(
!
iface_index
||
!
if_indextoname
(
iface_index
,
ifr
.
ifr_name
))
return
;
#ifdef HAVE_PF_PACKET
sendto
(
context
->
rawfd
,
rawpacket
,
ntohs
(
rawpacket
->
ip
.
ip_len
),
0
,
(
struct
sockaddr
*
)
&
dest
,
sizeof
(
dest
));
#elif defined(IP_RECVIF)
if
(
msg
.
msg_controllen
<
sizeof
(
struct
cmsghdr
))
return
;
for
(
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
cmptr
;
cmptr
=
CMSG_NXTHDR
(
&
msg
,
cmptr
))
if
(
cmptr
->
cmsg_level
==
IPPROTO_IP
&&
cmptr
->
cmsg_type
==
IP_RECVIF
)
iface_index
=
((
struct
sockaddr_dl
*
)
CMSG_DATA
(
cmptr
))
->
sdl_index
;
if
(
!
iface_index
||
!
if_indextoname
(
iface_index
,
ifr
.
ifr_name
))
return
;
#else
if
(
!
names
||
!
names
->
name
||
names
->
next
)
{
syslog
(
LOG_ERR
,
"must set exactly one interface on broken systems without IP_RECVIF"
);
return
;
}
else
strcpy
(
ifr
.
ifr_name
,
names
->
name
);
#endif
#ifdef HAVE_BPF
iov
[
0
].
iov_base
=
(
char
*
)
&
header
;
iov
[
0
].
iov_len
=
sizeof
(
struct
ether_header
);
iov
[
1
].
iov_base
=
(
char
*
)
rawpacket
;
iov
[
1
].
iov_len
=
ntohs
(
rawpacket
->
ip
.
ip_len
);
writev
(
context
->
rawfd
,
iov
,
2
);
#endif
}
ifr
.
ifr_addr
.
sa_family
=
AF_LINK
;
if
(
ioctl
(
dhcp_fd
,
SIOCGIFADDR
,
&
ifr
)
<
0
)
return
;
memcpy
(
iface_hwaddr
,
LLADDR
((
struct
sockaddr_dl
*
)
&
ifr
.
ifr_addr
),
ETHER_ADDR_LEN
);
#endif
ifr
.
ifr_addr
.
sa_family
=
AF_INET
;
if
(
ioctl
(
dhcp_fd
,
SIOCGIFADDR
,
&
ifr
)
<
0
)
return
;
iface_addr
=
((
struct
sockaddr_in
*
)
&
ifr
.
ifr_addr
)
->
sin_addr
;
/* enforce available interface configuration */
for
(
tmp
=
except
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
(
strcmp
(
tmp
->
name
,
ifr
.
ifr_name
)
==
0
))
return
;
if
(
names
||
addrs
)
{
for
(
tmp
=
names
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
(
strcmp
(
tmp
->
name
,
ifr
.
ifr_name
)
==
0
))
break
;
if
(
!
tmp
)
for
(
tmp
=
addrs
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
addr
.
sa
.
sa_family
==
AF_INET
&&
tmp
->
addr
.
in
.
sin_addr
.
s_addr
==
iface_addr
.
s_addr
)
break
;
if
(
!
tmp
)
return
;
}
/* If the packet came via a relay, use that address to look up the context,
else use the address of the interface is arrived on. */
source
=
mess
->
giaddr
.
s_addr
?
mess
->
giaddr
:
iface_addr
;
for
(
context
=
contexts
;
context
;
context
=
context
->
next
)
{
if
(
!
context
->
netmask
.
s_addr
&&
!
mess
->
giaddr
.
s_addr
&&
ioctl
(
dhcp_fd
,
SIOCGIFNETMASK
,
&
ifr
)
!=
-
1
)
real_netmask
=
((
struct
sockaddr_in
*
)
&
ifr
.
ifr_addr
)
->
sin_addr
;
else
real_netmask
=
context
->
netmask
;
if
(
real_netmask
.
s_addr
&&
(
source
.
s_addr
&
real_netmask
.
s_addr
)
==
(
context
->
start
.
s_addr
&
real_netmask
.
s_addr
)
&&
(
source
.
s_addr
&
real_netmask
.
s_addr
)
==
(
context
->
end
.
s_addr
&
real_netmask
.
s_addr
))
break
;
}
if
(
!
context
)
{
syslog
(
LOG_WARNING
,
"no address range available for DHCP request via %s"
,
inet_ntoa
(
source
));
return
;
}
netmask_save
=
context
->
netmask
;
broadcast_save
=
context
->
broadcast
;
context
->
netmask
=
real_netmask
;
if
(
!
context
->
broadcast
.
s_addr
)
{
if
(
mess
->
giaddr
.
s_addr
)
context
->
broadcast
.
s_addr
=
(
mess
->
giaddr
.
s_addr
&
real_netmask
.
s_addr
)
|
~
real_netmask
.
s_addr
;
else
if
(
ioctl
(
dhcp_fd
,
SIOCGIFBRDADDR
,
&
ifr
)
!=
-
1
)
context
->
broadcast
=
((
struct
sockaddr_in
*
)
&
ifr
.
ifr_addr
)
->
sin_addr
;
else
context
->
broadcast
.
s_addr
=
(
iface_addr
.
s_addr
&
real_netmask
.
s_addr
)
|
~
real_netmask
.
s_addr
;
}
if
(
ioctl
(
dhcp_fd
,
SIOCGIFMTU
,
&
ifr
)
==
-
1
)
ifr
.
ifr_mtu
=
ETHERMTU
;
lease_prune
(
NULL
,
now
);
/* lose any expired leases */
newlen
=
dhcp_reply
(
context
,
iface_addr
,
ifr
.
ifr_name
,
ifr
.
ifr_mtu
,
rawpacket
,
sz
,
now
,
namebuff
,
dhcp_opts
,
dhcp_configs
,
domain_suffix
,
dhcp_file
,
dhcp_sname
,
dhcp_next_server
);
lease_update_file
(
0
,
now
);
lease_update_dns
();
context
->
netmask
=
netmask_save
;
context
->
broadcast
=
broadcast_save
;
if
(
newlen
==
0
)
return
;
if
(
mess
->
giaddr
.
s_addr
||
mess
->
ciaddr
.
s_addr
)
{
/* To send to BOOTP relay or configured client, use
the IP packet */
struct
sockaddr_in
dest
;
dest
.
sin_family
=
AF_INET
;
if
(
mess
->
giaddr
.
s_addr
)
{
dest
.
sin_port
=
htons
(
DHCP_SERVER_PORT
);
dest
.
sin_addr
=
mess
->
giaddr
;
}
else
{
dest
.
sin_port
=
htons
(
DHCP_CLIENT_PORT
);
dest
.
sin_addr
=
mess
->
ciaddr
;
}
sendto
(
dhcp_fd
,
mess
,
newlen
,
0
,
(
struct
sockaddr
*
)
&
dest
,
sizeof
(
dest
));
}
else
{
/* Hairy stuff, packet either has to go to the
net broadcast or the destination can't reply to ARP yet,
but we do know the physical address.
Build the packet by steam, and send directly, bypassing
the kernel IP stack */
u32
i
,
sum
;
unsigned
char
hwdest
[
ETHER_ADDR_LEN
];
if
(
ntohs
(
mess
->
flags
)
&
0x8000
)
{
memset
(
hwdest
,
255
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
INADDR_BROADCAST
;
}
else
{
memcpy
(
hwdest
,
mess
->
chaddr
,
ETHER_ADDR_LEN
);
rawpacket
->
ip
.
ip_dst
.
s_addr
=
mess
->
yiaddr
.
s_addr
;
}
rawpacket
->
ip
.
ip_p
=
IPPROTO_UDP
;
rawpacket
->
ip
.
ip_src
.
s_addr
=
iface_addr
.
s_addr
;
rawpacket
->
ip
.
ip_len
=
htons
(
sizeof
(
struct
ip
)
+
sizeof
(
struct
udphdr
)
+
newlen
)
;
rawpacket
->
ip
.
ip_hl
=
sizeof
(
struct
ip
)
/
4
;
rawpacket
->
ip
.
ip_v
=
IPVERSION
;
rawpacket
->
ip
.
ip_tos
=
0
;
rawpacket
->
ip
.
ip_id
=
htons
(
0
);
rawpacket
->
ip
.
ip_off
=
htons
(
0x4000
);
/* don't fragment */
rawpacket
->
ip
.
ip_ttl
=
IPDEFTTL
;
rawpacket
->
ip
.
ip_sum
=
0
;
for
(
sum
=
0
,
i
=
0
;
i
<
sizeof
(
struct
ip
)
/
2
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
ip
)[
i
];
while
(
sum
>>
16
)
sum
=
(
sum
&
0xffff
)
+
(
sum
>>
16
);
rawpacket
->
ip
.
ip_sum
=
(
sum
==
0xffff
)
?
sum
:
~
sum
;
rawpacket
->
udp
.
uh_sport
=
htons
(
DHCP_SERVER_PORT
);
rawpacket
->
udp
.
uh_dport
=
htons
(
DHCP_CLIENT_PORT
);
((
u8
*
)
&
rawpacket
->
data
)[
newlen
]
=
0
;
/* for checksum, in case length is odd. */
rawpacket
->
udp
.
uh_sum
=
0
;
rawpacket
->
udp
.
uh_ulen
=
sum
=
htons
(
sizeof
(
struct
udphdr
)
+
newlen
);
sum
+=
htons
(
IPPROTO_UDP
);
for
(
i
=
0
;
i
<
4
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
ip
.
ip_src
)[
i
];
for
(
i
=
0
;
i
<
(
sizeof
(
struct
udphdr
)
+
newlen
+
1
)
/
2
;
i
++
)
sum
+=
((
u16
*
)
&
rawpacket
->
udp
)[
i
];
while
(
sum
>>
16
)
sum
=
(
sum
&
0xffff
)
+
(
sum
>>
16
);
rawpacket
->
udp
.
uh_sum
=
(
sum
==
0xffff
)
?
sum
:
~
sum
;
{
#ifdef HAVE_BPF
struct
ether_header
header
;
header
.
ether_type
=
htons
(
ETHERTYPE_IP
);
memcpy
(
header
.
ether_shost
,
iface_hwaddr
,
ETHER_ADDR_LEN
);
memcpy
(
header
.
ether_dhost
,
hwdest
,
ETHER_ADDR_LEN
);
ioctl
(
raw_fd
,
BIOCSETIF
,
&
ifr
);
iov
[
0
].
iov_base
=
(
char
*
)
&
header
;
iov
[
0
].
iov_len
=
sizeof
(
struct
ether_header
);
iov
[
1
].
iov_base
=
(
char
*
)
rawpacket
;
iov
[
1
].
iov_len
=
ntohs
(
rawpacket
->
ip
.
ip_len
);
writev
(
raw_fd
,
iov
,
2
);
#else
struct
sockaddr_ll
dest
;
dest
.
sll_family
=
AF_PACKET
;
dest
.
sll_halen
=
ETHER_ADDR_LEN
;
dest
.
sll_ifindex
=
iface_index
;
dest
.
sll_protocol
=
htons
(
ETHERTYPE_IP
);
memcpy
(
dest
.
sll_addr
,
hwdest
,
ETHER_ADDR_LEN
);
sendto
(
raw_fd
,
rawpacket
,
ntohs
(
rawpacket
->
ip
.
ip_len
),
0
,
(
struct
sockaddr
*
)
&
dest
,
sizeof
(
dest
));
#endif
}
}
}
int
address_available
(
struct
dhcp_context
*
context
,
struct
in_addr
taddr
)
{
/* Check is an address is OK for this network, ie
...
...
@@ -269,18 +433,89 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
return
NULL
;
}
void
set_configs_from_cache
(
struct
dhcp_config
*
configs
)
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches. */
struct
dhcp_config
*
dhcp_read_ethers
(
struct
dhcp_config
*
configs
,
char
*
buff
)
{
FILE
*
f
=
fopen
(
ETHERSFILE
,
"r"
);
unsigned
int
e0
,
e1
,
e2
,
e3
,
e4
,
e5
;
char
*
ip
,
*
cp
,
*
name
;
struct
in_addr
addr
;
struct
dhcp_config
*
new
;
if
(
!
f
)
die
(
"Failed to open "
ETHERSFILE
":%s"
,
NULL
);
while
(
fgets
(
buff
,
MAXDNAME
,
f
))
{
while
(
strlen
(
buff
)
>
0
&&
(
buff
[
strlen
(
buff
)
-
1
]
==
'\n'
||
buff
[
strlen
(
buff
)
-
1
]
==
' '
||
buff
[
strlen
(
buff
)
-
1
]
==
'\t'
))
buff
[
strlen
(
buff
)
-
1
]
=
0
;
if
((
*
buff
==
'#'
)
||
(
*
buff
==
'+'
))
continue
;
for
(
ip
=
buff
;
*
ip
&&
*
ip
!=
' '
;
ip
++
);
for
(;
*
ip
&&
*
ip
==
' '
;
ip
++
)
*
ip
=
0
;
if
(
!*
ip
)
continue
;
if
(
!
sscanf
(
buff
,
"%x:%x:%x:%x:%x:%x"
,
&
e0
,
&
e1
,
&
e2
,
&
e3
,
&
e4
,
&
e5
))
continue
;
/* check for name or dotted-quad */
for
(
cp
=
ip
;
*
cp
;
cp
++
)
if
(
!
(
*
cp
==
'.'
||
(
*
cp
>=
'0'
&&
*
cp
<=
'9'
)))
break
;
if
(
!*
cp
)
{
name
=
NULL
;
if
((
addr
.
s_addr
=
inet_addr
(
ip
))
==
(
in_addr_t
)
-
1
)
continue
;
}
else
{
name
=
safe_string_alloc
(
ip
);
addr
.
s_addr
=
0
;
}
new
=
safe_malloc
(
sizeof
(
struct
dhcp_config
));
new
->
clid_len
=
0
;
new
->
clid
=
NULL
;
new
->
hwaddr
[
0
]
=
e0
;
new
->
hwaddr
[
1
]
=
e1
;
new
->
hwaddr
[
2
]
=
e2
;
new
->
hwaddr
[
3
]
=
e3
;
new
->
hwaddr
[
4
]
=
e4
;
new
->
hwaddr
[
5
]
=
e5
;
new
->
hostname
=
name
;
new
->
addr
=
addr
;
new
->
lease_time
=
0
;
new
->
next
=
configs
;
configs
=
new
;
}
fclose
(
f
);
return
configs
;
}
void
dhcp_update_configs
(
struct
dhcp_config
*
configs
)
{
/* Some people like to keep all static IP addresses in /etc/hosts.
This goes through /etc/hosts and sets static addresses for any DHCP config
records which don't have an address and whose name matches. */
struct
dhcp_config
*
config
;
struct
crec
*
crec
;
for
(
config
=
configs
;
config
;
config
=
config
->
next
)
if
(
config
->
addr
.
s_addr
==
0
&&
config
->
hostname
&&
(
crec
=
cache_find_by_name
(
NULL
,
config
->
hostname
,
0
,
F_IPV4
))
&&
(
crec
->
flags
&
F_HOSTS
))
config
->
addr
=
crec
->
addr
.
addr
.
addr4
;
}
src/dnsmasq.c
View file @
44a2a316
...
...
@@ -16,7 +16,7 @@
#include "dnsmasq.h"
static
int
sigterm
,
sighup
,
sigusr1
,
sig
usr2
;
static
int
sigterm
,
sighup
,
sigusr1
,
sig
alarm
;
static
void
sig_handler
(
int
sig
)
{
...
...
@@ -26,23 +26,24 @@ static void sig_handler(int sig)
sighup
=
1
;
else
if
(
sig
==
SIGUSR1
)
sigusr1
=
1
;
else
if
(
sig
==
SIG
USR2
)
sig
usr2
=
1
;
else
if
(
sig
==
SIG
ALRM
)
sig
alarm
=
1
;
}
int
main
(
int
argc
,
char
**
argv
)
{
char
*
int_err_string
;
int
cachesize
=
CACHESIZ
;
int
port
=
NAMESERVER_PORT
;
int
maxleases
=
MAXLEASES
;
int
query_port
=
0
;
int
first_loop
=
1
;
unsigned
long
local_ttl
=
0
;
unsigned
int
options
;
unsigned
int
options
,
min_leasetime
;
char
*
runfile
=
RUNFILE
;
time_t
resolv_changed
=
0
;
time_t
now
,
last
=
0
;
struct
irec
*
iface
,
*
interfaces
=
NULL
;
struct
irec
*
interfaces
=
NULL
;
struct
listener
*
listener
,
*
listeners
;
char
*
mxname
=
NULL
;
char
*
mxtarget
=
NULL
;
char
*
lease_file
=
NULL
;
...
...
@@ -53,9 +54,9 @@ int main (int argc, char **argv)
struct
iname
*
if_names
=
NULL
;
struct
iname
*
if_addrs
=
NULL
;
struct
iname
*
if_except
=
NULL
;
struct
iname
*
if_tmp
;
struct
server
*
serv_addrs
=
NULL
;
char
*
dnamebuff
,
*
packet
;
int
uptime_fd
=
-
1
;
struct
server
*
servers
,
*
last_server
;
struct
resolvc
default_resolv
=
{
NULL
,
1
,
0
,
RESOLVFILE
};
struct
resolvc
*
resolv
=
&
default_resolv
;
...
...
@@ -66,29 +67,33 @@ int main (int argc, char **argv)
struct
dhcp_opt
*
dhcp_options
=
NULL
;
char
*
dhcp_file
=
NULL
,
*
dhcp_sname
=
NULL
;
struct
in_addr
dhcp_next_server
;
int
leasefd
=
0
;
int
leasefd
=
-
1
,
dhcpfd
=
-
1
,
dhcp_raw_fd
=
-
1
;
struct
sigaction
sigact
;
sigset_t
sigmask
;
sighup
=
1
;
/* init cache the first time through */
sigusr1
=
0
;
/* but don't dump */
sigusr2
=
0
;
/* or rescan interfaces */
sigterm
=
0
;
/* or die */
#ifdef HAVE_BROKEN_RTC
sigalarm
=
1
;
/* need regular lease dumps */
#else
sigalarm
=
0
;
/* or not */
#endif
sigact
.
sa_handler
=
sig_handler
;
sigact
.
sa_flags
=
0
;
sigemptyset
(
&
sigact
.
sa_mask
);
sigaction
(
SIGUSR1
,
&
sigact
,
NULL
);
sigaction
(
SIGUSR2
,
&
sigact
,
NULL
);
sigaction
(
SIGHUP
,
&
sigact
,
NULL
);
sigaction
(
SIGTERM
,
&
sigact
,
NULL
);
sigaction
(
SIGALRM
,
&
sigact
,
NULL
);
/* now block all the signals, they stay that way except
during the call to pselect */
sigaddset
(
&
sigact
.
sa_mask
,
SIGUSR1
);
sigaddset
(
&
sigact
.
sa_mask
,
SIGUSR2
);
sigaddset
(
&
sigact
.
sa_mask
,
SIGTERM
);
sigaddset
(
&
sigact
.
sa_mask
,
SIGHUP
);
sigaddset
(
&
sigact
.
sa_mask
,
SIGALRM
);
sigprocmask
(
SIG_BLOCK
,
&
sigact
.
sa_mask
,
&
sigmask
);
/* These get allocated here to avoid overflowing the small stack
...
...
@@ -96,17 +101,21 @@ int main (int argc, char **argv)
maximal sixed domain name and gets passed into all the processing
code. We manage to get away with one buffer. */
dnamebuff
=
safe_malloc
(
MAXDNAME
);
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
packet
=
safe_malloc
(
PACKETSZ
+
MAXDNAME
+
RRFIXEDSZ
);
packet
=
safe_malloc
(
DNSMASQ_PACKETSZ
);
dhcp_next_server
.
s_addr
=
0
;
options
=
read_opts
(
argc
,
argv
,
dnamebuff
,
&
resolv
,
&
mxname
,
&
mxtarget
,
&
lease_file
,
&
username
,
&
groupname
,
&
domain_suffix
,
&
runfile
,
&
if_names
,
&
if_addrs
,
&
if_except
,
&
bogus_addr
,
&
serv_addrs
,
&
cachesize
,
&
port
,
&
query_port
,
&
local_ttl
,
&
addn_hosts
,
&
dhcp
,
&
dhcp_configs
,
&
dhcp_options
,
&
dhcp_file
,
&
dhcp_sname
,
&
dhcp_next_server
);
&
dhcp
,
&
dhcp_configs
,
&
dhcp_options
,
&
dhcp_file
,
&
dhcp_sname
,
&
dhcp_next_server
,
&
maxleases
,
&
min_leasetime
);
/* if we cannot support binding the wildcard address, set the "bind only
interfaces in use" option */
#ifndef HAVE_UDP_SRC_DST
options
|=
OPT_NOWILD
;
#endif
if
(
!
lease_file
)
lease_file
=
LEASEFILE
;
...
...
@@ -121,46 +130,32 @@ int main (int argc, char **argv)
}
}
if
((
int_err_string
=
enumerate_interfaces
(
&
interfaces
,
if_names
,
if_addrs
,
if_except
,
dhcp
,
port
)))
die
(
int_err_string
,
NULL
);
for
(
if_tmp
=
if_names
;
if_tmp
;
if_tmp
=
if_tmp
->
next
)
if
(
if_tmp
->
name
&&
!
if_tmp
->
found
)
die
(
"unknown interface %s"
,
if_tmp
->
name
);
for
(
if_tmp
=
if_addrs
;
if_tmp
;
if_tmp
=
if_tmp
->
next
)
if
(
!
if_tmp
->
found
)
{
#ifdef HAVE_IPV6
if
(
if_tmp
->
addr
.
sa
.
sa_family
==
AF_INET
)
inet_ntop
(
AF_INET
,
&
if_tmp
->
addr
.
in
.
sin_addr
,
dnamebuff
,
MAXDNAME
);
else
inet_ntop
(
AF_INET6
,
&
if_tmp
->
addr
.
in6
.
sin6_addr
,
dnamebuff
,
MAXDNAME
);
die
(
"no interface with address %s"
,
dnamebuff
);
#else
die
(
"no interface with address %s"
,
inet_ntoa
(
if_tmp
->
addr
.
in
.
sin_addr
));
#endif
}
interfaces
=
enumerate_interfaces
(
if_names
,
if_addrs
,
if_except
,
port
);
if
(
options
&
OPT_NOWILD
)
listeners
=
create_bound_listeners
(
interfaces
);
else
listeners
=
create_wildcard_listeners
(
port
);
forward_init
(
1
);
cache_init
(
cachesize
,
options
&
OPT_LOG
);
#ifdef HAVE_BROKEN_RTC
if
((
uptime_fd
=
open
(
UPTIME
,
O_RDONLY
))
==
-
1
)
die
(
"cannot open "
UPTIME
":%s"
,
NULL
);
#endif
now
=
dnsmasq_time
(
uptime_fd
);
if
(
dhcp
)
{
#if !defined(HAVE_PF_PACKET) && !defined(HAVE_BPF)
die
(
"no DHCP support available on this OS."
,
NULL
);
#endif
for
(
dhcp_tmp
=
dhcp
;
dhcp_tmp
;
dhcp_tmp
=
dhcp_tmp
->
next
)
if
(
!
dhcp_tmp
->
iface
)
die
(
"No suitable interface for DHCP service at address %s"
,
inet_ntoa
(
dhcp_tmp
->
start
));
set_configs_from_cache
(
dhcp_configs
);
leasefd
=
lease_init
(
lease_file
,
domain_suffix
,
dnamebuff
,
packet
,
time
(
NULL
),
dhcp_configs
);
lease_update_dns
(
1
);
/* must follow cache_init and lease_init */
dhcp_init
(
&
dhcpfd
,
&
dhcp_raw_fd
);
leasefd
=
lease_init
(
lease_file
,
domain_suffix
,
dnamebuff
,
packet
,
now
,
maxleases
);
if
(
options
&
OPT_ETHERS
)
dhcp_configs
=
dhcp_read_ethers
(
dhcp_configs
,
dnamebuff
);
dhcp_update_configs
(
dhcp_configs
);
lease_update_from_configs
(
dhcp_configs
,
domain_suffix
);
/* must follow cache_init and lease_init */
lease_update_file
(
0
,
now
);
lease_update_dns
();
}
setbuf
(
stdout
,
NULL
);
...
...
@@ -198,19 +193,16 @@ int main (int argc, char **argv)
for
(
i
=
0
;
i
<
64
;
i
++
)
{
for
(
iface
=
interfaces
;
iface
;
iface
=
iface
->
next
)
if
(
iface
->
fd
==
i
)
for
(
listener
=
listeners
;
listener
;
listener
=
listener
->
next
)
if
(
listener
->
fd
==
i
)
break
;
if
(
iface
)
if
(
listener
)
continue
;
for
(
dhcp_tmp
=
dhcp
;
dhcp_tmp
;
dhcp_tmp
=
dhcp_tmp
->
next
)
if
(
dhcp_tmp
->
fd
==
i
||
dhcp_tmp
->
rawfd
==
i
)
break
;
if
(
dhcp_tmp
)
continue
;
if
(
dhcp
&&
(
i
==
leasefd
))
if
(
i
==
leasefd
||
i
==
uptime_fd
||
i
==
dhcpfd
||
i
==
dhcp_raw_fd
)
continue
;
close
(
i
);
...
...
@@ -255,10 +247,15 @@ int main (int argc, char **argv)
sprintf
(
packet
,
"infinite"
);
else
sprintf
(
packet
,
"%ds"
,
(
int
)
dhcp_tmp
->
lease_time
);
syslog
(
LOG_INFO
,
"DHCP
on %s
, IP range %s -- %s, lease time %s"
,
d
hcp_tmp
->
iface
,
d
namebuff
,
inet_ntoa
(
dhcp_tmp
->
end
),
packet
);
syslog
(
LOG_INFO
,
"DHCP, IP range %s -- %s, lease time %s"
,
dnamebuff
,
inet_ntoa
(
dhcp_tmp
->
end
),
packet
);
}
#ifdef HAVE_BROKEN_RTC
if
(
dhcp
)
syslog
(
LOG_INFO
,
"DHCP, %s will be written every %ds"
,
lease_file
,
min_leasetime
/
3
);
#endif
if
(
getuid
()
==
0
||
geteuid
()
==
0
)
syslog
(
LOG_WARNING
,
"failed to drop root privs"
);
...
...
@@ -271,8 +268,13 @@ int main (int argc, char **argv)
if
(
sighup
)
{
cache_reload
(
options
,
dnamebuff
,
domain_suffix
,
addn_hosts
);
set_configs_from_cache
(
dhcp_configs
);
lease_update_dns
(
1
);
if
(
dhcp
)
{
dhcp_update_configs
(
dhcp_configs
);
lease_update_from_configs
(
dhcp_configs
,
domain_suffix
);
lease_update_file
(
0
,
now
);
lease_update_dns
();
}
if
(
resolv
&&
(
options
&
OPT_NO_POLL
))
servers
=
last_server
=
check_servers
(
reload_servers
(
resolv
->
name
,
dnamebuff
,
servers
,
query_port
),
...
...
@@ -286,18 +288,16 @@ int main (int argc, char **argv)
sigusr1
=
0
;
}
if
(
sig
usr2
)
if
(
sig
alarm
)
{
if
(
getuid
()
!=
0
&&
port
<=
1024
)
syslog
(
LOG_ERR
,
"cannot re-scan interfaces unless --user=root"
);
else
{
syslog
(
LOG_INFO
,
"rescanning network interfaces"
);
int_err_string
=
enumerate_interfaces
(
&
interfaces
,
if_names
,
if_addrs
,
if_except
,
NULL
,
port
);
if
(
int_err_string
)
syslog
(
LOG_ERR
,
int_err_string
,
strerror
(
errno
));
}
sigusr2
=
0
;
if
(
dhcp
)
{
lease_update_file
(
1
,
now
);
#ifdef HAVE_BROKEN_RTC
alarm
(
min_leasetime
/
3
);
#endif
}
sigalarm
=
0
;
}
FD_ZERO
(
&
rset
);
...
...
@@ -313,19 +313,20 @@ int main (int argc, char **argv)
maxfd
=
serverfdp
->
fd
;
}
for
(
iface
=
interfaces
;
iface
;
iface
=
iface
->
next
)
for
(
listener
=
listeners
;
listener
;
listener
=
listener
->
next
)
{
FD_SET
(
iface
->
fd
,
&
rset
);
if
(
iface
->
fd
>
maxfd
)
maxfd
=
iface
->
fd
;
FD_SET
(
listener
->
fd
,
&
rset
);
if
(
listener
->
fd
>
maxfd
)
maxfd
=
listener
->
fd
;
}
for
(
dhcp_tmp
=
dhcp
;
dhcp_tmp
;
dhcp_tmp
=
dhcp_tmp
->
next
)
if
(
dhcp
)
{
FD_SET
(
dhcp
_tmp
->
fd
,
&
rset
);
if
(
dhcp
_tmp
->
fd
>
maxfd
)
maxfd
=
dhcp
_tmp
->
fd
;
FD_SET
(
dhcpfd
,
&
rset
);
if
(
dhcpfd
>
maxfd
)
maxfd
=
dhcpfd
;
}
#ifdef HAVE_PSELECT
if
(
pselect
(
maxfd
+
1
,
&
rset
,
NULL
,
NULL
,
NULL
,
&
sigmask
)
<
0
)
FD_ZERO
(
&
rset
);
/* rset otherwise undefined after error */
...
...
@@ -342,7 +343,7 @@ int main (int argc, char **argv)
}
first_loop
=
0
;
now
=
time
(
NULL
);
now
=
dnsmasq_time
(
uptime_fd
);
/* Check for changes to resolv files once per second max. */
if
(
last
==
0
||
difftime
(
now
,
last
)
>
1
.
0
)
...
...
@@ -391,62 +392,28 @@ int main (int argc, char **argv)
last_server
=
reply_query
(
serverfdp
->
fd
,
options
,
packet
,
now
,
dnamebuff
,
last_server
,
bogus_addr
);
for
(
dhcp_tmp
=
dhcp
;
dhcp_tmp
;
dhcp_tmp
=
dhcp_tmp
->
next
)
if
(
FD_ISSET
(
dhcp_tmp
->
fd
,
&
rset
))
dhcp_packet
(
dhcp_tmp
,
packet
,
dhcp_options
,
dhcp_configs
,
now
,
dnamebuff
,
domain_suffix
,
dhcp_file
,
dhcp_sname
,
dhcp_next_server
);
if
(
dhcp
&&
FD_ISSET
(
dhcpfd
,
&
rset
)
)
dhcp_packet
(
dhcp
,
packet
,
dhcp_options
,
dhcp_configs
,
now
,
dnamebuff
,
domain_suffix
,
dhcp_file
,
dhcp_sname
,
dhcp_next_server
,
dhcpfd
,
dhcp_raw_fd
,
if_names
,
if_addrs
,
if_except
);
for
(
iface
=
interfaces
;
iface
;
iface
=
iface
->
next
)
{
if
(
FD_ISSET
(
iface
->
fd
,
&
rset
))
{
/* request packet, deal with query */
union
mysockaddr
udpaddr
;
socklen_t
udplen
=
sizeof
(
udpaddr
);
HEADER
*
header
=
(
HEADER
*
)
packet
;
int
m
,
n
=
recvfrom
(
iface
->
fd
,
packet
,
PACKETSZ
,
0
,
&
udpaddr
.
sa
,
&
udplen
);
udpaddr
.
sa
.
sa_family
=
iface
->
addr
.
sa
.
sa_family
;
#ifdef HAVE_IPV6
if
(
udpaddr
.
sa
.
sa_family
==
AF_INET6
)
udpaddr
.
in6
.
sin6_flowinfo
=
htonl
(
0
);
#endif
if
(
n
>=
(
int
)
sizeof
(
HEADER
)
&&
!
header
->
qr
)
{
if
(
extract_request
(
header
,
(
unsigned
int
)
n
,
dnamebuff
))
{
if
(
udpaddr
.
sa
.
sa_family
==
AF_INET
)
log_query
(
F_QUERY
|
F_IPV4
|
F_FORWARD
,
dnamebuff
,
(
struct
all_addr
*
)
&
udpaddr
.
in
.
sin_addr
);
#ifdef HAVE_IPV6
else
log_query
(
F_QUERY
|
F_IPV6
|
F_FORWARD
,
dnamebuff
,
(
struct
all_addr
*
)
&
udpaddr
.
in6
.
sin6_addr
);
#endif
}
m
=
answer_request
(
header
,
((
char
*
)
header
)
+
PACKETSZ
,
(
unsigned
int
)
n
,
mxname
,
mxtarget
,
options
,
now
,
local_ttl
,
dnamebuff
);
if
(
m
>=
1
)
{
/* answered from cache, send reply */
sendto
(
iface
->
fd
,
(
char
*
)
header
,
m
,
0
,
&
udpaddr
.
sa
,
sa_len
(
&
udpaddr
));
}
else
{
/* cannot answer from cache, send on to real nameserver */
last_server
=
forward_query
(
iface
->
fd
,
&
udpaddr
,
header
,
n
,
options
,
dnamebuff
,
servers
,
last_server
,
now
,
local_ttl
);
}
}
}
}
for
(
listener
=
listeners
;
listener
;
listener
=
listener
->
next
)
if
(
FD_ISSET
(
listener
->
fd
,
&
rset
))
last_server
=
receive_query
(
listener
,
packet
,
mxname
,
mxtarget
,
options
,
now
,
local_ttl
,
dnamebuff
,
if_names
,
if_addrs
,
if_except
,
last_server
,
servers
);
}
syslog
(
LOG_INFO
,
"exiting on receipt of SIGTERM"
);
#ifdef HAVE_BROKEN_RTC
if
(
dhcp
)
lease_update_file
(
1
,
now
);
#endif
if
(
leasefd
!=
-
1
)
close
(
leasefd
);
return
0
;
}
...
...
src/dnsmasq.h
View file @
44a2a316
...
...
@@ -11,23 +11,24 @@
*/
/* Author's email: simon@thekelleys.org.uk */
#ifdef __linux__
/* for pselect.... */
#define _XOPEN_SOURCE 600
#define _XOPEN_SOURCE 600
/* but then DNS headers don't compile without.... */
#define _BSD_SOURCE
/* and also, on FreeBSD 5.0 ..... */
#define __BSD_VISIBLE 1
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/types.h>
#include <netinet/in.h>
/* get this before config.h too. */
#include <syslog.h>
#include "config.h"
#include <arpa/nameser.h>
#include <arpa/inet.h>
#include <sys/stat.h>
...
...
@@ -36,7 +37,7 @@
#include <sys/ioctl.h>
#include <sys/select.h>
#if defined(__sun) || defined(__sun__)
#include <sys/sockio.h>
#
include <sys/sockio.h>
#endif
#include <sys/time.h>
#include <net/if.h>
...
...
@@ -54,19 +55,26 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <net/ethernet.h>
#if defined(__OpenBSD__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
#endif
#include <net/if_arp.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#ifdef HAVE_PF_PACKET
#include <netpacket/packet.h>
#endif
#ifdef HAVE_BPF
#include <net/bpf.h>
#include <net/if_dl.h>
# include <net/bpf.h>
# include <net/if_dl.h>
#else
# include <netpacket/packet.h>
#endif
#include <sys/uio.h>
/* Size: we check after adding each record, so there must be
memory for the largest packet, and the largest record */
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV 1
#define OPT_FILTER 2
#define OPT_LOG 4
...
...
@@ -80,6 +88,8 @@
#define OPT_LOCALMX 1024
#define OPT_NO_NEG 2048
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
struct
all_addr
{
union
{
...
...
@@ -175,20 +185,20 @@ struct server {
struct
server
*
next
;
};
/* linked list of all the interfaces in the system and
the sockets we have bound to each one. */
struct
irec
{
union
mysockaddr
addr
;
int
fd
;
int
valid
;
struct
irec
*
next
;
};
struct
listener
{
int
fd
,
family
;
struct
listener
*
next
;
};
/* interface and address parms from command line. */
struct
iname
{
char
*
name
;
union
mysockaddr
addr
;
int
found
;
struct
iname
*
next
;
};
...
...
@@ -202,6 +212,7 @@ struct resolvc {
struct
frec
{
union
mysockaddr
source
;
struct
all_addr
dest
;
struct
server
*
sentto
;
unsigned
short
orig_id
,
new_id
;
int
fd
;
...
...
@@ -232,16 +243,15 @@ struct dhcp_config {
struct
dhcp_opt
{
int
opt
,
len
,
is_addr
;
unsigned
char
*
val
;
char
*
netid
;
struct
dhcp_opt
*
next
;
};
struct
dhcp_context
{
int
fd
,
rawfd
,
ifindex
;
char
*
iface
;
unsigned
char
hwaddr
[
ETHER_ADDR_LEN
];
unsigned
int
lease_time
;
struct
in_addr
serv_addr
,
netmask
,
broadcast
;
struct
in_addr
netmask
,
broadcast
;
struct
in_addr
start
,
end
,
last
;
/* range of available addresses */
char
*
netid
;
struct
dhcp_context
*
next
;
};
...
...
@@ -313,7 +323,7 @@ char *safe_string_alloc(char *cp);
int
sa_len
(
union
mysockaddr
*
addr
);
int
sockaddr_isequal
(
union
mysockaddr
*
s1
,
union
mysockaddr
*
s2
);
int
hostname_isequal
(
unsigned
char
*
a
,
unsigned
char
*
b
);
time_t
dnsmasq_time
(
int
fd
);
/* option.c */
unsigned
int
read_opts
(
int
argc
,
char
**
argv
,
char
*
buff
,
struct
resolvc
**
resolv_file
,
char
**
mxname
,
char
**
mxtarget
,
char
**
lease_file
,
...
...
@@ -323,35 +333,37 @@ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resol
struct
bogus_addr
**
bogus_addr
,
struct
server
**
serv_addrs
,
int
*
cachesize
,
int
*
port
,
int
*
query_port
,
unsigned
long
*
local_ttl
,
char
**
addn_hosts
,
struct
dhcp_context
**
dhcp
,
struct
dhcp_config
**
dhcp_conf
,
struct
dhcp_opt
**
opts
,
char
**
dhcp_file
,
char
**
dhcp_sname
,
struct
in_addr
*
dhcp_next_server
);
char
**
dhcp_file
,
char
**
dhcp_sname
,
struct
in_addr
*
dhcp_next_server
,
int
*
maxleases
,
unsigned
int
*
min_leasetime
);
/* forward.c */
void
forward_init
(
int
first
);
void
reap_forward
(
int
fd
);
struct
server
*
forward_query
(
int
udpfd
,
union
mysockaddr
*
udpaddr
,
HEADER
*
header
,
int
plen
,
unsigned
int
options
,
char
*
dnamebuff
,
struct
server
*
servers
,
struct
server
*
last_server
,
time_t
now
,
unsigned
long
local_ttl
);
struct
server
*
reply_query
(
int
fd
,
int
options
,
char
*
packet
,
time_t
now
,
char
*
dnamebuff
,
struct
server
*
last_server
,
struct
bogus_addr
*
bogus_nxdomain
);
struct
server
*
receive_query
(
struct
listener
*
listen
,
char
*
packet
,
char
*
mxname
,
char
*
mxtarget
,
unsigned
int
options
,
time_t
now
,
unsigned
long
local_ttl
,
char
*
namebuff
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
struct
server
*
last_server
,
struct
server
*
servers
);
/* network.c */
struct
server
*
reload_servers
(
char
*
fname
,
char
*
buff
,
struct
server
*
servers
,
int
query_port
);
struct
server
*
check_servers
(
struct
server
*
new
,
struct
irec
*
interfaces
,
struct
serverfd
**
sfds
);
char
*
enumerate_interfaces
(
struct
irec
**
interfaces
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
struct
dhcp_context
*
dhcp
,
int
port
);
struct
irec
*
enumerate_interfaces
(
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
int
port
);
struct
listener
*
create_wildcard_listeners
(
int
port
);
struct
listener
*
create_bound_listeners
(
struct
irec
*
interfaces
);
/* dhcp.c */
void
dhcp_
packet
(
struct
dhcp_context
*
context
,
char
*
packet
,
struct
dhcp_opt
*
dhcp_opts
,
struct
dhcp_
config
*
dhcp_configs
,
void
dhcp_
init
(
int
*
fdp
,
int
*
rfdp
);
void
dhcp_packet
(
struct
dhcp_context
*
contexts
,
char
*
packet
,
struct
dhcp_
opt
*
dhcp_opts
,
struct
dhcp_config
*
dhcp_configs
,
time_t
now
,
char
*
namebuff
,
char
*
domain_suffix
,
char
*
dhcp_file
,
char
*
dhcp_sname
,
struct
in_addr
dhcp_next_server
);
char
*
dhcp_file
,
char
*
dhcp_sname
,
struct
in_addr
dhcp_next_server
,
int
dhcp_fd
,
int
raw_fd
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
);
int
address_available
(
struct
dhcp_context
*
context
,
struct
in_addr
addr
);
int
address_allocate
(
struct
dhcp_context
*
context
,
struct
dhcp_config
*
configs
,
struct
in_addr
*
addrp
);
...
...
@@ -359,12 +371,14 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
struct
dhcp_context
*
context
,
unsigned
char
*
clid
,
int
clid_len
,
unsigned
char
*
hwaddr
,
char
*
hostname
);
void
set_configs_from_cache
(
struct
dhcp_config
*
configs
);
struct
dhcp_config
*
read_ethers
(
struct
dhcp_config
*
configs
,
char
*
buff
);
void
dhcp_update_configs
(
struct
dhcp_config
*
configs
);
struct
dhcp_config
*
dhcp_read_ethers
(
struct
dhcp_config
*
configs
,
char
*
buff
);
/* lease.c */
void
lease_update_dns
(
int
force_dns
);
void
lease_update_file
(
int
force
,
time_t
now
);
void
lease_update_dns
(
void
);
int
lease_init
(
char
*
lease_file
,
char
*
domain
,
char
*
buff
,
char
*
buff2
,
time_t
now
,
struct
dhcp_config
*
dhcp_config
s
);
char
*
buff2
,
time_t
now
,
int
maxlease
s
);
struct
dhcp_lease
*
lease_allocate
(
unsigned
char
*
clid
,
int
clid_len
,
struct
in_addr
addr
);
void
lease_set_hwaddr
(
struct
dhcp_lease
*
lease
,
unsigned
char
*
hwaddr
);
void
lease_set_hostname
(
struct
dhcp_lease
*
lease
,
char
*
name
,
char
*
suffix
);
...
...
@@ -372,10 +386,15 @@ void lease_set_expires(struct dhcp_lease *lease, time_t exp);
struct
dhcp_lease
*
lease_find_by_client
(
unsigned
char
*
clid
,
int
clid_len
);
struct
dhcp_lease
*
lease_find_by_addr
(
struct
in_addr
addr
);
void
lease_prune
(
struct
dhcp_lease
*
target
,
time_t
now
);
void
lease_update_from_configs
(
struct
dhcp_config
*
dhcp_configs
,
char
*
domain
);
/* rfc2131.c */
int
dhcp_reply
(
struct
dhcp_context
*
context
,
struct
dhcp_packet
*
mess
,
int
dhcp_reply
(
struct
dhcp_context
*
context
,
struct
in_addr
iface_addr
,
char
*
iface_name
,
int
iface_mtu
,
struct
udp_dhcp_packet
*
rawpacket
,
unsigned
int
sz
,
time_t
now
,
char
*
namebuff
,
struct
dhcp_opt
*
dhcp_opts
,
struct
dhcp_config
*
dhcp_configs
,
struct
dhcp_opt
*
dhcp_opts
,
struct
dhcp_config
*
dhcp_configs
,
char
*
domain_suffix
,
char
*
dhcp_file
,
char
*
dhcp_sname
,
struct
in_addr
dhcp_next_server
);
src/forward.c
View file @
44a2a316
...
...
@@ -33,21 +33,91 @@ void forward_init(int first)
f
->
new_id
=
0
;
}
/* delete all forward records recieved from socket fd */
void
reap_forward
(
int
fd
)
/* Send a UDP packet with it's source address set as "source"
unless nowild is true, when we just send it with the kernel default */
static
void
send_from
(
int
fd
,
int
nowild
,
char
*
packet
,
int
len
,
union
mysockaddr
*
to
,
struct
all_addr
*
source
)
{
struct
frec
*
f
;
struct
msghdr
msg
;
struct
iovec
iov
[
1
];
struct
cmsghdr
*
cmptr
;
union
{
struct
cmsghdr
align
;
/* this ensures alignment */
#if defined(IP_PKTINFO)
char
control
[
CMSG_SPACE
(
sizeof
(
struct
in_pktinfo
))];
#elif defined(IP_SENDSRCADDR)
char
control
[
CMSG_SPACE
(
sizeof
(
struct
in_addr
))];
#endif
#ifdef HAVE_IPV6
char
control6
[
CMSG_SPACE
(
sizeof
(
struct
in6_pktinfo
))];
#endif
}
control_u
;
iov
[
0
].
iov_base
=
packet
;
iov
[
0
].
iov_len
=
len
;
if
(
nowild
)
{
msg
.
msg_control
=
NULL
;
msg
.
msg_controllen
=
0
;
}
else
{
msg
.
msg_control
=
&
control_u
;
msg
.
msg_controllen
=
sizeof
(
control_u
);
}
msg
.
msg_flags
=
0
;
msg
.
msg_name
=
to
;
msg
.
msg_namelen
=
sa_len
(
to
);
msg
.
msg_iov
=
iov
;
msg
.
msg_iovlen
=
1
;
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
#if defined(IP_PKTINFO)
if
(
!
nowild
&&
to
->
sa
.
sa_family
==
AF_INET
)
{
struct
in_pktinfo
*
pkt
=
(
struct
in_pktinfo
*
)
CMSG_DATA
(
cmptr
);
pkt
->
ipi_ifindex
=
0
;
pkt
->
ipi_spec_dst
=
source
->
addr
.
addr4
;
msg
.
msg_controllen
=
cmptr
->
cmsg_len
=
CMSG_LEN
(
sizeof
(
struct
in_pktinfo
));
cmptr
->
cmsg_level
=
SOL_IP
;
cmptr
->
cmsg_type
=
IP_PKTINFO
;
}
#elif defined(IP_SENDSRCADDR)
if
(
!
nowild
&&
to
->
sa
.
sa_family
==
AF_INET
)
{
struct
in_addr
*
a
=
(
struct
in_addr
*
)
CMSG_DATA
(
cmptr
);
*
a
=
source
->
addr
.
addr4
;
msg
.
msg_controllen
=
cmptr
->
cmsg_len
=
CMSG_LEN
(
sizeof
(
struct
in_addr
));
cmptr
->
cmsg_level
=
IPPROTO_IP
;
cmptr
->
cmsg_type
=
IP_SENDSRCADDR
;
}
#endif
#ifdef HAVE_IPV6
if
(
!
nowild
&&
to
->
sa
.
sa_family
==
AF_INET6
)
{
struct
in6_pktinfo
*
pkt
=
(
struct
in6_pktinfo
*
)
CMSG_DATA
(
cmptr
);
pkt
->
ipi6_ifindex
=
0
;
pkt
->
ipi6_addr
=
source
->
addr
.
addr6
;
msg
.
msg_controllen
=
cmptr
->
cmsg_len
=
CMSG_LEN
(
sizeof
(
struct
in6_pktinfo
));
cmptr
->
cmsg_type
=
IPV6_PKTINFO
;
cmptr
->
cmsg_level
=
IPV6_LEVEL
;
cmptr
->
cmsg_level
=
IPPROTO_IPV6
;
}
#endif
for
(
f
=
frec_list
;
f
;
f
=
f
->
next
)
if
(
f
->
fd
==
fd
)
f
->
new_id
=
0
;
sendmsg
(
fd
,
&
msg
,
0
);
}
/* returns new last_server */
struct
server
*
forward_query
(
int
udpfd
,
union
mysockaddr
*
udpaddr
,
HEADER
*
header
,
int
plen
,
unsigned
int
options
,
char
*
dnamebuff
,
struct
server
*
servers
,
struct
server
*
last_server
,
time_t
now
,
unsigned
long
local_ttl
)
static
struct
server
*
forward_query
(
int
udpfd
,
union
mysockaddr
*
udpaddr
,
struct
all_addr
*
dst_addr
,
HEADER
*
header
,
int
plen
,
unsigned
int
options
,
char
*
dnamebuff
,
struct
server
*
servers
,
struct
server
*
last_server
,
time_t
now
,
unsigned
long
local_ttl
)
{
struct
frec
*
forward
;
char
*
domain
=
NULL
;
...
...
@@ -113,10 +183,10 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
if
(
serv
->
flags
&
SERV_LITERAL_ADDRESS
)
{
/* flags gets set if server is in fact an answer */
unsigned
short
sflag
=
serv
->
addr
.
sa
.
sa_family
==
AF_INET
?
F_IPV4
:
F_IPV6
;
if
(
sflag
&
gotname
)
/* only OK if addrfamily == query */
if
(
(
sflag
|
F_QUERY
)
&
gotname
)
/* only OK if addrfamily == query */
{
type
=
SERV_HAS_DOMAIN
;
flags
=
sflag
;
flags
=
gotname
;
domain
=
serv
->
domain
;
matchlen
=
domainlen
;
if
(
serv
->
addr
.
sa
.
sa_family
==
AF_INET
)
...
...
@@ -139,7 +209,12 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
}
if
(
flags
)
/* flags set here means a literal found */
log_query
(
F_CONFIG
|
F_FORWARD
|
flags
,
dnamebuff
,
addrp
);
{
if
(
flags
&
F_QUERY
)
log_query
(
F_CONFIG
|
F_FORWARD
|
F_NEG
,
dnamebuff
,
NULL
);
else
log_query
(
F_CONFIG
|
F_FORWARD
|
flags
,
dnamebuff
,
addrp
);
}
else
{
/* we may by policy not forward names without a domain part */
...
...
@@ -162,6 +237,7 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
forward
->
sentto
=
last_server
;
forward
->
source
=
*
udpaddr
;
forward
->
dest
=
*
dst_addr
;
forward
->
new_id
=
get_id
();
forward
->
fd
=
udpfd
;
forward
->
orig_id
=
ntohs
(
header
->
id
);
...
...
@@ -228,8 +304,8 @@ struct server *forward_query(int udpfd, union mysockaddr *udpaddr, HEADER *heade
/* could not send on, return empty answer or address if known for whole domain */
plen
=
setup_reply
(
header
,
(
unsigned
int
)
plen
,
addrp
,
flags
,
local_ttl
);
send
to
(
udpfd
,
(
char
*
)
header
,
plen
,
0
,
&
udpaddr
->
sa
,
sa_len
(
udpaddr
)
);
send
_from
(
udpfd
,
options
&
OPT_NOWILD
,
(
char
*
)
header
,
plen
,
udpaddr
,
dst_addr
);
if
(
flags
&
(
F_NOERR
|
F_NXDOMAIN
))
log_query
(
F_CONFIG
|
F_FORWARD
|
F_NEG
|
gotname
|
(
flags
&
F_NXDOMAIN
),
dnamebuff
,
NULL
);
...
...
@@ -273,15 +349,161 @@ struct server *reply_query(int fd, int options, char *packet, time_t now,
since that will prod the resolver into moving to TCP - which we
don't support. */
header
->
tc
=
0
;
/* goodbye truncate */
sendto
(
forward
->
fd
,
packet
,
n
,
0
,
&
forward
->
source
.
sa
,
sa_len
(
&
forward
->
source
));
send_from
(
forward
->
fd
,
options
&
OPT_NOWILD
,
packet
,
n
,
&
forward
->
source
,
&
forward
->
dest
);
forward
->
new_id
=
0
;
/* cancel */
}
}
return
last_server
;
}
struct
server
*
receive_query
(
struct
listener
*
listen
,
char
*
packet
,
char
*
mxname
,
char
*
mxtarget
,
unsigned
int
options
,
time_t
now
,
unsigned
long
local_ttl
,
char
*
namebuff
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
struct
server
*
last_server
,
struct
server
*
servers
)
{
HEADER
*
header
=
(
HEADER
*
)
packet
;
union
mysockaddr
source_addr
;
struct
iname
*
tmp
;
struct
all_addr
dst_addr
;
int
m
,
n
,
gotit
=
0
;
struct
iovec
iov
[
1
];
struct
msghdr
msg
;
struct
cmsghdr
*
cmptr
;
char
if_name
[
IF_NAMESIZE
];
union
{
struct
cmsghdr
align
;
/* this ensures alignment */
#ifdef HAVE_IPV6
char
control6
[
CMSG_SPACE
(
sizeof
(
struct
in6_pktinfo
))];
#endif
#if defined(IP_PKTINFO)
char
control
[
CMSG_SPACE
(
sizeof
(
struct
in_pktinfo
))];
#elif defined(IP_RECVDSTADDR)
char
control
[
CMSG_SPACE
(
sizeof
(
struct
in_addr
))
+
CMSG_SPACE
(
sizeof
(
struct
sockaddr_dl
))];
#endif
}
control_u
;
iov
[
0
].
iov_base
=
packet
;
iov
[
0
].
iov_len
=
PACKETSZ
;
msg
.
msg_control
=
control_u
.
control
;
msg
.
msg_controllen
=
sizeof
(
control_u
);
msg
.
msg_flags
=
0
;
msg
.
msg_name
=
&
source_addr
;
msg
.
msg_namelen
=
sizeof
(
source_addr
);
msg
.
msg_iov
=
iov
;
msg
.
msg_iovlen
=
1
;
n
=
recvmsg
(
listen
->
fd
,
&
msg
,
0
);
source_addr
.
sa
.
sa_family
=
listen
->
family
;
#ifdef HAVE_IPV6
if
(
listen
->
family
==
AF_INET6
)
source_addr
.
in6
.
sin6_flowinfo
=
htonl
(
0
);
#endif
if
(
!
(
options
&
OPT_NOWILD
)
&&
msg
.
msg_controllen
<
sizeof
(
struct
cmsghdr
))
return
last_server
;
#if defined(IP_PKTINFO)
if
(
!
(
options
&
OPT_NOWILD
)
&&
listen
->
family
==
AF_INET
)
for
(
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
cmptr
;
cmptr
=
CMSG_NXTHDR
(
&
msg
,
cmptr
))
if
(
cmptr
->
cmsg_level
==
SOL_IP
&&
cmptr
->
cmsg_type
==
IP_PKTINFO
)
{
dst_addr
.
addr
.
addr4
=
((
struct
in_pktinfo
*
)
CMSG_DATA
(
cmptr
))
->
ipi_spec_dst
;
if_indextoname
(((
struct
in_pktinfo
*
)
CMSG_DATA
(
cmptr
))
->
ipi_ifindex
,
if_name
);
gotit
=
1
;
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if
(
!
(
options
&
OPT_NOWILD
)
&&
listen
->
family
==
AF_INET
)
{
for
(
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
cmptr
;
cmptr
=
CMSG_NXTHDR
(
&
msg
,
cmptr
))
if
(
cmptr
->
cmsg_level
==
IPPROTO_IP
&&
cmptr
->
cmsg_type
==
IP_RECVDSTADDR
)
{
dst_addr
.
addr
.
addr4
=
*
((
struct
in_addr
*
)
CMSG_DATA
(
cmptr
));
gotit
=
1
;
}
else
if
(
cmptr
->
cmsg_level
==
IPPROTO_IP
&&
cmptr
->
cmsg_type
==
IP_RECVIF
)
if_indextoname
(((
struct
sockaddr_dl
*
)
CMSG_DATA
(
cmptr
))
->
sdl_index
,
if_name
);
}
#endif
#ifdef HAVE_IPV6
if
(
!
(
options
&
OPT_NOWILD
)
&&
listen
->
family
==
AF_INET6
)
{
for
(
cmptr
=
CMSG_FIRSTHDR
(
&
msg
);
cmptr
;
cmptr
=
CMSG_NXTHDR
(
&
msg
,
cmptr
))
if
(
cmptr
->
cmsg_level
==
IPV6_LEVEL
&&
cmptr
->
cmsg_type
==
IPV6_PKTINFO
)
{
dst_addr
.
addr
.
addr6
=
((
struct
in6_pktinfo
*
)
CMSG_DATA
(
cmptr
))
->
ipi6_addr
;
if_indextoname
(((
struct
in6_pktinfo
*
)
CMSG_DATA
(
cmptr
))
->
ipi6_ifindex
,
if_name
);
gotit
=
1
;
}
}
#endif
if
(
n
<
(
int
)
sizeof
(
HEADER
)
||
header
->
qr
)
return
last_server
;
/* enforce available interface configuration */
if
(
!
(
options
&
OPT_NOWILD
))
{
if
(
!
gotit
)
return
last_server
;
for
(
tmp
=
except
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
(
strcmp
(
tmp
->
name
,
if_name
)
==
0
))
return
last_server
;
if
(
names
||
addrs
)
{
for
(
tmp
=
names
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
(
strcmp
(
tmp
->
name
,
if_name
)
==
0
))
break
;
if
(
!
tmp
)
for
(
tmp
=
addrs
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
addr
.
sa
.
sa_family
==
listen
->
family
)
{
if
(
tmp
->
addr
.
sa
.
sa_family
==
AF_INET
&&
tmp
->
addr
.
in
.
sin_addr
.
s_addr
==
dst_addr
.
addr
.
addr4
.
s_addr
)
break
;
#ifdef HAVE_IPV6
else
if
(
tmp
->
addr
.
sa
.
sa_family
==
AF_INET6
&&
memcmp
(
&
tmp
->
addr
.
in6
.
sin6_addr
,
&
dst_addr
.
addr
.
addr6
,
sizeof
(
struct
in6_addr
))
==
0
)
break
;
#endif
}
if
(
!
tmp
)
return
last_server
;
}
}
if
(
extract_request
(
header
,
(
unsigned
int
)
n
,
namebuff
))
{
if
(
listen
->
family
==
AF_INET
)
log_query
(
F_QUERY
|
F_IPV4
|
F_FORWARD
,
namebuff
,
(
struct
all_addr
*
)
&
source_addr
.
in
.
sin_addr
);
#ifdef HAVE_IPV6
else
log_query
(
F_QUERY
|
F_IPV6
|
F_FORWARD
,
namebuff
,
(
struct
all_addr
*
)
&
source_addr
.
in6
.
sin6_addr
);
#endif
}
m
=
answer_request
(
header
,
((
char
*
)
header
)
+
PACKETSZ
,
(
unsigned
int
)
n
,
mxname
,
mxtarget
,
options
,
now
,
local_ttl
,
namebuff
);
if
(
m
>=
1
)
send_from
(
listen
->
fd
,
options
&
OPT_NOWILD
,
(
char
*
)
header
,
m
,
&
source_addr
,
&
dst_addr
);
else
last_server
=
forward_query
(
listen
->
fd
,
&
source_addr
,
&
dst_addr
,
header
,
n
,
options
,
namebuff
,
servers
,
last_server
,
now
,
local_ttl
);
return
last_server
;
}
static
struct
frec
*
get_new_frec
(
time_t
now
)
{
struct
frec
*
f
=
frec_list
,
*
oldest
=
NULL
;
...
...
src/lease.c
View file @
44a2a316
...
...
@@ -16,10 +16,11 @@
static
struct
dhcp_lease
*
leases
;
FILE
*
lease_file
;
int
dns_dirty
,
file_dirty
;
int
dns_dirty
,
file_dirty
,
new_lease
;
int
leases_left
;
int
lease_init
(
char
*
filename
,
char
*
domain
,
char
*
buff
,
char
*
buff2
,
time_t
now
,
struct
dhcp_config
*
dhcp_config
s
)
char
*
buff2
,
time_t
now
,
int
maxlease
s
)
{
unsigned
int
e0
,
e1
,
e2
,
e3
,
e4
,
e5
,
a0
,
a1
,
a2
,
a3
;
unsigned
long
ei
;
...
...
@@ -27,20 +28,24 @@ int lease_init(char *filename, char *domain, char *buff,
unsigned
char
hwaddr
[
ETHER_ADDR_LEN
];
struct
in_addr
addr
;
struct
dhcp_lease
*
lease
;
struct
dhcp_config
*
config
;
int
clid_len
=
0
;
int
has_old
=
0
;
leases
=
NULL
;
if
(
!
(
lease_file
=
fopen
(
filename
,
"a+"
)))
leases_left
=
maxleases
;
if
(
!
(
lease_file
=
fopen
(
filename
,
"r+"
)))
die
(
"cannot open or create leases file: %s"
,
NULL
);
rewind
(
lease_file
);
/* file opened with mode a+ which sets pointer at end. */
while
(
fscanf
(
lease_file
,
"%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %256s %500s"
,
&
ei
,
&
e0
,
&
e1
,
&
e2
,
&
e3
,
&
e4
,
&
e5
,
&
a0
,
&
a1
,
&
a2
,
&
a3
,
buff
,
buff2
)
==
13
)
{
#ifdef HAVE_BROKEN_RTC
if
(
ei
)
expires
=
(
time_t
)
ei
+
now
;
else
expires
=
(
time_t
)
0
;
#else
/* strictly time_t is opaque, but this hack should work on all sane systems,
even when sizeof(time_t) == 8 */
expires
=
(
time_t
)
ei
;
...
...
@@ -50,6 +55,7 @@ int lease_init(char *filename, char *domain, char *buff,
has_old
=
1
;
continue
;
/* expired */
}
#endif
hwaddr
[
0
]
=
e0
;
hwaddr
[
1
]
=
e1
;
...
...
@@ -74,7 +80,7 @@ int lease_init(char *filename, char *domain, char *buff,
}
if
(
!
(
lease
=
lease_allocate
(
buff2
,
clid_len
,
addr
)))
die
(
"
cannot get memory
"
,
NULL
);
die
(
"
too many stored leases
"
,
NULL
);
lease
->
expires
=
expires
;
memcpy
(
lease
->
hwaddr
,
hwaddr
,
ETHER_ADDR_LEN
);
...
...
@@ -85,32 +91,54 @@ int lease_init(char *filename, char *domain, char *buff,
dns_dirty
=
1
;
file_dirty
=
has_old
;
new_lease
=
0
;
return
fileno
(
lease_file
);
}
void
lease_update_from_configs
(
struct
dhcp_config
*
dhcp_configs
,
char
*
domain
)
{
/* changes to the config may change current leases. */
struct
dhcp_lease
*
lease
;
struct
dhcp_config
*
config
;
/* Deal with edits to the config file which may have changed the hostname
associated with a hardware address. Do this after the main loop so that
changes get written back out */
for
(
lease
=
leases
;
lease
;
lease
=
lease
->
next
)
if
((
config
=
find_config
(
dhcp_configs
,
NULL
,
lease
->
clid
,
lease
->
clid_len
,
lease
->
hwaddr
,
NULL
))
&&
(
config
->
hostname
))
lease_set_hostname
(
lease
,
config
->
hostname
,
domain
);
return
fileno
(
lease_file
);
}
void
lease_update_
dns
(
int
force_dns
)
void
lease_update_
file
(
int
force
,
time_t
now
)
{
struct
dhcp_lease
*
lease
;
int
i
;
int
i
=
force
;
/* avoid warning */
unsigned
long
expires
;
#ifdef HAVE_BROKEN_RTC
if
(
force
||
new_lease
)
{
lease_prune
(
NULL
,
now
);
#else
if
(
file_dirty
)
{
#endif
rewind
(
lease_file
);
ftruncate
(
fileno
(
lease_file
),
0
);
for
(
lease
=
leases
;
lease
;
lease
=
lease
->
next
)
{
#ifdef HAVE_BROKEN_RTC
if
(
lease
->
expires
)
expires
=
(
unsigned
long
)
difftime
(
lease
->
expires
,
now
);
else
expires
=
0
;
#else
expires
=
now
;
/* eliminate warning */
expires
=
(
unsigned
long
)
lease
->
expires
;
#endif
fprintf
(
lease_file
,
"%lu %.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %s "
,
(
unsigned
long
)
lease
->
expires
,
lease
->
hwaddr
[
0
],
lease
->
hwaddr
[
1
],
expires
,
lease
->
hwaddr
[
0
],
lease
->
hwaddr
[
1
],
lease
->
hwaddr
[
2
],
lease
->
hwaddr
[
3
],
lease
->
hwaddr
[
4
],
lease
->
hwaddr
[
5
],
inet_ntoa
(
lease
->
addr
),
lease
->
hostname
?
lease
->
hostname
:
"*"
);
...
...
@@ -129,13 +157,18 @@ void lease_update_dns(int force_dns)
fflush
(
lease_file
);
fsync
(
fileno
(
lease_file
));
file_dirty
=
0
;
new_lease
=
0
;
}
}
if
(
dns_dirty
||
force_dns
)
void
lease_update_dns
(
void
)
{
struct
dhcp_lease
*
lease
;
if
(
dns_dirty
)
{
cache_unhash_dhcp
();
for
(
lease
=
leases
;
lease
;
lease
=
lease
->
next
)
{
if
(
lease
->
fqdn
)
...
...
@@ -173,6 +206,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if
(
lease
->
clid
)
free
(
lease
->
clid
);
free
(
lease
);
leases_left
++
;
}
else
up
=
&
lease
->
next
;
...
...
@@ -220,7 +254,7 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct
dhcp_lease
*
lease_allocate
(
unsigned
char
*
clid
,
int
clid_len
,
struct
in_addr
addr
)
{
struct
dhcp_lease
*
lease
;
if
(
!
(
lease
=
malloc
(
sizeof
(
struct
dhcp_lease
))))
if
(
!
leases_left
||
!
(
lease
=
malloc
(
sizeof
(
struct
dhcp_lease
))))
return
NULL
;
lease
->
clid
=
NULL
;
...
...
@@ -245,6 +279,8 @@ struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_a
leases
=
lease
;
file_dirty
=
1
;
new_lease
=
1
;
leases_left
--
;
return
lease
;
}
...
...
@@ -294,14 +330,12 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
lease_tmp
->
fqdn
=
NULL
;
}
}
if
(
!
new_name
)
{
new_name
=
malloc
(
strlen
(
name
)
+
1
);
strcpy
(
new_name
,
name
);
}
if
(
suffix
&&
!
new_fqdn
)
if
(
!
new_name
&&
(
new_name
=
malloc
(
strlen
(
name
)
+
1
)))
strcpy
(
new_name
,
name
);
if
(
suffix
&&
!
new_fqdn
&&
(
new_fqdn
=
malloc
(
strlen
(
name
)
+
strlen
(
suffix
)
+
2
)))
{
new_fqdn
=
malloc
(
strlen
(
name
)
+
strlen
(
suffix
)
+
2
);
strcpy
(
new_fqdn
,
name
);
strcat
(
new_fqdn
,
"."
);
strcat
(
new_fqdn
,
suffix
);
...
...
src/network.c
View file @
44a2a316
...
...
@@ -14,152 +14,89 @@
#include "dnsmasq.h"
static
char
*
add_iface
(
struct
irec
**
list
,
unsigned
int
flags
,
char
*
name
,
union
mysockaddr
*
addr
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
)
static
struct
irec
*
add_iface
(
struct
irec
*
list
,
char
*
name
,
union
mysockaddr
*
addr
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
)
{
struct
irec
*
iface
;
int
fd
,
opt
;
struct
iname
*
tmp
;
/* check blacklist */
if
(
except
)
for
(
tmp
=
except
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
strcmp
(
tmp
->
name
,
name
)
==
0
)
return
NULL
;
/* we may need to check the whitelist */
if
(
names
)
if
(
names
||
addrs
)
{
for
(
tmp
=
names
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
(
strcmp
(
tmp
->
name
,
name
)
==
0
))
{
tmp
->
found
=
1
;
break
;
}
if
(
!
(
flags
&
IFF_LOOPBACK
)
&&
!
tmp
)
/* not on whitelist and not loopback */
break
;
if
(
!
tmp
&&
!
addrs
)
return
NULL
;
}
if
(
addrs
)
{
for
(
tmp
=
addrs
;
tmp
;
tmp
=
tmp
->
next
)
if
(
sockaddr_isequal
(
&
tmp
->
addr
,
addr
))
{
tmp
->
found
=
1
;
break
;
}
break
;
if
(
!
tmp
)
/* not on whitelist */
return
NULL
;
}
/* check blacklist */
if
(
except
)
for
(
tmp
=
except
;
tmp
;
tmp
=
tmp
->
next
)
if
(
tmp
->
name
&&
strcmp
(
tmp
->
name
,
name
)
==
0
)
return
NULL
;
/* check whether the interface IP has been added already
it is possible to have multiple interfaces with the same address
and we may be re-scanning. */
for
(
iface
=
*
list
;
iface
;
iface
=
iface
->
next
)
if
(
sockaddr_isequal
(
&
iface
->
addr
,
addr
))
it is possible to have multiple interfaces with the same address */
for
(;
list
;
list
=
list
->
next
)
if
(
sockaddr_isequal
(
&
list
->
addr
,
addr
))
break
;
if
(
iface
)
{
iface
->
valid
=
1
;
return
NULL
;
}
if
((
fd
=
socket
(
addr
->
sa
.
sa_family
,
SOCK_DGRAM
,
0
))
==
-
1
)
return
"failed to create socket: %s"
;
/* Set SO_REUSEADDR on the socket, this allows is to bind
specific addresses even if BIND is running and has bound *:53 */
opt
=
1
;
if
(
setsockopt
(
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
bind
(
fd
,
&
addr
->
sa
,
sa_len
(
addr
))
==
-
1
)
{
int
errsave
=
errno
;
close
(
fd
);
errno
=
errsave
;
/* IPv6 interfaces sometimes return ENODEV to bind() for unknown
(to me) reasons. Don't treat that as fatal. */
return
errno
==
ENODEV
?
NULL
:
"failed to bind socket: %s"
;
}
if
(
list
)
return
NULL
;
/* If OK, add it to the head of the list */
if
(
!
(
iface
=
malloc
(
sizeof
(
struct
irec
))))
{
close
(
fd
);
return
"cannot allocate interface"
;
}
iface
->
fd
=
fd
;
iface
=
safe_malloc
(
sizeof
(
struct
irec
));
iface
->
addr
=
*
addr
;
iface
->
next
=
*
list
;
iface
->
valid
=
1
;
*
list
=
iface
;
return
NULL
;
return
iface
;
}
/* get all interfaces in system and for each one allowed add it to the chain
at interfacep. May be called more that once: interfaces which still exist
are left on the chain, those which have gone have sockets close()ed an are
unlinked. Return value is NULL if OK, an error string and the value of errno
on error. */
char
*
enumerate_interfaces
(
struct
irec
**
interfacep
,
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
struct
dhcp_context
*
dhcp
,
int
port
)
struct
irec
*
enumerate_interfaces
(
struct
iname
*
names
,
struct
iname
*
addrs
,
struct
iname
*
except
,
int
port
)
{
/* this code is adapted from Stevens, page 434. It finally
destroyed my faith in the C/unix API */
int
len
=
100
*
sizeof
(
struct
ifreq
);
int
errsave
,
lastlen
=
0
;
struct
irec
*
iface
,
*
prev
;
char
*
buf
,
*
ptr
,
*
err
=
NULL
;
struct
ifconf
ifc
;
struct
irec
*
iface
=
NULL
,
*
new
;
char
*
buf
,
*
ptr
;
struct
ifreq
*
ifr
=
NULL
;
struct
ifconf
ifc
;
int
lastlen
=
0
;
int
len
=
20
*
sizeof
(
struct
ifreq
);
int
fd
=
socket
(
PF_INET
,
SOCK_DGRAM
,
0
);
int
rawfd
=
-
1
;
if
(
fd
==
-
1
)
return
"cannot create socket to enumerate interfaces: %s"
;
/* make all interfaces as old. Any left that way after the scan are reaped. */
for
(
iface
=
*
interfacep
;
iface
;
iface
=
iface
->
next
)
iface
->
valid
=
0
;
die
(
"cannot create socket to enumerate interfaces: %s"
,
NULL
);
while
(
1
)
{
if
(
!
(
buf
=
malloc
(
len
)))
{
err
=
"cannot allocate buffer"
;
goto
end
;
}
ifc
.
ifc_len
=
len
;
ifc
.
ifc_buf
=
buf
;
if
(
ioctl
(
fd
,
SIOCGIFCONF
,
&
ifc
)
<
0
)
{
if
(
errno
!=
EINVAL
||
lastlen
!=
0
)
{
err
=
"ioctl error while enumerating interfaces: %s"
;
goto
end
;
}
}
else
{
if
(
ifc
.
ifc_len
==
lastlen
)
break
;
/* got a big enough buffer now */
lastlen
=
ifc
.
ifc_len
;
}
len
+=
10
*
sizeof
(
struct
ifreq
);
free
(
buf
);
}
for
(
ptr
=
buf
;
ptr
<
buf
+
ifc
.
ifc_len
;
)
{
buf
=
safe_malloc
(
len
);
ifc
.
ifc_len
=
len
;
ifc
.
ifc_buf
=
buf
;
if
(
ioctl
(
fd
,
SIOCGIFCONF
,
&
ifc
)
<
0
)
{
if
(
errno
!=
EINVAL
||
lastlen
!=
0
)
die
(
"ioctl error while enumerating interfaces: %s"
,
NULL
);
}
else
{
if
(
ifc
.
ifc_len
==
lastlen
)
break
;
/* got a big enough buffer now */
lastlen
=
ifc
.
ifc_len
;
}
len
+=
10
*
sizeof
(
struct
ifreq
);
free
(
buf
);
}
for
(
ptr
=
buf
;
ptr
<
buf
+
len
;
)
{
union
mysockaddr
addr
;
#ifdef HAVE_SOCKADDR_SA_LEN
...
...
@@ -168,10 +105,7 @@ char *enumerate_interfaces(struct irec **interfacep,
unaligned accesses. */
int
ifr_len
=
((
struct
ifreq
*
)
ptr
)
->
ifr_addr
.
sa_len
+
IF_NAMESIZE
;
if
(
!
(
ifr
=
realloc
(
ifr
,
ifr_len
)))
{
err
=
"cannot allocate buffer"
;
goto
end
;
}
die
(
"cannot allocate buffer"
,
NULL
);
memcpy
(
ifr
,
ptr
,
ifr_len
);
ptr
+=
ifr_len
;
...
...
@@ -179,7 +113,7 @@ char *enumerate_interfaces(struct irec **interfacep,
ifr
=
(
struct
ifreq
*
)
ptr
;
ptr
+=
sizeof
(
struct
ifreq
);
#endif
/* copy address since getting flags overwrites */
if
(
ifr
->
ifr_addr
.
sa_family
==
AF_INET
)
{
...
...
@@ -202,14 +136,24 @@ char *enumerate_interfaces(struct irec **interfacep,
continue
;
/* unknown address family */
if
(
ioctl
(
fd
,
SIOCGIFFLAGS
,
ifr
)
<
0
)
die
(
"ioctl error getting interface flags: %m"
,
NULL
);
/* If we are restricting the set of interfaces to use, make
sure that loopback interfaces are in that set. */
if
(
names
&&
(
ifr
->
ifr_flags
&
IFF_LOOPBACK
))
{
err
=
"ioctl error getting interface flags: %m"
;
goto
end
;
struct
iname
*
lo
=
safe_malloc
(
sizeof
(
struct
iname
));
lo
->
name
=
safe_string_alloc
(
ifr
->
ifr_name
);
lo
->
next
=
names
->
next
;
names
->
next
=
lo
;
}
if
((
err
=
add_iface
(
interfacep
,
ifr
->
ifr_flags
,
ifr
->
ifr_name
,
if
((
new
=
add_iface
(
iface
,
ifr
->
ifr_name
,
&
addr
,
names
,
addrs
,
except
)))
goto
end
;
{
new
->
next
=
iface
;
iface
=
new
;
}
#if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
/* IPv6 addresses don't seem to work with SIOCGIFCONF. Barf */
...
...
@@ -252,141 +196,16 @@ char *enumerate_interfaces(struct irec **interfacep,
fclose
(
f
);
}
if
(
found
&&
(
err
=
add_iface
(
interfacep
,
ifr
->
ifr_flags
,
ifr
->
ifr_name
,
&
addr6
,
names
,
addrs
,
except
)))
goto
end
;
if
(
found
&&
(
new
=
add_iface
(
iface
,
ifr
->
ifr_name
,
&
addr6
,
names
,
addrs
,
except
)))
{
new
->
next
=
iface
;
iface
=
new
;
}
}
#endif
/* LINUX */
/* dhcp is non-null only on the first call: set up the relevant
interface-related DHCP stuff here. DHCP is IPv4 only.
Because errors here are ultimately fatal we can return directly and not bother
closing the descriptor.
*/
if
(
dhcp
&&
addr
.
sa
.
sa_family
==
AF_INET
&&
!
(
ifr
->
ifr_flags
&
(
IFF_LOOPBACK
|
IFF_POINTOPOINT
)))
{
struct
in_addr
netmask
,
broadcast
;
struct
dhcp_context
*
context
;
int
opt
=
1
;
if
(
ioctl
(
fd
,
SIOCGIFNETMASK
,
ifr
)
<
0
)
return
"ioctl error getting interface netmask: %s"
;
netmask
=
((
struct
sockaddr_in
*
)
&
ifr
->
ifr_addr
)
->
sin_addr
;
if
(
ioctl
(
fd
,
SIOCGIFBRDADDR
,
ifr
)
<
0
)
return
"ioctl error getting interface broadcast address: %s"
;
broadcast
=
((
struct
sockaddr_in
*
)
&
ifr
->
ifr_addr
)
->
sin_addr
;
for
(
context
=
dhcp
;
context
;
context
=
context
->
next
)
if
(
!
context
->
iface
&&
/* may be more than one iface with same addr */
((
addr
.
in
.
sin_addr
.
s_addr
&
netmask
.
s_addr
)
==
(
context
->
start
.
s_addr
&
netmask
.
s_addr
))
&&
((
addr
.
in
.
sin_addr
.
s_addr
&
netmask
.
s_addr
)
==
(
context
->
end
.
s_addr
&
netmask
.
s_addr
)))
{
struct
sockaddr_in
saddr
;
#ifdef HAVE_BPF
char
filename
[
50
];
int
b
=
0
;
while
(
1
)
{
sprintf
(
filename
,
"/dev/bpf%d"
,
b
);
if
((
rawfd
=
open
(
filename
,
O_RDWR
,
0
))
==
-
1
)
{
if
(
errno
!=
EBUSY
)
return
"Cannot create DHCP BPF socket: %s"
;
b
++
;
}
else
if
(
ioctl
(
rawfd
,
BIOCSETIF
,
ifr
)
<
0
)
return
"Can't attach interface to BPF device: %s"
;
else
break
;
}
if
(
context
->
next
)
return
"no support for DHCP on multiple networks under this OS"
;
#endif
#ifdef HAVE_PF_PACKET
if
(
rawfd
==
-
1
&&
/* same packet socket for all interfaces */
(
rawfd
=
socket
(
PF_PACKET
,
SOCK_DGRAM
,
htons
(
ETHERTYPE_IP
)))
==
-
1
)
return
"Cannot create DHCP packet socket: %s"
;
/* do this last so that the index is still in ifr for the
call to setsockopt(SO_BINDTODEVICE) */
if
(
ioctl
(
fd
,
SIOCGIFINDEX
,
ifr
)
<
0
)
return
"ioctl error getting interface index: %m"
;
context
->
ifindex
=
ifr
->
ifr_ifindex
;
#endif
context
->
rawfd
=
rawfd
;
context
->
serv_addr
=
addr
.
in
.
sin_addr
;
context
->
netmask
=
netmask
;
context
->
broadcast
=
broadcast
;
if
(
!
(
context
->
iface
=
malloc
(
strlen
(
ifr
->
ifr_name
)
+
1
)))
return
"cannot allocate interface name"
;
strcpy
(
context
->
iface
,
ifr
->
ifr_name
);
saddr
.
sin_family
=
AF_INET
;
saddr
.
sin_port
=
htons
(
DHCP_SERVER_PORT
);
saddr
.
sin_addr
.
s_addr
=
INADDR_ANY
;
if
((
context
->
fd
=
socket
(
PF_INET
,
SOCK_DGRAM
,
IPPROTO_UDP
))
==
-
1
)
return
"cannot create DHCP server socket: %s"
;
if
(
setsockopt
(
context
->
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#ifdef HAVE_PF_PACKET
setsockopt
(
context
->
fd
,
SOL_SOCKET
,
SO_BINDTODEVICE
,
ifr
,
sizeof
(
*
ifr
))
==
-
1
||
#endif
setsockopt
(
context
->
fd
,
SOL_SOCKET
,
SO_BROADCAST
,
&
opt
,
sizeof
(
opt
))
==
-
1
)
return
"failed to set options on DHCP socket: %s"
;
if
(
bind
(
context
->
fd
,
(
struct
sockaddr
*
)
&
saddr
,
sizeof
(
struct
sockaddr_in
)))
return
"failed to bind DHCP server socket: %s"
;
}
}
}
#ifdef HAVE_BPF
/* now go through the interfaces again, looking for AF_LINK records
to get hardware addresses from */
for
(
ptr
=
buf
;
ptr
<
buf
+
ifc
.
ifc_len
;
)
{
struct
dhcp_context
*
context
;
#ifdef HAVE_SOCKADDR_SA_LEN
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
int
ifr_len
=
((
struct
ifreq
*
)
ptr
)
->
ifr_addr
.
sa_len
+
IF_NAMESIZE
;
if
(
!
(
ifr
=
realloc
(
ifr
,
ifr_len
)))
{
err
=
"cannot allocate buffer"
;
goto
end
;
}
memcpy
(
ifr
,
ptr
,
ifr_len
);
ptr
+=
ifr_len
;
#else
ifr
=
(
struct
ifreq
*
)
ptr
;
ptr
+=
sizeof
(
struct
ifreq
);
#endif
if
(
ifr
->
ifr_addr
.
sa_family
==
AF_LINK
)
for
(
context
=
dhcp
;
context
;
context
=
context
->
next
)
if
(
context
->
iface
&&
strcmp
(
context
->
iface
,
ifr
->
ifr_name
)
==
0
)
memcpy
(
context
->
hwaddr
,
LLADDR
((
struct
sockaddr_dl
*
)
&
ifr
->
ifr_addr
),
ETHER_ADDR_LEN
);
}
#endif
end:
errsave
=
errno
;
/* since errno gets overwritten by close */
if
(
buf
)
free
(
buf
);
#ifdef HAVE_SOCKADDR_SA_LEN
...
...
@@ -394,37 +213,91 @@ char *enumerate_interfaces(struct irec **interfacep,
free
(
ifr
);
#endif
close
(
fd
);
if
(
err
)
{
errno
=
errsave
;
return
err
;
return
iface
;
}
struct
listener
*
create_wildcard_listeners
(
int
port
)
{
union
mysockaddr
addr
;
int
opt
=
1
;
struct
listener
*
listen
;
#ifdef HAVE_IPV6
int
fd
;
#endif
addr
.
in
.
sin_family
=
AF_INET
;
addr
.
in
.
sin_addr
.
s_addr
=
INADDR_ANY
;
addr
.
in
.
sin_port
=
htons
(
port
);
#ifdef HAVE_SOCKADDR_SA_LEN
addr
.
in
.
sin_len
=
sizeof
(
struct
sockaddr_in
);
#endif
listen
=
safe_malloc
(
sizeof
(
struct
listener
));
if
((
listen
->
fd
=
socket
(
AF_INET
,
SOCK_DGRAM
,
0
))
==
-
1
)
die
(
"failed to create socket: %s"
,
NULL
);
if
(
setsockopt
(
listen
->
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#if defined(IP_PKTINFO)
setsockopt
(
listen
->
fd
,
SOL_IP
,
IP_PKTINFO
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt
(
listen
->
fd
,
IPPROTO_IP
,
IP_RECVDSTADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
setsockopt
(
listen
->
fd
,
IPPROTO_IP
,
IP_RECVIF
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
#endif
bind
(
listen
->
fd
,
(
struct
sockaddr
*
)
&
addr
,
sa_len
(
&
addr
))
==
-
1
)
die
(
"failed to bind socket: %s"
,
NULL
);
listen
->
next
=
NULL
;
listen
->
family
=
AF_INET
;
#ifdef HAVE_IPV6
addr
.
in6
.
sin6_family
=
AF_INET6
;
addr
.
in6
.
sin6_addr
=
in6addr_any
;
addr
.
in6
.
sin6_port
=
htons
(
port
);
addr
.
in6
.
sin6_flowinfo
=
htonl
(
0
);
#ifdef HAVE_SOCKADDR_SA_LEN
addr
.
in6
.
sin6_len
=
sizeof
(
struct
sockaddr_in6
);
#endif
if
((
fd
=
socket
(
AF_INET6
,
SOCK_DGRAM
,
0
))
==
-
1
)
{
if
(
errno
!=
EPROTONOSUPPORT
&&
errno
!=
EAFNOSUPPORT
&&
errno
!=
EINVAL
)
die
(
"failed to create IPv6 socket: %s"
,
NULL
);
}
else
{
listen
->
next
=
safe_malloc
(
sizeof
(
struct
listener
));
listen
->
next
->
fd
=
fd
;
listen
->
next
->
family
=
AF_INET6
;
listen
->
next
->
next
=
NULL
;
if
(
setsockopt
(
listen
->
next
->
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
setsockopt
(
listen
->
next
->
fd
,
IPV6_LEVEL
,
IPV6_PKTINFO
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
bind
(
listen
->
next
->
fd
,
(
struct
sockaddr
*
)
&
addr
,
sa_len
(
&
addr
))
==
-
1
)
die
(
"failed to bind IPv6 socket: %s"
,
NULL
);
}
#endif
return
listen
;
}
struct
listener
*
create_bound_listeners
(
struct
irec
*
interfaces
)
{
struct
listener
*
listeners
=
NULL
;
struct
irec
*
iface
;
int
opt
=
1
;
/* now remove interfaces which were not found on this scan */
for
(
prev
=
NULL
,
iface
=
*
interfacep
;
iface
;
)
for
(
iface
=
interfaces
;
iface
;
iface
=
iface
->
next
)
{
if
(
iface
->
valid
)
{
prev
=
iface
;
iface
=
iface
->
next
;
}
else
{
struct
irec
*
tmp
=
iface
;
close
(
iface
->
fd
);
/* remove pending queries from this interface */
reap_forward
(
iface
->
fd
);
/* unlink */
if
(
prev
)
prev
->
next
=
iface
->
next
;
else
*
interfacep
=
iface
->
next
;
iface
=
iface
->
next
;
free
(
tmp
);
}
struct
listener
*
new
=
safe_malloc
(
sizeof
(
struct
listener
));
new
->
family
=
iface
->
addr
.
sa
.
sa_family
;
new
->
next
=
listeners
;
listeners
=
new
;
if
((
new
->
fd
=
socket
(
iface
->
addr
.
sa
.
sa_family
,
SOCK_DGRAM
,
0
))
==
-
1
)
die
(
"failed to create socket: %s"
,
NULL
);
if
(
setsockopt
(
new
->
fd
,
SOL_SOCKET
,
SO_REUSEADDR
,
&
opt
,
sizeof
(
opt
))
==
-
1
||
bind
(
new
->
fd
,
&
iface
->
addr
.
sa
,
sa_len
(
&
iface
->
addr
))
==
-
1
)
die
(
"failed to bind socket: %s"
,
NULL
);
}
return
NULL
;
/* no error */
return
listeners
;
}
static
struct
serverfd
*
allocate_sfd
(
union
mysockaddr
*
addr
,
struct
serverfd
**
sfds
)
...
...
@@ -607,7 +480,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
source_addr
.
in6
.
sin6_family
=
addr
.
in6
.
sin6_family
=
AF_INET6
;
addr
.
in6
.
sin6_port
=
htons
(
NAMESERVER_PORT
);
source_addr
.
in6
.
sin6_flowinfo
=
addr
.
in6
.
sin6_flowinfo
=
htonl
(
0
);
source_addr
.
in6
.
sin6_addr
=
in6addr_any
;
source_addr
.
in6
.
sin6_addr
=
in6addr_any
;
source_addr
.
in6
.
sin6_port
=
htons
(
query_port
);
}
#endif
/* IPV6 */
...
...
src/option.c
View file @
44a2a316
...
...
@@ -21,7 +21,7 @@ struct myoption {
int
val
;
};
#define OPTSTRING "
DNLERowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M
:"
#define OPTSTRING "
ZDNLERzowefnbvhdqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X
:"
static
struct
myoption
opts
[]
=
{
{
"version"
,
0
,
0
,
'v'
},
...
...
@@ -66,6 +66,8 @@ static struct myoption opts[] = {
{
"query-port"
,
1
,
0
,
'Q'
},
{
"except-interface"
,
1
,
0
,
'I'
},
{
"domain-needed"
,
0
,
0
,
'D'
},
{
"dhcp-lease-max"
,
1
,
0
,
'X'
},
{
"read-ethers"
,
0
,
0
,
'Z'
},
{
0
,
0
,
0
,
0
}
};
...
...
@@ -85,9 +87,11 @@ static struct optflags optmap[] = {
{
'o'
,
OPT_ORDER
},
{
'R'
,
OPT_NO_RESOLV
},
{
'E'
,
OPT_EXPAND
},
{
'L'
,
OPT_LOCALMX
},
{
'N'
,
OPT_NO_NEG
},
{
'D'
,
OPT_NODOTS_LOCAL
},
{
'L'
,
OPT_LOCALMX
},
{
'N'
,
OPT_NO_NEG
},
{
'D'
,
OPT_NODOTS_LOCAL
},
{
'z'
,
OPT_NOWILD
},
{
'Z'
,
OPT_ETHERS
},
{
'v'
,
0
},
{
'w'
,
0
},
{
0
,
0
}
...
...
@@ -136,6 +140,9 @@ static char *usage =
"-v, --version Display dnsmasq version.
\n
"
"-w, --help Display this message.
\n
"
"-x, --pid-file=path Specify path of PID file. (defaults to "
RUNFILE
").
\n
"
"-X, --dhcp-lease-max=number Specify maximum number of DHCP leases (defaults to %d).
\n
"
"-z, --bind-interfaces Bind only to interfaces in use.
\n
"
"-Z, --read-ethers Read DHCP static host information from "
ETHERSFILE
".
\n
"
"
\n
"
;
...
...
@@ -146,15 +153,19 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
struct
bogus_addr
**
bogus_addr
,
struct
server
**
serv_addrs
,
int
*
cachesize
,
int
*
port
,
int
*
query_port
,
unsigned
long
*
local_ttl
,
char
**
addn_hosts
,
struct
dhcp_context
**
dhcp
,
struct
dhcp_config
**
dhcp_conf
,
struct
dhcp_opt
**
dhcp_opts
,
char
**
dhcp_file
,
char
**
dhcp_sname
,
struct
in_addr
*
dhcp_next_server
)
char
**
dhcp_sname
,
struct
in_addr
*
dhcp_next_server
,
int
*
dhcp_max
,
unsigned
int
*
min_leasetime
)
{
int
option
=
0
,
i
;
unsigned
int
flags
=
0
;
FILE
*
f
=
NULL
;
char
*
conffile
=
CONFFILE
;
int
conffile_set
=
0
;
int
lineno
=
0
;
opterr
=
0
;
*
min_leasetime
=
UINT_MAX
;
while
(
1
)
{
...
...
@@ -175,6 +186,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
else
{
char
*
p
;
lineno
++
;
/* dump comments */
for
(
p
=
buff
;
*
p
;
p
++
)
if
(
*
p
==
'#'
)
...
...
@@ -200,7 +212,11 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if
(
strcmp
(
opts
[
i
].
name
,
buff
)
==
0
)
option
=
opts
[
i
].
val
;
if
(
!
option
)
die
(
"bad option %s"
,
buff
);
{
sprintf
(
buff
,
"bad option at line %d of %s "
,
lineno
,
conffile
);
complain
(
buff
,
NULL
);
continue
;
}
}
}
...
...
@@ -220,7 +236,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if
(
!
f
&&
option
==
'w'
)
{
fprintf
(
stderr
,
usage
,
CACHESIZ
);
fprintf
(
stderr
,
usage
,
CACHESIZ
,
MAXLEASES
);
exit
(
0
);
}
...
...
@@ -236,15 +252,22 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
flags
|=
optmap
[
i
].
flag
;
option
=
0
;
if
(
f
&&
optarg
)
die
(
"extraneous parameter for %s in config file."
,
buff
);
{
sprintf
(
buff
,
"extraneous parameter at line %d of %s "
,
lineno
,
conffile
);
complain
(
buff
,
NULL
);
}
break
;
}
if
(
option
&&
option
!=
'?'
)
{
if
(
f
&&
!
optarg
)
die
(
"missing parameter for %s in config file."
,
buff
);
{
sprintf
(
buff
,
"missing parameter at line %d of %s "
,
lineno
,
conffile
);
complain
(
buff
,
NULL
);
continue
;
}
switch
(
option
)
{
case
'C'
:
...
...
@@ -332,7 +355,6 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* new->name may be NULL if someone does
"interface=" to disable all interfaces except loop. */
new
->
name
=
safe_string_alloc
(
optarg
);
new
->
found
=
0
;
break
;
}
...
...
@@ -364,8 +386,6 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
struct
iname
*
new
=
safe_malloc
(
sizeof
(
struct
iname
));
new
->
next
=
*
if_addrs
;
*
if_addrs
=
new
;
new
->
found
=
0
;
#ifdef HAVE_IPV6
if
(
inet_pton
(
AF_INET
,
optarg
,
&
new
->
addr
.
in
.
sin_addr
))
{
...
...
@@ -392,7 +412,14 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
#endif
else
option
=
'?'
;
/* error */
{
option
=
'?'
;
/* error */
free
(
new
);
new
=
NULL
;
}
if
(
new
)
*
if_addrs
=
new
;
break
;
}
...
...
@@ -410,10 +437,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
char
*
domain
;
*
end
=
0
;
if
(
!
canonicalise
(
optarg
))
{
option
=
'?'
;
break
;
}
option
=
'?'
;
domain
=
safe_string_alloc
(
optarg
);
/* NULL if strlen is zero */
serv
=
safe_malloc
(
sizeof
(
struct
server
));
serv
->
next
=
newlist
;
...
...
@@ -443,20 +467,14 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
{
newlist
->
flags
|=
SERV_LITERAL_ADDRESS
;
if
(
!
(
newlist
->
flags
&
SERV_TYPE
))
{
option
=
'?'
;
break
;
}
option
=
'?'
;
}
if
(
!*
optarg
)
{
newlist
->
flags
|=
SERV_NO_ADDR
;
/* no server */
if
(
newlist
->
flags
&
SERV_LITERAL_ADDRESS
)
{
option
=
'?'
;
break
;
}
option
=
'?'
;
}
else
{
...
...
@@ -531,16 +549,26 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
serv
=
newlist
;
while
(
serv
->
next
)
if
(
option
==
'?'
)
while
(
newlist
)
{
serv
=
newlist
;
newlist
=
newlist
->
next
;
free
(
serv
);
}
else
{
serv
->
next
->
flags
=
serv
->
flags
;
serv
->
next
->
addr
=
serv
->
addr
;
serv
->
next
->
source_addr
=
serv
->
source_addr
;
serv
=
serv
->
next
;
serv
=
newlist
;
while
(
serv
->
next
)
{
serv
->
next
->
flags
=
serv
->
flags
;
serv
->
next
->
addr
=
serv
->
addr
;
serv
->
next
->
source_addr
=
serv
->
source_addr
;
serv
=
serv
->
next
;
}
serv
->
next
=
*
serv_addrs
;
*
serv_addrs
=
newlist
;
}
serv
->
next
=
*
serv_addrs
;
*
serv_addrs
=
newlist
;
break
;
}
...
...
@@ -570,34 +598,73 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*
local_ttl
=
(
unsigned
long
)
atoi
(
optarg
);
break
;
case
'X'
:
*
dhcp_max
=
atoi
(
optarg
);
break
;
case
'F'
:
{
char
*
comma
;
int
k
,
leasepos
=
2
;
char
*
cp
,
*
comma
,
*
a
[
5
]
=
{
NULL
,
NULL
,
NULL
,
NULL
,
NULL
};
struct
dhcp_context
*
new
=
safe_malloc
(
sizeof
(
struct
dhcp_context
));
new
->
next
=
*
dhcp
;
*
dhcp
=
new
;
new
->
lease_time
=
DEFLEASE
;
new
->
netmask
.
s_addr
=
0
;
new
->
broadcast
.
s_addr
=
0
;
new
->
netid
=
NULL
;
if
(
!
(
comma
=
strchr
(
optarg
,
','
))
||
(
*
comma
=
0
)
||
((
new
->
start
.
s_addr
=
inet_addr
(
optarg
))
==
(
in_addr_t
)
-
1
))
{
option
=
'?'
;
for
(
cp
=
optarg
;
*
cp
;
cp
++
)
if
(
!
(
*
cp
==
' '
||
*
cp
==
'.'
||
(
*
cp
>=
'0'
&&
*
cp
<=
'9'
)))
break
;
if
(
*
cp
!=
','
&&
(
comma
=
strchr
(
optarg
,
','
)))
{
*
comma
=
0
;
new
->
netid
=
safe_string_alloc
(
optarg
);
a
[
0
]
=
comma
+
1
;
}
else
a
[
0
]
=
optarg
;
optarg
=
comma
+
1
;
if
((
comma
=
strchr
(
optarg
,
','
)))
for
(
k
=
1
;
k
<
5
;
k
++
)
{
if
(
!
(
a
[
k
]
=
strchr
(
a
[
k
-
1
],
','
)))
break
;
*
(
a
[
k
]
++
)
=
0
;
}
if
((
k
<
2
)
||
((
new
->
start
.
s_addr
=
inet_addr
(
a
[
0
]))
==
(
in_addr_t
)
-
1
)
||
((
new
->
end
.
s_addr
=
inet_addr
(
a
[
1
]))
==
(
in_addr_t
)
-
1
))
{
option
=
'?'
;
free
(
new
);
break
;
}
else
*
dhcp
=
new
;
if
(
k
>=
3
&&
strchr
(
a
[
2
],
'.'
)
&&
((
new
->
netmask
.
s_addr
=
inet_addr
(
a
[
2
]))
!=
(
in_addr_t
)
-
1
))
leasepos
=
3
;
if
(
k
>=
4
&&
strchr
(
a
[
3
],
'.'
)
&&
((
new
->
broadcast
.
s_addr
=
inet_addr
(
a
[
3
]))
!=
(
in_addr_t
)
-
1
))
leasepos
=
4
;
if
(
k
>=
leasepos
+
1
)
{
*
(
comma
++
)
=
0
;
if
(
strcmp
(
comma
,
"infinite"
)
==
0
)
if
(
strcmp
(
a
[
leasepos
],
"infinite"
)
==
0
)
new
->
lease_time
=
0xffffffff
;
else
{
int
fac
=
1
;
if
(
strlen
(
comma
)
>
0
)
if
(
strlen
(
a
[
leasepos
]
)
>
0
)
{
switch
(
comma
[
strlen
(
comma
)
-
1
])
switch
(
a
[
leasepos
][
strlen
(
a
[
leasepos
]
)
-
1
])
{
case
'h'
:
case
'H'
:
...
...
@@ -606,24 +673,21 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case
'm'
:
case
'M'
:
fac
*=
60
;
comma
[
strlen
(
comma
)
-
1
]
=
0
;
/* fall through */
case
's'
:
case
'S'
:
a
[
leasepos
][
strlen
(
a
[
leasepos
])
-
1
]
=
0
;
}
new
->
lease_time
=
atoi
(
comma
)
*
fac
;
new
->
lease_time
=
atoi
(
a
[
leasepos
])
*
fac
;
if
(
new
->
lease_time
<
*
min_leasetime
)
*
min_leasetime
=
new
->
lease_time
;
}
}
}
if
((
new
->
end
.
s_addr
=
inet_addr
(
optarg
))
==
(
in_addr_t
)
-
1
)
{
option
=
'?'
;
break
;
}
new
->
last
=
new
->
start
;
new
->
iface
=
NULL
;
break
;
}
...
...
@@ -636,8 +700,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
struct
in_addr
in
;
new
->
next
=
*
dhcp_conf
;
*
dhcp_conf
=
new
;
memset
(
new
->
hwaddr
,
0
,
ETHER_ADDR_LEN
);
new
->
clid_len
=
0
;
new
->
clid
=
NULL
;
...
...
@@ -715,7 +778,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
case
'm'
:
case
'M'
:
fac
*=
60
;
/* fall through */
case
's'
:
case
'S'
:
*
lastp
=
0
;
}
}
...
...
@@ -734,32 +799,60 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
new
->
hostname
=
safe_string_alloc
(
a
[
j
]);
}
else
new
->
lease_time
=
atoi
(
a
[
j
])
*
fac
;
{
new
->
lease_time
=
atoi
(
a
[
j
])
*
fac
;
if
(
new
->
lease_time
<
*
min_leasetime
)
*
min_leasetime
=
new
->
lease_time
;
}
}
if
(
option
==
'?'
)
free
(
new
);
else
*
dhcp_conf
=
new
;
break
;
}
case
'O'
:
{
struct
dhcp_opt
*
new
=
safe_malloc
(
sizeof
(
struct
dhcp_opt
));
char
*
cp
,
*
comma
=
strchr
(
optarg
,
','
)
;
char
*
cp
,
*
comma
;
int
addrs
,
is_addr
;
new
->
next
=
*
dhcp_opts
;
new
->
len
=
0
;
new
->
is_addr
=
0
;
*
dhcp_opts
=
new
;
new
->
netid
=
NULL
;
if
((
comma
=
strchr
(
optarg
,
','
)))
{
*
comma
=
0
;
for
(
cp
=
optarg
;
*
cp
;
cp
++
)
if
(
!
(
*
cp
==
' '
||
(
*
cp
>=
'0'
&&
*
cp
<=
'9'
)))
break
;
if
(
*
cp
)
{
new
->
netid
=
safe_string_alloc
(
optarg
);
optarg
=
comma
+
1
;
if
((
comma
=
strchr
(
optarg
,
','
)))
*
comma
=
0
;
}
}
if
((
new
->
opt
=
atoi
(
optarg
))
==
0
)
{
option
=
'?'
;
free
(
new
);
break
;
}
if
(
!
comma
)
break
;
*
comma
=
0
;
{
*
dhcp_opts
=
new
;
break
;
}
/* check for non-address list characters */
for
(
addrs
=
1
,
is_addr
=
0
,
cp
=
comma
+
1
;
*
cp
;
cp
++
)
...
...
@@ -805,6 +898,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
}
}
*
dhcp_opts
=
new
;
break
;
}
...
...
@@ -832,7 +926,10 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
if
(
option
==
'?'
)
{
if
(
f
)
die
(
"bad argument for option %s"
,
buff
);
{
sprintf
(
buff
,
"error at line %d of %s "
,
lineno
,
conffile
);
complain
(
buff
,
NULL
);
}
else
die
(
"bad command line options: try --help."
,
NULL
);
}
...
...
src/rfc1035.c
View file @
44a2a316
...
...
@@ -620,7 +620,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
header
->
ancount
=
htons
(
0
);
/* no answers unless changed below*/
if
(
flags
==
F_NEG
)
header
->
rcode
=
SERVFAIL
;
/* couldn't get memory */
else
if
(
flags
==
F_NOERR
)
else
if
(
flags
==
F_NOERR
||
flags
==
F_QUERY
)
header
->
rcode
=
NOERROR
;
/* empty domain */
else
if
(
flags
==
F_NXDOMAIN
)
header
->
rcode
=
NXDOMAIN
;
...
...
src/rfc2131.c
View file @
44a2a316
...
...
@@ -25,14 +25,17 @@
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_CLIENT_ID 61
#define OPTION_REQUESTED_IP 50
#define OPTION_LEASE_TIME 51
#define OPTION_OVERLOAD 52
#define OPTION_MESSAGE_TYPE 53
#define OPTION_SERVER_IDENTIFIER 54
#define OPTION_REQUESTED_OPTIONS 55
#define OPTION_MESSAGE 56
#define OPTION_MAXMESSAGE 57
#define OPTION_T1 58
#define OPTION_T2 59
#define OPTION_CLIENT_ID 61
#define OPTION_END 255
#define DHCPDISCOVER 1
...
...
@@ -45,21 +48,29 @@
#define DHCPINFORM 8
static
unsigned
char
*
option_put
(
unsigned
char
*
p
,
unsigned
char
*
end
,
int
opt
,
int
len
,
unsigned
int
val
);
static
unsigned
char
*
option_put_string
(
unsigned
char
*
p
,
unsigned
char
*
end
,
int
opt
,
char
*
string
);
static
void
bootp_option_put
(
struct
dhcp_packet
*
mess
,
char
*
filename
,
char
*
sname
);
static
int
option_len
(
unsigned
char
*
opt
);
static
void
*
option_ptr
(
unsigned
char
*
opt
);
static
struct
in_addr
option_addr
(
unsigned
char
*
opt
);
static
unsigned
int
option_uint
(
unsigned
char
*
opt
);
static
void
log_packet
(
char
*
type
,
struct
in_addr
*
addr
,
unsigned
char
*
hwaddr
,
char
*
interface
);
static
unsigned
int
option_uint
(
unsigned
char
*
opt
,
int
size
);
static
void
log_packet
(
char
*
type
,
struct
in_addr
*
addr
,
unsigned
char
*
hwaddr
,
char
*
interface
,
char
*
string
);
static
unsigned
char
*
option_find
(
struct
dhcp_packet
*
mess
,
int
size
,
int
opt_type
);
static
unsigned
char
*
do_req_options
(
struct
dhcp_context
*
context
,
unsigned
char
*
p
,
unsigned
char
*
end
,
unsigned
char
*
req_options
,
struct
dhcp_opt
*
config_opts
,
char
*
domainname
,
char
*
hostname
);
char
*
domainname
,
char
*
hostname
,
struct
in_addr
relay
,
struct
in_addr
iface_addr
,
int
iface_mtu
);
int
dhcp_reply
(
struct
dhcp_context
*
context
,
struct
dhcp_packet
*
mess
,
int
dhcp_reply
(
struct
dhcp_context
*
context
,
struct
in_addr
iface_addr
,
char
*
iface_name
,
int
iface_mtu
,
struct
udp_dhcp_packet
*
rawpacket
,
unsigned
int
sz
,
time_t
now
,
char
*
namebuff
,
struct
dhcp_opt
*
dhcp_opts
,
struct
dhcp_config
*
dhcp_configs
,
char
*
domain_suffix
,
char
*
dhcp_file
,
char
*
dhcp_sname
,
...
...
@@ -68,9 +79,13 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
unsigned
char
*
opt
,
*
clid
;
struct
dhcp_lease
*
lease
;
int
clid_len
;
struct
dhcp_packet
*
mess
=
&
rawpacket
->
data
;
unsigned
char
*
p
=
mess
->
options
;
/* default max reply packet length, max be overridden */
unsigned
char
*
end
=
(
unsigned
char
*
)(
rawpacket
+
1
);
char
*
hostname
=
NULL
;
char
*
req_options
=
NULL
;
char
*
message
=
NULL
;
unsigned
int
renewal_time
,
expires_time
,
def_time
;
struct
dhcp_config
*
config
;
...
...
@@ -82,6 +97,17 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
mess
->
op
=
BOOTREPLY
;
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_MAXMESSAGE
)))
{
int
maxsize
=
(
int
)
option_uint
(
opt
,
2
);
if
(
maxsize
>
DNSMASQ_PACKETSZ
)
maxsize
=
DNSMASQ_PACKETSZ
;
if
(
maxsize
>
iface_mtu
)
maxsize
=
iface_mtu
;
end
=
((
unsigned
char
*
)
rawpacket
)
+
maxsize
;
}
/* If there is no client identifier option, use the hardware address */
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_CLIENT_ID
)))
{
...
...
@@ -120,20 +146,19 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
/* ensure there are no strange chars in there */
if
(
!
canonicalise
(
hostname
))
hostname
=
NULL
;
}
if
(
hostname
)
{
char
*
dot
=
strchr
(
hostname
,
'.'
);
if
(
dot
)
else
{
if
(
!
domain_suffix
||
!
hostname_isequal
(
dot
+
1
,
domain_suffix
))
char
*
dot
=
strchr
(
hostname
,
'.'
);
if
(
dot
)
{
syslog
(
LOG_WARNING
,
"Ignoring DHCP host name %s because it has an illegal domain part"
,
hostname
);
hostname
=
NULL
;
if
(
!
domain_suffix
||
!
hostname_isequal
(
dot
+
1
,
domain_suffix
))
{
syslog
(
LOG_WARNING
,
"Ignoring DHCP host name %s because it has an illegal domain part"
,
hostname
);
hostname
=
NULL
;
}
else
*
dot
=
0
;
/* truncate */
}
else
*
dot
=
0
;
/* truncate */
}
}
...
...
@@ -143,7 +168,7 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_LEASE_TIME
)))
{
unsigned
int
req_time
=
option_uint
(
opt
);
unsigned
int
req_time
=
option_uint
(
opt
,
4
);
if
(
def_time
==
0xffffffff
||
(
req_time
!=
0xffffffff
&&
req_time
<
def_time
))
...
...
@@ -165,22 +190,64 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
switch
(
opt
[
2
])
{
case
DHCPRELEASE
:
if
(
lease
)
case
DHCPDECLINE
:
if
(
!
(
opt
=
option_find
(
mess
,
sz
,
OPTION_SERVER_IDENTIFIER
))
||
(
iface_addr
.
s_addr
!=
option_addr
(
opt
).
s_addr
))
return
0
;
/* sanitise any message. Paranoid? Moi? */
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_MESSAGE
)))
{
char
*
p
=
option_ptr
(
opt
),
*
q
=
namebuff
;
int
i
;
for
(
i
=
option_len
(
opt
);
i
>
0
;
i
--
)
{
char
c
=
*
p
++
;
if
(
isprint
(
c
))
*
q
++
=
c
;
}
*
q
++
=
0
;
/* add terminator */
message
=
namebuff
;
}
if
(
!
(
opt
=
option_find
(
mess
,
sz
,
OPTION_REQUESTED_IP
)))
return
0
;
log_packet
(
"DECLINE"
,
option_ptr
(
opt
),
mess
->
chaddr
,
iface_name
,
message
);
if
(
lease
&&
lease
->
addr
.
s_addr
==
option_addr
(
opt
).
s_addr
)
lease_prune
(
lease
,
now
);
if
(
config
&&
config
->
addr
.
s_addr
&&
config
->
addr
.
s_addr
==
option_addr
(
opt
).
s_addr
)
{
log_packet
(
"RELEASE"
,
&
lease
->
addr
,
mess
->
chaddr
,
context
->
iface
);
lease_prune
(
lease
,
now
)
;
syslog
(
LOG_WARNING
,
"disabling DHCP static address %s"
,
inet_ntoa
(
config
->
addr
)
);
config
->
addr
.
s_addr
=
0
;
}
return
0
;
case
DHCPRELEASE
:
if
(
!
(
opt
=
option_find
(
mess
,
sz
,
OPTION_SERVER_IDENTIFIER
))
||
(
iface_addr
.
s_addr
!=
option_addr
(
opt
).
s_addr
))
return
0
;
log_packet
(
"RELEASE"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
iface_name
,
NULL
);
if
(
lease
&&
lease
->
addr
.
s_addr
==
mess
->
ciaddr
.
s_addr
)
lease_prune
(
lease
,
now
);
return
0
;
case
DHCPDISCOVER
:
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_REQUESTED_IP
)))
mess
->
yiaddr
=
option_addr
(
opt
);
log_packet
(
"DISCOVER"
,
opt
?
&
mess
->
yiaddr
:
NULL
,
mess
->
chaddr
,
context
->
iface
);
log_packet
(
"DISCOVER"
,
opt
?
&
mess
->
yiaddr
:
NULL
,
mess
->
chaddr
,
iface_name
,
NULL
);
if
(
lease
)
if
(
lease
&&
((
lease
->
addr
.
s_addr
&
context
->
netmask
.
s_addr
)
==
(
context
->
start
.
s_addr
&
context
->
netmask
.
s_addr
)))
mess
->
yiaddr
=
lease
->
addr
;
else
if
(
config
&&
config
->
addr
.
s_addr
&&
!
lease_find_by_addr
(
config
->
addr
))
mess
->
yiaddr
=
config
->
addr
;
...
...
@@ -193,50 +260,30 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
bootp_option_put
(
mess
,
dhcp_file
,
dhcp_sname
);
mess
->
siaddr
=
dhcp_next_server
;
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_MESSAGE_TYPE
,
1
,
DHCPOFFER
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
context
->
serv_addr
.
s_addr
));
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_LEASE_TIME
,
4
,
expires_time
);
p
=
do_req_options
(
context
,
p
,
&
mess
->
options
[
308
],
req_options
,
dhcp_opts
,
domain_suffix
,
NULL
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_END
,
0
,
0
);
p
=
option_put
(
p
,
end
,
OPTION_MESSAGE_TYPE
,
1
,
DHCPOFFER
);
p
=
option_put
(
p
,
end
,
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
iface_addr
.
s_addr
));
p
=
option_put
(
p
,
end
,
OPTION_LEASE_TIME
,
4
,
expires_time
);
p
=
do_req_options
(
context
,
p
,
end
,
req_options
,
dhcp_opts
,
domain_suffix
,
NULL
,
mess
->
giaddr
,
iface_addr
,
iface_mtu
);
p
=
option_put
(
p
,
end
,
OPTION_END
,
0
,
0
);
log_packet
(
"OFFER"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
context
->
iface
);
log_packet
(
"OFFER"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
iface_name
,
NULL
);
return
p
-
(
unsigned
char
*
)
mess
;
case
DHCPREQUEST
:
if
(
mess
->
ciaddr
.
s_addr
)
{
/* RENEWING or REBINDING */
/* Must exist a lease for this address */
log_packet
(
"REQUEST"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
context
->
iface
);
if
(
!
lease
||
mess
->
ciaddr
.
s_addr
!=
lease
->
addr
.
s_addr
)
{
log_packet
(
"NAK"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
context
->
iface
);
mess
->
siaddr
.
s_addr
=
mess
->
yiaddr
.
s_addr
=
mess
->
ciaddr
.
s_addr
=
0
;
bootp_option_put
(
mess
,
NULL
,
NULL
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_MESSAGE_TYPE
,
1
,
DHCPNAK
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_END
,
0
,
0
);
return
(
unsigned
char
*
)
mess
-
p
;
/* -ve to force bcast */
}
mess
->
yiaddr
=
mess
->
ciaddr
;
}
else
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_REQUESTED_IP
)))
{
/* SELECTING or INIT_REBOOT */
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_SERVER_IDENTIFIER
))
&&
(
context
->
serv_addr
.
s_addr
!=
option_addr
(
opt
).
s_addr
))
return
0
;
mess
->
yiaddr
=
option_addr
(
opt
);
/* The RFC says that this is already zero, but there exist
real-world counter examples. */
mess
->
ciaddr
.
s_addr
=
0
;
if
(
!
(
opt
=
option_find
(
mess
,
sz
,
OPTION_REQUESTED_IP
)))
if
((
opt
=
option_find
(
mess
,
sz
,
OPTION_SERVER_IDENTIFIER
))
&&
(
iface_addr
.
s_addr
!=
option_addr
(
opt
).
s_addr
))
return
0
;
mess
->
yiaddr
=
option_addr
(
opt
);
log_packet
(
"REQUEST"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
context
->
iface
);
/* If a lease exists for this host and another address, squash it. */
if
(
lease
&&
lease
->
addr
.
s_addr
!=
mess
->
yiaddr
.
s_addr
)
{
...
...
@@ -246,24 +293,47 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
/* accept addresses in the dynamic range or ones allocated statically to
particular hosts or an address which the host already has. */
if
(
!
lease
&&
!
address_available
(
context
,
mess
->
yiaddr
)
&&
(
!
config
||
config
->
addr
.
s_addr
==
0
||
config
->
addr
.
s_addr
!=
mess
->
yiaddr
.
s_addr
))
{
log_packet
(
"NAK"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
context
->
iface
);
mess
->
siaddr
.
s_addr
=
mess
->
yiaddr
.
s_addr
=
mess
->
ciaddr
.
s_addr
=
0
;
bootp_option_put
(
mess
,
NULL
,
NULL
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_MESSAGE_TYPE
,
1
,
DHCPNAK
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_END
,
0
,
0
);
return
(
unsigned
char
*
)
mess
-
p
;
/* -ve to force bcast */
if
(
!
lease
)
{
if
(
!
address_available
(
context
,
mess
->
yiaddr
)
&&
(
!
config
||
config
->
addr
.
s_addr
==
0
||
config
->
addr
.
s_addr
!=
mess
->
yiaddr
.
s_addr
))
message
=
"address unavailable"
;
else
if
(
!
(
lease
=
lease_allocate
(
clid
,
clid_len
,
mess
->
yiaddr
)))
message
=
"no leases left"
;
}
if
(
!
lease
&&
!
(
lease
=
lease_allocate
(
clid
,
clid_len
,
mess
->
yiaddr
)))
}
else
{
/* RENEWING or REBINDING */
/* Must exist a lease for this address */
if
(
!
mess
->
ciaddr
.
s_addr
)
return
0
;
}
mess
->
yiaddr
=
mess
->
ciaddr
;
if
(
!
lease
||
mess
->
ciaddr
.
s_addr
!=
lease
->
addr
.
s_addr
)
message
=
"lease not found"
;
}
/* If a machine moves networks whilst it has a lease, we catch that here. */
if
((
mess
->
yiaddr
.
s_addr
&
context
->
netmask
.
s_addr
)
!=
(
context
->
start
.
s_addr
&
context
->
netmask
.
s_addr
))
message
=
"wrong network"
;
log_packet
(
"REQUEST"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
iface_name
,
NULL
);
if
(
message
)
{
log_packet
(
"NAK"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
iface_name
,
message
);
mess
->
siaddr
.
s_addr
=
mess
->
yiaddr
.
s_addr
=
mess
->
ciaddr
.
s_addr
=
0
;
bootp_option_put
(
mess
,
NULL
,
NULL
);
p
=
option_put
(
p
,
end
,
OPTION_MESSAGE_TYPE
,
1
,
DHCPNAK
);
p
=
option_put_string
(
p
,
end
,
OPTION_MESSAGE
,
message
);
p
=
option_put
(
p
,
end
,
OPTION_END
,
0
,
0
);
mess
->
flags
|=
htons
(
0x8000
);
/* broadcast */
return
p
-
(
unsigned
char
*
)
mess
;
}
log_packet
(
"ACK"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
iface_name
,
hostname
);
lease_set_hwaddr
(
lease
,
mess
->
chaddr
);
lease_set_hostname
(
lease
,
hostname
,
domain_suffix
);
...
...
@@ -271,38 +341,48 @@ int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess,
bootp_option_put
(
mess
,
dhcp_file
,
dhcp_sname
);
mess
->
siaddr
=
dhcp_next_server
;
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_MESSAGE_TYPE
,
1
,
DHCPACK
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
context
->
serv_addr
.
s_addr
));
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_LEASE_TIME
,
4
,
renewal_time
);
p
=
do_req_options
(
context
,
p
,
&
mess
->
options
[
308
],
req_options
,
dhcp_opts
,
domain_suffix
,
hostname
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_END
,
0
,
0
);
log_packet
(
"ACK"
,
&
mess
->
yiaddr
,
mess
->
chaddr
,
context
->
iface
);
p
=
option_put
(
p
,
end
,
OPTION_MESSAGE_TYPE
,
1
,
DHCPACK
);
p
=
option_put
(
p
,
end
,
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
iface_addr
.
s_addr
));
p
=
option_put
(
p
,
end
,
OPTION_LEASE_TIME
,
4
,
renewal_time
);
if
(
renewal_time
!=
0xffffffff
)
{
unsigned
short
fuzz
=
rand16
();
while
(
fuzz
>
(
renewal_time
/
16
))
fuzz
=
fuzz
/
2
;
p
=
option_put
(
p
,
end
,
OPTION_T1
,
4
,
(
renewal_time
/
2
)
-
fuzz
);
p
=
option_put
(
p
,
end
,
OPTION_T2
,
4
,
((
renewal_time
*
7
)
/
8
)
-
fuzz
);
}
p
=
do_req_options
(
context
,
p
,
end
,
req_options
,
dhcp_opts
,
domain_suffix
,
hostname
,
mess
->
giaddr
,
iface_addr
,
iface_mtu
);
p
=
option_put
(
p
,
end
,
OPTION_END
,
0
,
0
);
return
p
-
(
unsigned
char
*
)
mess
;
case
DHCPINFORM
:
log_packet
(
"INFORM"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
context
->
iface
);
log_packet
(
"INFORM"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
iface_name
,
NULL
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_MESSAGE_TYPE
,
1
,
DHCPACK
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
context
->
serv_addr
.
s_addr
));
p
=
do_req_options
(
context
,
p
,
&
mess
->
options
[
308
],
req_options
,
dhcp_opts
,
domain_suffix
,
hostname
);
p
=
option_put
(
p
,
&
mess
->
options
[
308
],
OPTION_END
,
0
,
0
);
p
=
option_put
(
p
,
end
,
OPTION_MESSAGE_TYPE
,
1
,
DHCPACK
);
p
=
option_put
(
p
,
end
,
OPTION_SERVER_IDENTIFIER
,
INADDRSZ
,
ntohl
(
iface_addr
.
s_addr
));
p
=
do_req_options
(
context
,
p
,
end
,
req_options
,
dhcp_opts
,
domain_suffix
,
hostname
,
mess
->
giaddr
,
iface_addr
,
iface_mtu
);
p
=
option_put
(
p
,
end
,
OPTION_END
,
0
,
0
);
log_packet
(
"ACK"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
context
->
ifac
e
);
log_packet
(
"ACK"
,
&
mess
->
ciaddr
,
mess
->
chaddr
,
iface_name
,
hostnam
e
);
return
p
-
(
unsigned
char
*
)
mess
;
}
return
0
;
}
static
void
log_packet
(
char
*
type
,
struct
in_addr
*
addr
,
unsigned
char
*
hwaddr
,
char
*
interface
)
static
void
log_packet
(
char
*
type
,
struct
in_addr
*
addr
,
unsigned
char
*
hwaddr
,
char
*
interface
,
char
*
string
)
{
syslog
(
LOG_INFO
,
"DHCP%s(%s)%s%s
hwaddr=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x
"
,
syslog
(
LOG_INFO
,
"DHCP%s(%s)%s%s
%.2x:%.2x:%.2x:%.2x:%.2x:%.2x%s%s
"
,
type
,
interface
,
addr
?
" "
:
""
,
addr
?
inet_ntoa
(
*
addr
)
:
""
,
hwaddr
[
0
],
hwaddr
[
1
],
hwaddr
[
2
],
hwaddr
[
3
],
hwaddr
[
4
],
hwaddr
[
5
]);
hwaddr
[
0
],
hwaddr
[
1
],
hwaddr
[
2
],
hwaddr
[
3
],
hwaddr
[
4
],
hwaddr
[
5
],
string
?
" "
:
""
,
string
?
string
:
""
);
}
static
int
option_len
(
unsigned
char
*
opt
)
...
...
@@ -326,14 +406,17 @@ static struct in_addr option_addr(unsigned char *opt)
return
ret
;
}
static
unsigned
int
option_uint
(
unsigned
char
*
opt
)
static
unsigned
int
option_uint
(
unsigned
char
*
opt
,
int
size
)
{
/* this worries about unaligned data and byte order */
unsigned
int
ret
;
memcpy
(
&
ret
,
option_ptr
(
opt
),
sizeof
(
unsigned
int
)
);
unsigned
int
ret
=
0
;
int
i
;
unsigned
char
*
p
=
option_ptr
(
opt
);
return
ntohl
(
ret
);
for
(
i
=
0
;
i
<
size
;
i
++
)
ret
=
(
ret
<<
8
)
|
*
p
++
;
return
ret
;
}
static
void
bootp_option_put
(
struct
dhcp_packet
*
mess
,
char
*
filename
,
char
*
sname
)
...
...
@@ -349,21 +432,36 @@ static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sna
static
unsigned
char
*
option_put
(
unsigned
char
*
p
,
unsigned
char
*
end
,
int
opt
,
int
len
,
unsigned
int
val
)
{
int
i
;
if
(
p
+
len
+
2
<
end
)
/* always keep one octet space for the END option. */
if
((
opt
==
OPTION_END
)
||
(
p
+
len
+
3
<
end
))
{
*
(
p
++
)
=
opt
;
*
(
p
++
)
=
len
;
for
(
i
=
0
;
i
<
len
;
i
++
)
*
(
p
++
)
=
val
>>
(
8
*
(
len
-
(
i
+
1
)));
if
(
opt
!=
OPTION_END
)
{
*
(
p
++
)
=
len
;
for
(
i
=
0
;
i
<
len
;
i
++
)
*
(
p
++
)
=
val
>>
(
8
*
(
len
-
(
i
+
1
)));
}
}
return
p
;
}
static
unsigned
char
*
option_put_string
(
unsigned
char
*
p
,
unsigned
char
*
end
,
int
opt
,
char
*
string
)
{
if
(
p
+
strlen
(
string
)
+
3
<
end
)
{
*
(
p
++
)
=
opt
;
*
(
p
++
)
=
strlen
(
string
);
memcpy
(
p
,
string
,
strlen
(
string
));
p
+=
strlen
(
string
);
}
return
p
;
}
static
unsigned
char
*
option_find1
(
unsigned
char
*
p
,
unsigned
char
*
end
,
int
opt
,
int
*
overload
)
{
if
(
!
p
)
return
NULL
;
...
...
@@ -425,10 +523,11 @@ static int in_list(unsigned char *list, int opt)
return
0
;
}
static
struct
dhcp_opt
*
option_find2
(
struct
dhcp_opt
*
opts
,
int
opt
)
static
struct
dhcp_opt
*
option_find2
(
struct
dhcp_
context
*
context
,
struct
dhcp_
opt
*
opts
,
int
opt
)
{
for
(;
opts
;
opts
=
opts
->
next
)
if
(
opts
->
opt
==
opt
)
if
(
opts
->
opt
==
opt
&&
(
!
opts
->
netid
||
(
context
->
netid
&&
strcmp
(
opts
->
netid
,
context
->
netid
)
==
0
)))
return
opts
;
return
NULL
;
}
...
...
@@ -437,59 +536,53 @@ static unsigned char *do_req_options(struct dhcp_context *context,
unsigned
char
*
p
,
unsigned
char
*
end
,
unsigned
char
*
req_options
,
struct
dhcp_opt
*
config_opts
,
char
*
domainname
,
char
*
hostname
)
char
*
domainname
,
char
*
hostname
,
struct
in_addr
relay
,
struct
in_addr
iface_addr
,
int
iface_mtu
)
{
int
i
;
if
(
!
req_options
)
return
p
;
if
(
in_list
(
req_options
,
OPTION_MAXMESSAGE
))
p
=
option_put
(
p
,
end
,
OPTION_MAXMESSAGE
,
2
,
sizeof
(
struct
udp_dhcp_packet
));
p
=
option_put
(
p
,
end
,
OPTION_MAXMESSAGE
,
2
,
DNSMASQ_PACKETSZ
>
iface_mtu
?
iface_mtu
:
DNSMASQ_PACKETSZ
);
if
(
in_list
(
req_options
,
OPTION_NETMASK
)
&&
!
option_find2
(
config_opts
,
OPTION_NETMASK
))
!
option_find2
(
con
text
,
con
fig_opts
,
OPTION_NETMASK
))
p
=
option_put
(
p
,
end
,
OPTION_NETMASK
,
INADDRSZ
,
ntohl
(
context
->
netmask
.
s_addr
));
if
(
in_list
(
req_options
,
OPTION_BROADCAST
)
&&
!
option_find2
(
config_opts
,
OPTION_BROADCAST
))
!
option_find2
(
con
text
,
con
fig_opts
,
OPTION_BROADCAST
))
p
=
option_put
(
p
,
end
,
OPTION_BROADCAST
,
INADDRSZ
,
ntohl
(
context
->
broadcast
.
s_addr
));
if
(
in_list
(
req_options
,
OPTION_ROUTER
)
&&
!
option_find2
(
config_opts
,
OPTION_ROUTER
))
p
=
option_put
(
p
,
end
,
OPTION_ROUTER
,
INADDRSZ
,
ntohl
(
context
->
serv_addr
.
s_addr
));
!
option_find2
(
context
,
config_opts
,
OPTION_ROUTER
))
p
=
option_put
(
p
,
end
,
OPTION_ROUTER
,
INADDRSZ
,
ntohl
(
relay
.
s_addr
?
relay
.
s_addr
:
iface_addr
.
s_addr
));
if
(
in_list
(
req_options
,
OPTION_DNSSERVER
)
&&
!
option_find2
(
config_opts
,
OPTION_DNSSERVER
))
p
=
option_put
(
p
,
end
,
OPTION_DNSSERVER
,
INADDRSZ
,
ntohl
(
context
->
serv
_addr
.
s_addr
));
!
option_find2
(
con
text
,
con
fig_opts
,
OPTION_DNSSERVER
))
p
=
option_put
(
p
,
end
,
OPTION_DNSSERVER
,
INADDRSZ
,
ntohl
(
iface
_addr
.
s_addr
));
if
(
in_list
(
req_options
,
OPTION_DOMAINNAME
)
&&
!
option_find2
(
config_opts
,
OPTION_DOMAINNAME
)
&&
domainname
&&
(
p
+
strlen
(
domainname
)
+
2
<
end
))
{
*
(
p
++
)
=
OPTION_DOMAINNAME
;
*
(
p
++
)
=
strlen
(
domainname
);
memcpy
(
p
,
domainname
,
strlen
(
domainname
));
p
+=
strlen
(
domainname
);
}
if
(
domainname
&&
in_list
(
req_options
,
OPTION_DOMAINNAME
)
&&
!
option_find2
(
context
,
config_opts
,
OPTION_DOMAINNAME
))
p
=
option_put_string
(
p
,
end
,
OPTION_DOMAINNAME
,
domainname
);
/* Note that we ignore attempts to set the hostname using
--dhcp-option=12,<name> */
if
(
in_list
(
req_options
,
OPTION_HOSTNAME
)
&&
hostname
&&
(
p
+
strlen
(
hostname
)
+
2
<
end
))
{
*
(
p
++
)
=
OPTION_HOSTNAME
;
*
(
p
++
)
=
strlen
(
hostname
);
memcpy
(
p
,
hostname
,
strlen
(
hostname
));
p
+=
strlen
(
hostname
);
}
if
(
hostname
&&
in_list
(
req_options
,
OPTION_HOSTNAME
))
p
=
option_put_string
(
p
,
end
,
OPTION_HOSTNAME
,
hostname
);
for
(
i
=
0
;
req_options
[
i
]
!=
OPTION_END
;
i
++
)
{
struct
dhcp_opt
*
opt
=
option_find2
(
config_opts
,
req_options
[
i
]);
struct
dhcp_opt
*
opt
=
option_find2
(
con
text
,
con
fig_opts
,
req_options
[
i
]);
if
(
req_options
[
i
]
!=
OPTION_HOSTNAME
&&
req_options
[
i
]
!=
OPTION_MAXMESSAGE
&&
opt
&&
(
p
+
opt
->
len
+
2
<
end
))
opt
&&
(
p
+
opt
->
len
+
3
<
end
))
{
*
(
p
++
)
=
opt
->
opt
;
*
(
p
++
)
=
opt
->
len
;
...
...
@@ -503,7 +596,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
{
/* zero means "self" */
if
(
a
->
s_addr
==
0
)
memcpy
(
p
,
&
context
->
serv
_addr
,
INADDRSZ
);
memcpy
(
p
,
&
iface
_addr
,
INADDRSZ
);
else
memcpy
(
p
,
a
,
INADDRSZ
);
p
+=
INADDRSZ
;
...
...
src/util.c
View file @
44a2a316
...
...
@@ -61,17 +61,15 @@ unsigned short rand16(void)
else
{
s
=
(
char
*
)
&
seed
;
while
(
(
c
<
sizeof
(
seed
))
&&
((
n
=
read
(
fd
,
sbuf
,
sizeof
(
seed
))
>
0
))
)
while
((
c
<
sizeof
(
seed
))
&&
((
n
=
read
(
fd
,
sbuf
,
sizeof
(
seed
))
>
0
))
)
{
memcpy
(
s
,
sbuf
,
n
);
s
+=
n
;
c
+=
n
;
}
if
(
n
<
0
)
{
seed
=
badseed
;
}
seed
=
badseed
;
close
(
fd
);
}
...
...
@@ -215,3 +213,18 @@ int hostname_isequal(unsigned char *a, unsigned char *b)
return
1
;
}
time_t
dnsmasq_time
(
int
fd
)
{
#ifdef HAVE_BROKEN_RTC
/* we use uptime as a time-base, rather than epoch time
because epoch time can break when a machine contacts
a nameserver and updates it. */
char
buf
[
30
];
lseek
(
fd
,
0
,
SEEK_SET
);
read
(
fd
,
buf
,
30
);
return
(
time_t
)
atol
(
buf
);
#else
fd
=
0
;
/* stop warning */
return
time
(
NULL
);
#endif
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment