Compare commits

...

224 Commits

Author SHA1 Message Date
Simon Kelley
8b46061e73 Fix DHCPv6 address allocation for some pathalogical cases. 2012-09-08 21:47:28 +01:00
Simon Kelley
4d0f5b4c44 Fix BOOTP option processing. 2012-09-05 23:29:30 +01:00
Simon Kelley
1dedeb87cc Fix Debian package adduser dependency. 2012-09-04 21:50:52 +01:00
Simon Kelley
79cfefd856 Make pid-file creation immune to symlink attack. 2012-09-02 13:29:51 +01:00
Simon Kelley
0c0d4793ac Tidy buffer use in DHCP startup logging. 2012-09-02 12:57:43 +01:00
Simon Kelley
12d71ed28c Finesse the check for /etc/hosts names which conflict with DHCP names. 2012-08-30 15:16:41 +01:00
Simon Kelley
9fed0f71c2 Further tweaks to DHCP FQDN option. 2012-08-30 11:43:35 +01:00
Simon Kelley
2e34ac1403 Handle DHCP FQDN option with all flags zero and --dhcp-client-update 2012-08-29 14:15:25 +01:00
Simon Kelley
bc54ae392b Debian packaging fixes. 2012-08-28 21:26:56 +01:00
Simon Kelley
00acd06340 Tweak get-version to do the right thing with multiple head tags. 2012-08-17 14:18:50 +01:00
Simon Kelley
476e4a03c1 Bump Debian version 2012-08-17 13:45:49 +01:00
Simon Kelley
5f11b3e5e0 Cope with --listen-address for not yet existent addr in bind-dynamic mode. 2012-08-16 14:04:05 +01:00
Simon Kelley
3169daad46 Fix TFTP access control, broken earlier in release. 2012-08-13 17:39:57 +01:00
Simon Kelley
fd05f12790 Set prefix on-link bit in RAs 2012-08-12 17:48:50 +01:00
Simon Kelley
ad094275b0 Alternate DBus service name via --enable-dbus 2012-08-10 17:10:54 +01:00
Simon Kelley
c740e4f342 Fix FTBFS when -DNO_DHCP - thanks Sung Pae. 2012-08-09 16:19:01 +01:00
Simon Kelley
132255b5da OpenBSD build fix. 2012-08-06 20:12:04 +01:00
Simon Kelley
c4c0488ac6 Update french translation. 2012-08-06 20:09:15 +01:00
Simon Kelley
a2ce6fcc91 Man page typos 2012-08-06 20:05:48 +01:00
Simon Kelley
12090548d2 Add debian/dnsmasq-base.conffiles 2012-08-06 20:00:58 +01:00
Simon Kelley
8223cb15e7 Update FAQ to cover --bind-dynamic. 2012-07-29 20:21:57 +01:00
Simon Kelley
4ba9b38cc5 Debian package: move /etc/dbus-1/system.d/dnsmasq.conf. 2012-07-29 17:07:48 +01:00
Simon Kelley
42243214b5 "w" multiplier in lease times. 2012-07-20 15:19:18 +01:00
Simon Kelley
23245c0cb2 RFC 4242 support. 2012-07-18 16:21:11 +01:00
Simon Kelley
b271446f82 Typo. 2012-07-17 12:09:26 +01:00
Simon Kelley
611ebc5f1e Fix broken caching of CNAME chains. 2012-07-16 16:23:46 +01:00
Simon Kelley
be0f45cdbc Typo fix. Thanks Wieland Hoffmann. 2012-07-16 13:35:25 +01:00
Simon Kelley
9b40cbf587 Fix FTBFS when TFTP disabled. 2012-07-13 19:58:26 +01:00
Simon Kelley
c4a7f90ebb Config parsing error-handling update. 2012-07-12 20:52:12 +01:00
Simon Kelley
9609baee41 Merge branch 'access_control' 2012-07-10 15:06:34 +01:00
Simon Kelley
395eb71931 Better log message when dhcp hosts|opts file cannot be read. 2012-07-06 22:07:05 +01:00
Simon Kelley
8bc4cecee6 Remove libvirt-inspired but never used access control features. 2012-07-03 21:04:11 +01:00
Simon Kelley
6b617c0d15 Logging library in Android build scaffold. 2012-06-29 22:11:26 +01:00
Simon Kelley
55d290a3bf Handle pid-file location in Android. 2012-06-29 20:58:32 +01:00
Simon Kelley
e17b4b3871 Fix build-failure with -DNO_DHCP6. 2012-06-28 21:44:30 +01:00
Simon Kelley
236e072cab Typo in BSD-only code. 2012-06-26 21:33:01 +01:00
Simon Kelley
05ff1ed7cc Man page update. 2012-06-26 16:58:12 +01:00
Simon Kelley
2b5bae9a8f Fall back from --bind-dynamic to --bind-interfaces in BSD, rather than quitting. 2012-06-26 16:55:23 +01:00
Simon Kelley
39f1b8e73d Better logging of socket-creation errors. 2012-06-20 20:04:27 +01:00
Simon Kelley
af576b56c2 Tidy up - no functional change. 2012-06-20 14:17:04 +01:00
Simon Kelley
54dd393f39 Add --bind-dynamic 2012-06-20 11:23:38 +01:00
Simon Kelley
4ce4f3779b Fix un-initialised "used" field in --listen-address structure.
Also remove unused "isloop" field.
2012-06-14 11:50:45 +01:00
Simon Kelley
8b3ae2fd43 Check tftp-root exists and is accessible at startup. 2012-06-13 13:43:49 +01:00
Simon Kelley
ed55cb66e6 Correct listener logic when no TFTP and no-interface listen address. 2012-06-12 21:56:29 +01:00
Simon Kelley
2cd9a0de1f Debian systemd startup fixes for resolvconf integration. 2012-06-11 21:56:10 +01:00
Simon Kelley
c514ab9907 Update Debian changelog. 2012-06-07 15:35:08 +01:00
Simon Kelley
078a630bba Do duplicate dhcp-host address check in --test mode. 2012-06-07 13:56:23 +01:00
Simon Kelley
43c271b07c Debian package build - pass calculated LDFLAGS to make. 2012-06-07 10:02:53 +01:00
Simon Kelley
24ce681e51 Add instructions/patches for dbus activation to contrib/systemd. 2012-06-04 21:40:11 +01:00
Simon Kelley
5ae34bf3c8 Fix RA when interface has more than one address on the same network. 2012-06-04 21:14:03 +01:00
Simon Kelley
51931b888a Fix access control when DHCPv6 but no RA in use. 2012-05-29 17:06:02 +01:00
Simon Kelley
9f7f3b1216 Add --dns-rr option. 2012-05-28 21:39:57 +01:00
Simon Kelley
97c83bb05b Use dpkg-buildflags in Debian rules file. 2012-05-28 18:21:59 +01:00
Simon Kelley
8767ceecd4 Make libvirt-style access control work when only doing RA. 2012-05-21 20:54:19 +01:00
Simon Kelley
18c63eff8f Fix non-response to router-solicitations when
router-advertisement configured, but DHCPv6 not
configured.
2012-05-21 14:34:15 +01:00
Simon Kelley
c64b7f6a78 Fix is_same_net6 - bugged if prefix length not divisible by 8. 2012-05-18 10:19:59 +01:00
Simon Kelley
068b4b51e3 Bump Debian version. 2012-05-12 15:25:33 +01:00
Simon Kelley
919dd7cf14 Fixed missing periodic-ras in some configurations. 2012-05-12 15:23:09 +01:00
Simon Kelley
f632e56793 Cope with router-solicit packets without valid source address. 2012-05-12 15:05:34 +01:00
Simon Kelley
2021c66251 code-size tweak 2012-05-07 16:43:21 +01:00
Simon Kelley
8358e0f4b2 Update German translation and fix CHANGELOG typos. Sorry, Conrad! 2012-04-29 21:53:09 +01:00
Simon Kelley
7f61b3ad59 Small SLAAC optimisation. 2012-04-29 16:01:28 +01:00
Simon Kelley
a9ab732e35 reconfirm SLAAC addresses when DHCPv4 leases go though INIT_REBOOT state. 2012-04-29 16:01:28 +01:00
Simon Kelley
11263a462c isprint cast. 2012-04-29 16:01:28 +01:00
Simon Kelley
231d061b45 Tidy TXT record sanitising 2012-04-29 16:01:28 +01:00
Simon Kelley
cdbee9a40b Find room to store key-id and digest-type in DS records.
->uid is now overloaded to store key length
2012-04-27 10:30:49 +01:00
Simon Kelley
7b4ad2eb34 Teach cache to store DS and DNSKEY records 2012-04-27 10:30:49 +01:00
Simon Kelley
19d69be220 CHANGELOG update. 2012-04-27 10:14:34 +01:00
Simon Kelley
04363607aa Fix tftp-over-IPv4 regression on OpenBSD. 2012-04-27 10:11:51 +01:00
Simon Kelley
dcffad2a86 Ensure that the DBus DhcpLeaseUpdated events are generated. 2012-04-24 15:25:18 +01:00
Simon Kelley
6a69ab5ebd Fix error-handling problem in TFTP server. 2012-04-24 14:42:26 +01:00
Simon Kelley
fc92ead0dd CHANGELOG typo. 2012-04-22 21:28:24 +01:00
Simon Kelley
61ce600b20 --tftp-lowercase option. 2012-04-20 21:28:49 +01:00
Simon Kelley
7a14dfebbb Tidy previous commit. 2012-04-20 20:50:42 +01:00
Simon Kelley
42fb8153ba Sanitise filenames logged by TFTP 2012-04-20 17:15:01 +01:00
Simon Kelley
6f13e53886 Tidy up malloc-failure handling. 2012-04-17 14:25:06 +01:00
Simon Kelley
d1c759c5c1 Answer CNAME queries correctly. 2012-04-16 17:26:19 +01:00
Simon Kelley
e46164e0bd Updated French translation. 2012-04-16 16:39:38 +01:00
Simon Kelley
7389ce7ff5 substitue non-portable tail command with sed. 2012-04-16 15:07:48 +01:00
Simon Kelley
2f77797b17 Add port option to example dnsmasq.conf 2012-04-16 14:58:53 +01:00
Simon Kelley
9380ba70d6 Set SO_BINDTODEVICE on DHCP sockets when doing DHCP on one interface
only. Fixes OpenSTack use-case.
2012-04-16 14:41:56 +01:00
Simon Kelley
1023dcbc9e Don't cache DNS data from non-recursive nameservers. 2012-04-09 18:00:08 +01:00
Simon Kelley
83e854e359 Typo. 2012-04-05 13:21:58 +01:00
Simon Kelley
50303b19d8 Remove redundant send_from logging code. 2012-04-04 22:13:17 +01:00
Simon Kelley
89382bacaa Tweak sending ICMP6 echo requests for slaac. 2012-04-04 20:48:16 +01:00
Simon Kelley
6c559c34df tweak favicon 2012-04-02 20:40:34 +01:00
Simon Kelley
adaa6888dd Move FIXME message to comment - having it emitted by the code is just confusing. 2012-04-02 10:02:12 +01:00
Simon Kelley
a813111379 Fix bug in tag-matching logic with negated tags. 2012-03-31 21:35:12 +01:00
Simon Kelley
18f0fb050b RDNSS and DNSSL data in router advertisements. 2012-03-31 21:18:55 +01:00
Simon Kelley
05e92e5afe More RA flag evolution. 2012-03-30 22:24:15 +01:00
Simon Kelley
4723d49dad Set managed RA flag always when doing DHCP. 2012-03-30 21:04:17 +01:00
Simon Kelley
fbbc14541a Fix off-by-one in DHCPv6 FQDN option decoding. 2012-03-30 20:48:20 +01:00
Simon Kelley
5ef33279f2 Tidying radv 2012-03-30 15:10:28 +01:00
Simon Kelley
1e02a85970 radv.c tidying. 2012-03-29 11:07:25 +01:00
Simon Kelley
0e88d53faa Fix preprocessor checks, IP_TOS -> IPV6_TCLASS 2012-03-28 22:22:05 +01:00
Simon Kelley
01d1b8ddf2 Changelog update. 2012-03-28 21:37:25 +01:00
Simon Kelley
c8257540bc "deprecated" lease-time keyword for IPv6 2012-03-28 21:15:41 +01:00
Simon Kelley
2240704863 DHCP start-up logging tweak 2012-03-27 14:42:48 +01:00
Simon Kelley
e8ca69ea16 Doc updates for latest RA changes. 2012-03-26 21:23:26 +01:00
Simon Kelley
da632e7cc1 Comment typo. 2012-03-26 11:14:05 +01:00
Simon Kelley
30cd96663f More flexible RA configuration. 2012-03-25 20:44:38 +01:00
Simon Kelley
7dbe98147d tweak ra timer code to avoid missing events. 2012-03-25 14:49:54 +01:00
Simon Kelley
5d71d83420 Listen on ICMP6 file decriptor even when on ra-only only in use. 2012-03-24 14:40:42 +00:00
Simon Kelley
38a59a9ff7 debian changelog untangle. 2012-03-23 10:08:12 +00:00
Simon Kelley
4b028ad612 Merge branch 'bind' 2012-03-23 10:02:30 +00:00
Simon Kelley
442560beb4 Debian changelog for preivious fix. 2012-03-23 10:01:13 +00:00
Simon Kelley
7d2b5c9583 Fix crash in DHCPINFORM without valid --dhcp-range. 2012-03-23 10:00:02 +00:00
Simon Kelley
29689cfa5a Handle errors when sending ICMP6 pings better. 2012-03-22 14:01:00 +00:00
Simon Kelley
52d4abf2f9 Make --listen-address work for all 127.0.0.0/8 addresses. 2012-03-21 21:39:48 +00:00
Simon Kelley
a953096485 Send "FTP transfer complete" events to the DHCP lease script. 2012-03-20 22:07:35 +00:00
Simon Kelley
884a6dfe6d RA managed-bit and use-SLAAC bit tweaks. 2012-03-20 16:20:22 +00:00
Simon Kelley
0068301d24 Conditional compilation tweak. 2012-03-19 20:29:55 +00:00
Simon Kelley
353ae4d270 Check assumed SLAAC addresses by pinging them. 2012-03-19 20:07:51 +00:00
Simon Kelley
e759d426fa --host-record support 2012-03-16 13:18:57 +00:00
Simon Kelley
40ef23b547 Move DHCP option stuff to dhcp-common.c 2012-03-13 21:59:28 +00:00
Simon Kelley
f5e8562f96 More DHCP-option logging tweaks. 2012-03-13 14:22:30 +00:00
Simon Kelley
1567feae3c Log vendor class for DHCPv6 2012-03-12 22:15:35 +00:00
Simon Kelley
daf061c9de randomise DHCPv6 lease renewal intervals 2012-03-12 21:57:18 +00:00
Simon Kelley
d0e2c6c9ab decode DHCPv4 T1, T2 and lease-time options better. 2012-03-12 21:44:14 +00:00
Simon Kelley
8643ec7fea Update CHANGLEOG 2012-03-12 20:04:14 +00:00
Simon Kelley
5cfea3d402 Tidy last commit. 2012-03-12 17:28:27 +00:00
Simon Kelley
6c8f21e4a4 More useful DHCPv6 packet logging. 2012-03-12 15:06:55 +00:00
Simon Kelley
1d0f91c4a9 Don't trust the port in the source address of requests.
At least one client gets it wrong: always send to the client port for
clients, and the server port for relays.
2012-03-12 11:56:22 +00:00
Simon Kelley
2a82db4caf Supply zero preference in advertise and reply messages 2012-03-10 21:40:10 +00:00
Simon Kelley
dd88c17f15 Add status code containing "success" to all IA_TA and IA_NA
which have IAADDR options. This communicates zero information and
RFC3315 is unclear that it's needed, but at least one client seems
to require it.
2012-03-10 20:46:57 +00:00
Simon Kelley
8b37270410 Implement --dhcp-duid 2012-03-09 17:45:10 +00:00
Simon Kelley
760169fc43 Debian updates. 2012-03-09 14:27:49 +00:00
Simon Kelley
7023e38294 Docs changes for ra-names. 2012-03-09 12:05:49 +00:00
Simon Kelley
a7cf58cc47 Merge branch 'ra-names' 2012-03-09 11:37:42 +00:00
Simon Kelley
e25d1a2ea2 Fix prefix-map build code logic. 2012-03-08 13:24:17 +00:00
Simon Kelley
70969c1757 move #include for Solaris and Apple. 2012-03-07 20:46:28 +00:00
Simon Kelley
3803437dcc tidying 2012-03-07 20:39:40 +00:00
Simon Kelley
eabc6dd76a Use getifaddrs on *BSD. 2012-03-07 20:28:20 +00:00
Simon Kelley
e28d2e2b77 Merge branch 'getifaddrs' 2012-03-07 20:26:23 +00:00
Simon Kelley
96fafe2ed6 Fixed typos and tested. 2012-03-07 20:25:39 +00:00
Simon Kelley
c81d390f84 Update man page to reflect the existance of DHCPv6 and RA. 2012-03-07 19:10:19 +00:00
Simon Kelley
08456c61f6 Use getifaddrs to find interfaces on *BSD 2012-03-07 19:08:11 +00:00
Simon Kelley
bc26f9a03f Handle firewire and EUI-64 addresses in the SLAAC code. 2012-03-07 13:13:56 +00:00
Simon Kelley
6ffeff86be Teach emit_dbus_signal() about IPv6 DHCPv6 leases. 2012-03-07 10:32:35 +00:00
Simon Kelley
f444cddbaf Don't waste time calculating EUI-64 when a lease doesn't have a name. 2012-03-07 10:15:57 +00:00
Simon Kelley
d13191a46c Bump Debian version number. 2012-03-06 19:57:39 +00:00
Simon Kelley
801ca9a7b7 Add ra-names SLAAC-hostnames from DHCPv4 option. 2012-03-06 19:30:17 +00:00
Simon Kelley
df66e341de Update polish translation. 2012-03-04 20:04:22 +00:00
Simon Kelley
71ee7ee254 Update French translation. 2012-03-03 18:06:49 +00:00
Simon Kelley
a156cae901 Typos in man page. 2012-03-02 21:10:39 +00:00
Simon Kelley
22b135a116 Fix paretheses in ADD_RDLEN - it always returned 1 before. 2012-03-01 19:54:50 +00:00
Simon Kelley
0f08983d85 Be more picly about the MAC address we use for DUID-creation. 2012-03-01 13:43:39 +00:00
Simon Kelley
e3e86343fc Fix DUID generation with HAVE_BROKEN_RTC. 2012-03-01 10:35:34 +00:00
Simon Kelley
7b6dd880f7 Fix socklen_t/size_t confusion in radv.c 2012-03-01 10:26:16 +00:00
Simon Kelley
b7f4020133 Fix FTBFS when HAVE_BROKEN_RTC defined. 2012-02-29 21:43:37 +00:00
Simon Kelley
c46c7c7584 tweak portable get-cwd trick code to work on GNU-make 3.82 2012-02-29 21:37:14 +00:00
Simon Kelley
552af8b988 Fix --localise-queries via interface lo bug. 2012-02-29 20:10:31 +00:00
Simon Kelley
4f8ff361dc Tiny makefile tweak. 2012-02-29 16:01:17 +00:00
Simon Kelley
0010b47439 RA configuration tweaks and documentation improvements. 2012-02-29 12:18:30 +00:00
Simon Kelley
4b86b65d07 Substitute local address for [::] DHCPv6 options, like DHCPv4. 2012-02-29 11:45:37 +00:00
Simon Kelley
248489401a Makefile cleanup - use lower case variables for internal use. 2012-02-29 11:23:41 +00:00
Simon Kelley
bc5992daf6 Merge messages files prior to rc1. 2012-02-28 18:07:15 +00:00
Simon Kelley
fdacfb0119 Makefile: support absolute paths in BUILDDIR 2012-02-28 15:20:25 +00:00
Simon Kelley
0d5d35d052 RA lifetimes must be at least 2 hours 2012-02-27 20:24:40 +00:00
Simon Kelley
843c96b4b3 Make RA without DHCPv6 possible. 2012-02-27 17:42:38 +00:00
Simon Kelley
58dc02ebf2 Order of fields in DHCPv6 log lines now consistent with DHCPv4 2012-02-27 11:49:37 +00:00
Simon Kelley
c239f7de25 rename header files. 2012-02-27 10:56:18 +00:00
Simon Kelley
ac8540c3c5 CHANGELOG entry to RA. 2012-02-26 20:57:31 +00:00
Simon Kelley
22d904db95 Fix RA on *BSD (missing sa_len) 2012-02-26 20:13:45 +00:00
Simon Kelley
741c2952d4 Tidy up RA scheduling. 2012-02-25 13:09:18 +00:00
Simon Kelley
96f6979c4f OpenBSD-friendly bld/get-version script 2012-02-25 11:31:15 +00:00
Simon Kelley
c5379c1ab6 Don't send hoplimit 1 when multicasting RAs 2012-02-24 20:05:52 +00:00
Simon Kelley
a4a5205fd7 Fix ordering problem in multicast setup. 2012-02-24 19:44:05 +00:00
Simon Kelley
c5ad4e7998 Router Advertisement 2012-02-24 16:06:20 +00:00
Simon Kelley
270dc2e199 Fix wrong fallback address in DHCPv6 2012-02-19 20:53:20 +00:00
Simon Kelley
948a0b6e81 don't use -m flag to grep - it's unavailable in OpenBSD 2012-02-19 20:25:01 +00:00
Simon Kelley
87b8ecb13a Fixed code passing tags to helper to work when there are no context tags.
Fixed call to sendto() in dhcp6.c. How did it every work before?
2012-02-18 21:23:16 +00:00
Simon Kelley
e44ddcac63 Fix hang at startup when DHCPv6 enabled on a complex network
configuration - we have to read all the MAC addresses from netlink,
not bail when we find a suitable one.

Fix thinko in dhcp_update_configs - thanks to Hartmut for spotting
that.

Get a sensible address for the default DNS server even when using a
relay.
2012-02-18 17:08:50 +00:00
Simon Kelley
00e9ad5217 Fixes for DHCPv6 tag system. 2012-02-16 21:53:11 +00:00
Simon Kelley
96c3879bed Change Debian rules file to relect the fact that we build DHCPv6 by default. 2012-02-16 20:07:17 +00:00
Simon Kelley
57f460de2f tweak Lua script argument passing and add --dhcp-luascript sectino to manpage. 2012-02-16 20:00:32 +00:00
Simon Kelley
6caacacf6d Putative fix to crash in IPv4 DHCP, introduced whilst
generalising the DHCP option filter code:
don't match options against context tag  when
context->netid.net == NULL, since there's no tag then.
2012-02-15 21:58:33 +00:00
Simon Kelley
60ac5af682 Remove extact-MAC address from DUID code. 2012-02-15 10:41:03 +00:00
Simon Kelley
caa94380ac bugs in IPv6 script calling - wrong DUID and lua relay_address 2012-02-15 10:29:50 +00:00
Simon Kelley
0793380b40 Implement dhcp-ignore-names and DNSMASQ_RELAY_ADDRESS for IPv6
Build DHCPv6 by default.
2012-02-14 20:55:25 +00:00
Simon Kelley
1adadf585d Tweak Makefile so it works again in BSD make.
First cut at man page changes for DHCPv6
2012-02-13 22:15:58 +00:00
Simon Kelley
e5ffdb9c77 BUILD_DIR -> BUILDDIR, for consistency. 2012-02-13 14:28:13 +00:00
Simon Kelley
6da5201092 Merge branch 'makefile' 2012-02-13 12:55:33 +00:00
Simon Kelley
b36ae19434 Add BUILD_DIR variable to makefile. 2012-02-13 12:54:34 +00:00
Simon Kelley
2307eac613 Fix code parsing --domain, broken whilst added IPv6 stuff. 2012-02-13 10:13:13 +00:00
Simon Kelley
127ea40ae7 Don't build DHCPv6 by default, except when build Debian package. 2012-02-11 22:14:52 +00:00
Simon Kelley
6aef600d48 Correct duid generate on FreeBSD 2012-02-11 22:01:50 +00:00
Simon Kelley
98d76a0326 Tweaks to fix compilation on FreeBSD. 2012-02-10 22:16:45 +00:00
Simon Kelley
6ea6dcf05b Update Debian readme for new build options and (belatedly) for the
introduction of dnsmasq-utils.
2012-02-10 21:26:52 +00:00
Simon Kelley
627797800d Report correct error if prefix in dhcp-range is less than 64
Update debian/rules to provide no_dhcp6 and use_lua build opts.
Log DHCPv6 information request packets.
2012-02-10 21:19:25 +00:00
Simon Kelley
c6cc03ed0c Merge branch 'dhcpv6' 2012-02-10 17:36:20 +00:00
Simon Kelley
3d7b550f52 missed DHCP6 conditional compilation. 2012-02-10 17:35:37 +00:00
Simon Kelley
751d6f4ae6 Allow the TFP server or boot server in --pxe-service, to
be a domain name instead of an IP address. This allows for
 round-robin to multiple servers, in the same way as
 --dhcp-boot.
2012-02-10 15:24:51 +00:00
Simon Kelley
a5c72ab51d DHCPv6 vendor class option includes an enterprise number. Handle that. 2012-02-10 13:42:47 +00:00
Simon Kelley
9bbc88762b Fix shell scripting bug in bug scripts. 2012-02-09 21:33:09 +00:00
Simon Kelley
ceae00dddf lease script should work with IPv6 now. 2012-02-09 21:28:14 +00:00
Simon Kelley
3634c54e8d dhcp-ignore and dhcp-match implemented for DHCPv6 now. 2012-02-08 14:22:37 +00:00
Simon Kelley
d74942a03d IPv6 address range parsing for --domain.
Counted string DHCP option type printing
2012-02-07 20:51:56 +00:00
Simon Kelley
70c5e3e076 DHCPDECLINE handling, domain handling, more complete address selection. 2012-02-06 22:05:15 +00:00
Simon Kelley
4cb1b32009 Clean compile, basic DHCPv6 functionality is there.
TODO
     hostname handling.
     update DHCP6 configs from dns
     parse domain=<domain>,<IPv6 range>
     pretty-print counted string options.
     DECLINE messages
     lease-script fro DHCPv6
2012-02-06 14:30:41 +00:00
Simon Kelley
3268e90f5e Make default NO_DHCP6 and remove compiler warning. 2012-01-22 16:15:02 +00:00
Simon Kelley
e98170816a Merge branch 'dhcpv6' 2012-01-22 16:07:22 +00:00
Simon Kelley
52b92f4db8 It compiles and it allocates a lease! No renewals yet. 2012-01-22 16:05:15 +00:00
Simon Kelley
a2761754da Fix problem if dnsmasq is started without the stdin,
stdout and stderr file descriptors open. This can manifest
itself as 100% CPU use. Thanks to Chris Moore for finding
this.
2012-01-18 16:07:21 +00:00
Simon Kelley
805a11345c We don't need to check that a cache record is F_HOST in the
duplicate address code, since we're now searching a temporary hash
which holds only F_HOST records.
2012-01-13 11:51:46 +00:00
Simon Kelley
1ab62aec37 Further tuning of the fast hostfile reading code.
Use the packet buffer for hash-buckets, better hash function.
2012-01-12 11:33:16 +00:00
Simon Kelley
915363f976 Tweaks to hostfile performance work. 2012-01-11 22:00:48 +00:00
Simon Kelley
205fafa577 Improve performance when reading large hostfiles. 2012-01-11 21:31:51 +00:00
Simon Kelley
be2daf4ad5 Enable DHCPv6 compilation 2012-01-07 17:51:57 +00:00
Simon Kelley
8ecfaa4adf Tidied up usage strings. 2012-01-07 15:29:48 +00:00
Simon Kelley
03bfcf6462 FAQ and example config additions for Windows 7 WPAD problem. 2012-01-07 14:37:37 +00:00
Simon Kelley
39bec5ff32 Remove duplicate tags in data supplied to lease script. 2012-01-06 22:36:58 +00:00
Simon Kelley
246839d64a Minimal update of doc.html - remove broken links and add git info. 2012-01-06 20:39:54 +00:00
Simon Kelley
3862deb398 Debian bug #654897 2012-01-06 20:16:07 +00:00
Simon Kelley
5954608577 Updated copyright notices. Happy new year! 2012-01-06 20:02:04 +00:00
Simon Kelley
984d2fded6 CHANGELOG update 2012-01-06 14:34:32 +00:00
Simon Kelley
a4f04ed45a Generate version string from git automatically 2012-01-06 11:47:02 +00:00
Simon Kelley
07736e8dcb VERSION file 2012-01-05 22:00:08 +00:00
Simon Kelley
00fc082d68 bump version in config.h 2012-01-05 21:42:12 +00:00
Simon Kelley
c72daea868 Accumulated 2.60 changes going into git 2012-01-05 21:33:27 +00:00
Simon Kelley
74c95c2542 import of dnsmasq-2.59.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
7de060b08d import of dnsmasq-2.58.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
572b41eb50 import of dnsmasq-2.57.tar.gz 2012-01-05 17:31:15 +00:00
Simon Kelley
28866e9567 import of dnsmasq-2.56.tar.gz 2012-01-05 17:31:15 +00:00
89 changed files with 21132 additions and 9451 deletions

3
Android.mk Normal file
View File

@@ -0,0 +1,3 @@
ifneq ($(TARGET_SIMULATOR),true)
include $(call all-subdir-makefiles)
endif

556
CHANGELOG
View File

@@ -1,17 +1,557 @@
version 2.64
Handle DHCP FQDN options with all flag bits zero and
--dhcp-client-update set. Thanks to Bernd Krumbroeck for
spotting the problem.
Finesse the check for /etc/hosts names which conflict with
DHCP names. Previously a name/address pair in /etc/hosts
which didn't match the name/address of a DHCP lease would
generate a warning. Now that only happesn if there is not
also a match. This allows multiple addresses for a name in
/etc/hosts with one of them assigned via DHCP.
Fix broken vendor-option processing for BOOTP. Thanks to
Hans-Joachim Baader for the bug report.
version 2.63
Do duplicate dhcp-host address check in --test mode.
Check that tftp-root directories are accessible before
start-up. Thanks to Daniel Veillard for the initial patch.
Allow more than one --tfp-root flag. The per-interface
stuff is pointless without that.
Add --bind-dynamic. A hybrid mode between the default and
--bind-interfaces which copes with dynamically created
interfaces.
A couple of fixes to the build system for Android. Thanks
to Metin Kaya for the patches.
Remove the interface:<interface> argument in --dhcp-range, and
the interface argument to --enable-tftp. These were a
still-born attempt to allow automatic isolated
configuration by libvirt, but have never (to my knowledge)
been used, had very strange semantics, and have been
superceded by other mechanisms.
Fixed bug logging filenames when duplicate dhcp-host
addresses are found. Thanks to John Hanks for the patch.
Fix regression in 2.61 which broke caching of CNAME
chains. Thanks to Atul Gupta for the bug report.
Allow the target of a --cname flag to be another --cname.
Teach DHCPv6 about the RFC 4242 information-refresh-time
option, and add parsing if the minutes, hours and days
format for options. Thanks to Francois-Xavier Le Bail for
the suggestion.
Allow "w" (for week) as multiplier in lease times, as well
as seconds, minutes, hours and days. Álvaro Gámez Machado
spotted the ommission.
Update French translation. Thanks to Gildas Le Nadan.
Allow a DBus service name to be given with --enable-dbus
which overrides the default,
uk.org.thekelleys.dnsmasq. Thanks to Mathieu
Trudel-Lapierre for the patch.
Set the "prefix on-link" bit in Router
Advertisements. Thanks to Gui Iribarren for the patch.
version 2.62
Update German translation. Thanks to Conrad Kostecki.
Cope with router-solict packets wich don't have a valid
source address. Thanks to Vladislav Grishenko for the patch.
Fixed bug which caused missing periodic router
advertisements with some configurations. Thanks to
Vladislav Grishenko for the patch.
Fixed bug which broke DHCPv6/RA with prefix lengths
which are not divisible by 8. Thanks to Andre Coetzee
for spotting this.
Fix non-response to router-solicitations when
router-advertisement configured, but DHCPv6 not
configured. Thanks to Marien Zwart for the patch.
Add --dns-rr, to allow arbitrary DNS resource records.
Fixed bug which broke RA scheduling when an interface had
two addresses in the same network. Thanks to Jim Bos for
his help nailing this.
version 2.61
Re-write interface discovery code on *BSD to use
getifaddrs. This is more portable, more straightforward,
and allows us to find the prefix length for IPv6
addresses.
Add ra-names, ra-stateless and slaac keywords for DHCPv6.
Dnsmasq can now synthesise AAAA records for dual-stack
hosts which get IPv6 addresses via SLAAC. It is also now
possible to use SLAAC and stateless DHCPv6, and to
tell clients to use SLAAC addresses as well as DHCP ones.
Thanks to Dave Taht for help with this.
Add --dhcp-duid to allow DUID-EN uids to be used.
Explicity send DHCPv6 replies to the correct port, instead
of relying on clients to send requests with the correct
source address, since at least one client in the wild gets
this wrong. Thanks to Conrad Kostecki for help tracking
this down.
Send a preference value of 255 in DHCPv6 replies when
--dhcp-authoritative is in effect. This tells clients not
to wait around for other DHCP servers.
Better logging of DHCPv6 options.
Add --host-record. Thanks to Rob Zwissler for the
suggestion.
Invoke the DHCP script with action "tftp" when a TFTP file
transfer completes. The size of the file, address to which
it was sent and complete pathname are supplied. Note that
version 2.60 introduced some script incompatibilties
associated with DHCPv6, and this is a further change. To
be safe, scripts should ignore unknown actions, and if
not IPv6-aware, should exit if the environment
variable DNSMASQ_IAID is set. The use-case for this is
to track netboot/install. Suggestion from Shantanu
Gadgil.
Update contrib/port-forward/dnsmasq-portforward to reflect
the above.
Set the environment variable DNSMASQ_LOG_DHCP when running
the script id --log-dhcp is in effect, so that script can
taylor their logging verbosity. Suggestion from Malte
Forkel.
Arrange that addresses specified with --listen-address
work even if there is no interface carrying the
address. This is chiefly useful for IPv4 loopback
addresses, where any address in 127.0.0.0/8 is a valid
loopback address, but normally only 127.0.0.1 appears on
the lo interface. Thanks to Mathieu Trudel-Lapierre for
the idea and initial patch.
Fix crash, introduced in 2.60, when a DHCPINFORM is
received from a network which has no valid dhcp-range.
Thanks to Stephane Glondu for the bug report.
Add a new DHCP lease time keyword, "deprecated" for
--dhcp-range. This is only valid for IPv6, and sets the
preffered lease time for both DHCP and RA to zero. The
effect is that clients can continue to use the address
for existing connections, but new connections will use
other addresses, if they exist. This makes hitless
renumbering at least possible.
Fix bug in address6_available() which caused DHCPv6 lease
aquisition to fail if more than one dhcp-range in use.
Provide RDNSS and DNSSL data in router advertisements,
using the settings provided for DHCP options
option6:domain-search and option6:dns-server.
Tweak logo/favicon.ico to add some transparency. Thanks to
SamLT for work on this.
Don't cache data from non-recursive nameservers, since it
may erroneously look like a valid CNAME to a non-exitant
name. Thanks to Ben Winslow for finding this.
Call SO_BINDTODEVICE on the DHCP socket(s) when doing DHCP
on exactly one interface and --bind-interfaces is set. This
makes the OpenStack use-case of one dnsmasq per virtual
interface work. This is only available on Linux; it's not
supported on other platforms. Thanks to Vishvananda Ishaya
and the OpenStack team for the suggestion.
Updated French translation. Thanks to Gildas Le Nadan.
Give correct from-cache answers to explict CNAME queries.
Thanks to Rob Zwissler for spotting this.
Add --tftp-lowercase option. Thanks to Oliver Rath for the
patch.
Ensure that the DBus DhcpLeaseUpdated events are generated
when a lease goes through INIT_REBOOT state, even if the
dhcp-script is not in use. Thanks to Antoaneta-Ecaterina
Ene for the patch.
Fix failure of TFTP over IPv4 on OpenBSD platform. Thanks
to Brad Smith for spotting this.
version 2.60
Fix compilation problem in Mac OS X Lion. Thanks to Olaf
Flebbe for the patch.
Fix DHCP when using --listen-address with an IP address
which is not the primary address of an interface.
Add --dhcp-client-update option.
Add Lua integration. Dnsmasq can now execute a DHCP
lease-change script written in Lua. This needs to be
enabled at compile time by setting HAVE_LUASCRIPT in
src/config.h or running "make COPTS=-DHAVE_LUASCRIPT"
Thanks to Jan-Piet Mens for the idea and proof-of-concept
implementation.
Tidied src/config.h to distinguish between
platform-dependent compile-time options which are selected
automatically, and builder-selectable compile time
options. Document the latter better, and describe how to
set them from the make command line.
Tidied up IPPROTO_IP/SOL_IP (and IPv6 equivalent)
confusion. IPPROTO_IP works everywhere now.
Set TOS on DHCP sockets, this improves things on busy
wireless networks. Thanks to Dave Taht for the patch.
Determine VERSION automatically based on git magic:
release tags or hash values.
Improve start-up speed when reading large hosts files
containing many distinct addresses.
Fix problem if dnsmasq is started without the stdin,
stdout and stderr file descriptors open. This can manifest
itself as 100% CPU use. Thanks to Chris Moore for finding
this.
Fix shell-scripting bug in bld/pkg-wrapper. Thanks to
Mark Mitchell for the patch.
Allow the TFP server or boot server in --pxe-service, to
be a domain name instead of an IP address. This allows for
round-robin to multiple servers, in the same way as
--dhcp-boot. A good suggestion from Cristiano Cumer.
Support BUILDDIR variable in the Makefile. Allows builds
for multiple archs from the same source tree with eg.
make BUILDDIR=linux (relative to dnsmasq tree)
make BUILDDIR=/tmp/openbsd (absolute path)
If BUILDDIR is not set, compilation happens in the src
directory, as before. Suggestion from Mark Mitchell.
Support DHCPv6. Support is there for the sort of things
the existing v4 server does, including tags, options,
static addresses and relay support. Missing is prefix
delegation, which is probably not required in the dnsmasq
niche, and an easy way to accept prefix delegations from
an upstream DHCPv6 server, which is. Future plans include
support for DHCPv6 router option and MAC address option
(to make selecting clients by MAC address work like IPv4).
These will be added as the standards mature.
This code has been tested, but this is the first release,
so don't bet the farm on it just yet. Many thanks to all
testers who have got it this far.
Support IPv6 router advertisements. This is a
simple-minded implementation, aimed at providing the
vestigial RA needed to go alongside IPv6. Is picks up
configuration from the DHCPv6 conf, and should just need
enabling with --enable-ra.
Fix long-standing wrinkle with --localise-queries that
could result in wrong answers when DNS packets arrive
via an interface other than the expected one. Thanks to
Lorenzo Milesi and John Hanks for spotting this one.
Update French translation. Thanks to Gildas Le Nadan.
Update Polish translation. Thanks to Jan Psota.
version 2.59
Fix regression in 2.58 which caused failure to start up
with some combinations of dnsmasq config and IPv6 kernel
network config. Thanks to Brielle Bruns for the bug
report.
Improve dnsmasq's behaviour when network interfaces are
still doing duplicate address detection (DAD). Previously,
dnsmasq would wait up to 20 seconds at start-up for the
DAD state to terminate. This is broken for bridge
interfaces on recent Linux kernels, which don't start DAD
until the bridge comes up, and so can take arbitrary
time. The new behaviour lets dnsmasq poll for an arbitrary
time whilst providing service on other interfaces. Thanks
to Stephen Hemminger for pointing out the problem.
version 2.58
Provide a definition of the SA_SIZE macro where it's
missing. Fixes build failure on openBSD.
Don't include a zero terminator at the end of messages
sent to /dev/log when /dev/log is a datagram socket.
Thanks to Didier Rabound for spotting the problem.
Add --dhcp-sequential-ip flag, to force allocation of IP
addresses in ascending order. Note that the default
pseudo-random mode is in general better but some
server-deployment applications need this.
Fix problem where a server-id of 0.0.0.0 is sent to a
client when a dhcp-relay is in use if a client renews a
lease after dnsmasq restart and before any clients on the
subnet get a new lease. Thanks to Mike Ruiz for assistance
in chasing this one down.
Don't return NXDOMAIN to an AAAA query if we have CNAME
which points to an A record only: NODATA is the correct
reply in this case. Thanks to Tom Fernandes for spotting
the problem.
Relax the need to supply a netmask in --dhcp-range for
networks which use a DHCP relay. Whilst this is still
desireable, in the absence of a netmask dnsmasq will use
a default based on the class (A, B, or C) of the address.
This should at least remove a cause of mysterious failure
for people using RFC1918 addresses and relays.
Add support for Linux conntrack connection marking. If
enabled with --conntrack, the connection mark for incoming
DNS queries will be copied to the outgoing connections
used to answer those queries. This allows clever firewall
and accounting stuff. Only available if dnsmasq is
compiled with HAVE_CONNTRACK and adds a dependency on
libnetfilter-conntrack. Thanks to Ed Wildgoose for the
initial idea, testing and sponsorship of this function.
Provide a sane error message when someone attempts to
match a tag in --dhcp-host.
Tweak the behaviour of --domain-needed, to avoid problems
with recursive nameservers downstream of dnsmasq. The new
behaviour only stops A and AAAA queries, and returns
NODATA rather than NXDOMAIN replies.
Efficiency fix for very large DHCP configurations, thanks
to James Gartrell and Mike Ruiz for help with this.
Allow the TFTP-server address in --dhcp-boot to be a
domain-name which is looked up in /etc/hosts. This can
give multiple IP addresses which are used round-robin,
thus doing TFTP server load-balancing. Thanks to Sushil
Agrawal for the patch.
When two tagged dhcp-options for a particular option
number are both valid, use the one which is valid without
a tag from the dhcp-range. Allows overriding of the value
of a DHCP option for a particular host as well as
per-network values. So
--dhcp-range=set:interface1,......
--dhcp-host=set:myhost,.....
--dhcp-option=tag:interface1,option:nis-domain,"domain1"
--dhcp-option=tag:myhost,option:nis-domain,"domain2"
will set the NIS-domain to domain1 for hosts in the range, but
override that to domain2 for a particular host.
Fix bug which resulted in truncated files and timeouts for
some TFTP transfers. The bug only occurs with netascii
transfers and needs an unfortunate relationship between
file size, blocksize and the number of newlines in the
last block before it manifests itself. Many thanks to
Alkis Georgopoulos for spotting the problem and providing
a comprehensive test-case.
Fix regression in TFTP server on *BSD platforms introduced
in version 2.56, due to confusion with sockaddr
length. Many thanks to Loïc Pefferkorn for finding this.
Support scope-ids in IPv6 addresses of nameservers from
/etc/resolv.conf and in --server options. Eg
nameserver fe80::202:a412:4512:7bbf%eth0 or
server=fe80::202:a412:4512:7bbf%eth0. Thanks to
Michael Stapelberg for the suggestion.
Update Polish translation, thanks to Jan Psota.
Update French translation. Thanks to Gildas Le Nadan.
version 2.57
Add patches to allow build under Android.
Provide our own header for the DNS protocol, rather than
relying on arpa/nameser.h. This has proved more or less
defective over the years and the final straw is that it's
effectively empty on Android.
Fix regression in 2.56 which caused hex constants in
configuration to be rejected if they contain the '*'
wildcard.
Correct wrong casts of arguments to ctype.h functions,
isdigit(), isxdigit() etc. Thanks to Matthias Andree for
spotting this.
Allow build with IDN support independently from i18n.
IDN support continues to be included automatically
when i18n is included.
'make COPTS=-DHAVE_IDN' is the magic incantation.
Modify check on extraneous command line junk (added in
2.56) so that it doesn't complain about extra _empty_
arguments. Otherwise this breaks libvirt.
version 2.56
Add a patch to allow dnsmasq to get interface names right in a
Solaris zone. Thanks to Dj Padzensky for this.
Improve data-type parsing heuristics so that
--dhcp-option=option:domain-search,.
treats the value as a string and not an IP address.
Thanks to Clemens Fischer for spotting that.
Add IPv6 support to the TFTP server. Many thanks to Jan
'RedBully' Seiffert for the patches.
Log DNS queries at level LOG_INFO, rather then
LOG_DEBUG. This makes things consistent with DHCP
logging. Thanks to Adam Pribyl for spotting the problem.
Ensure that dnsmasq terminates cleanly when using
--syslog-async even if it cannot make a connection to the
syslogd.
Add --add-mac option. This is to support currently
experimental DNS filtering facilities. Thanks to Benjamin
Petrin for the orignal patch.
Fix bug which meant that tags were ignored in dhcp-range
configuration specifying PXE-proxy service. Thanks to
Cristiano Cumer for spotting this.
Raise an error if there is extra junk, not part of an
option, on the command line.
Flag a couple of log messages in cache.c as coming from
the DHCP subsystem. Thanks to Olaf Westrik for the patch.
Omit timestamps from logs when a) logging to stderr and
b) --keep-in-forground is set. The logging facility on the
other end of stderr can be assumned to supply them. Thanks
to John Hallam for the patch.
Don't complain about strings longer than 255 characters in
--txt-record, just split the long strings into 255
character chunks instead.
Fix crash on double-free. This bug can only happen when
dhcp-script is in use and then only in rare circumstances
triggered by high DHCP transaction rate and a slow
script. Thanks to Ferenc Wagner for finding the problem.
Only log that a file has been sent by TFTP after the
transfer has completed succesfully.
A good suggestion from Ferenc Wagner: extend
the --domain option to allow this sort of thing:
--domain=thekelleys.org.uk,192.168.0.0/24,local
which automatically creates
--local=/thekelleys.org.uk/
--local=/0.168.192.in-addr.arpa/
Tighten up syntax checking of hex contants in the config
file. Thanks to Fred Damen for spotting this.
Add dnsmasq logo/icon, contributed by Justin Swift. Many
thanks for that.
Never cache DNS replies which have the 'cd' bit set, or
which result from queries forwarded with the 'cd' bit
set. The 'cd' bit instructs a DNSSEC validating server
upstream to ignore signature failures and return replies
anyway. Without this change it's possible to pollute the
dnsmasq cache with bad data by making a query with the
'cd' bit set and subsequent queries would return this data
without its being marked as suspect. Thanks to Anders
Kaseorg for pointing out this problem.
Add --proxy-dnssec flag, for compliance with RFC
4035. Dnsmasq will now clear the 'ad' bit in answers returned
from upstream validating nameservers unless this option is
set.
Allow a filename of "-" for --conf-file to read
stdin. Suggestion from Timothy Redaelli.
Rotate the order of SRV records in replies, to provide
round-robin load balancing when all the priorities are
equal. Thanks to Peter McKinney for the suggestion.
Edit
contrib/MacOSX-launchd/uk.org.thekelleys.dnsmasq.plist
so that it doesn't log all queries to a file by
default. Thanks again to Peter McKinney.
By default, setting an IPv4 address for a domain but not
an IPv6 address causes dnsmasq to return
an NODATA reply for IPv6 (or vice-versa). So
--address=/google.com/1.2.3.4 stops IPv6 queries for
*google.com from being forwarded. Make it possible to
override this behaviour by defining the sematics if the
same domain appears in both --server and --address.
In that case, the --address has priority for the address
family in which is appears, but the --server has priority
of the address family which doesn't appear in --adddress
So:
--address=/google.com/1.2.3.4
--server=/google.com/#
will return 1.2.3.4 for IPv4 queries for *.google.com but
forward IPv6 queries to the normal upstream nameserver.
Similarly when setting an IPv6 address
only this will allow forwarding of IPv4 queries. Thanks to
William for pointing out the need for this.
Allow more than one --dhcp-optsfile and --dhcp-hostsfile
and make them understand directories as arguments in the
same way as --addn-hosts. Suggestion from John Hanks.
Ignore rebinding requests for leases we don't know
about. Rebind is broadcast, so we might get to overhear a
request meant for another DHCP server. NAKing this is
wrong. Thanks to Brad D'Hondt for assistance with this.
Fix cosmetic bug which produced strange output when
dumping cache statistics with some configurations. Thanks
to Fedor Kozhevnikov for spotting this.
version 2.55
Fix crash when /etc/ethers is in use. Thanks to
Gianluigi Tiesi for finding this.
Fix crash when /etc/ethers is in use. Thanks to
Gianluigi Tiesi for finding this.
Fix crash in netlink_multicast(). Thanks to Arno Wald for
finding this one.
Fix crash in netlink_multicast(). Thanks to Arno Wald for
finding this one.
Allow the empty domain "." in dhcp domain-search (119)
options.
Allow the empty domain "." in dhcp domain-search (119)
options.
version 2.54
There is no version 2.54 to avoid confusion with 2.53,
which incorrectly identifies itself as 2.54.
There is no version 2.54 to avoid confusion with 2.53,
which incorrectly identifies itself as 2.54.
version 2.53

128
FAQ
View File

@@ -236,53 +236,70 @@ Q: What network types are supported by the DHCP server?
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
Linux all network types (including FireWire) are supported.
Q: What is this strange "bind-interface" option?
Q: What are these strange "bind-interface" and "bind-dynamic" options?
A: The DNS spec says that the reply to a DNS query must come from the
same address it was sent to. The traditional way to write an UDP
server to do this is to find all of the addresses belonging to the
machine (ie all the interfaces on the machine) and then create a
socket for each interface which is bound to the address of the
interface. Then when a packet is sent to address A, it is received
on the socket bound to address A and when the reply is also sent
via that socket, the source address is set to A by the kernel and
everything works. This is the how dnsmasq works when
"bind-interfaces" is set, with the obvious extension that is misses
out creating sockets for some interfaces depending on the
--interface, --address and --except-interface flags. The
disadvantage of this approach is that it breaks if interfaces don't
exist or are not configured when the daemon starts and does the
socket creation step. In a hotplug-aware world this is a real
problem.
A: Dnsmasq from v2.63 can operate in one of three different "networking
modes". This is unfortunate as it requires users configuring dnsmasq
to take into account some rather bizzare contraints and select the
mode which best fits the requirements of a particular installation.
The origin of these are deficiencies in the Unix networking
model and APIs and each mode has different advantages and
problems. Just to add to the confusion, not all modes are available on
all platforms (due the to lack of supporting network APIs).To further
add to the confusion, the rules for the DHCP subsystem on dnsmasq are
different to the rules for the DNS and TFTP subsystems.
The alternative approach is to have only one socket, which is bound
to the correct port and the wildcard IP address (0.0.0.0). That
socket will receive _all_ packets sent to port 53, no matter what
destination address they have. This solves the problem of
interfaces which are created or reconfigured after daemon
start-up. To make this work is more complicated because of the
"reply source address" problem. When a UDP packet is sent by a
socket bound to 0.0.0.0 its source address will be set to the
address of one of the machine's interfaces, but which one is not
determined and can vary depending on the OS being run. To get round
this it is neccessary to use a scary advanced API to determine the
address to which a query was sent, and force that to be the source
address in the reply. For IPv4 this stuff in non-portable and quite
often not even available (It's different between FreeBSD 5.x and
Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
have it at all.) Hence "bind-interfaces" has to always be available
as a fall back. For IPv6 the API is standard and universally
available.
The three modes are "wildcard", "bind-interfaces" and "bind-dynamic".
It could be argued that if the --interface or --address flags are
used then binding interfaces is more appropriate, but using
wildcard binding means that dnsmasq will quite happily start up
after being told to use interfaces which don't exist, but which are
created later. Wildcard binding breaks the scenario when dnsmasq is
listening on one interface and another server (most probably BIND)
is listening on another. It's not possible for BIND to bind to an
(address,port) pair when dnsmasq has bound (wildcard,port), hence
the ability to explicitly turn off wildcard binding.
In "wildcard" mode, dnsmasq binds the wildcard IP address (0.0.0.0 or
::). This allows it to recieve all the packets sent to the server on
the relevant port. Access control (--interface, --except-interface,
--listen-address, etc) is implemented by dnsmasq: it queries the
kernel to determine the interface on which a packet was recieved and
the address to which it was sent, and applies the configured
rules. Wildcard mode is the default if neither of the other modes are
specified.
In "bind-interfaces" mode, dnsmasq runs through all the network
interfaces available when it starts, finds the set of IP addresses on
those interfaces, filters that set using the access control
configuration, and then binds the set of IP addresses. Only packets
sent to the allowed addresses are delivered by the kernel to dnsmasq.
In "bind-dynamic" mode, access control filtering is done both by
binding individual IP addresses, as for bind-interfaces, and by
inspecting individual packets on arrival as for wildcard mode. In
addition, dnsmasq notices when new interfaces appear or new addresses
appear on existing interfaces, and the resulting IP addresses are
bound automatically without having to restart dnsmasq.
The mode chosen has four different effects: co-existence with other
servers, semantics of --interface access control, effect of new
interfaces, and legality of --interface specifications for
non-existent inferfaces. We will deal with these in order.
A dnsmasq instance running in wildcard mode precludes a machine from
running a second instance of dnsmasq or any other DNS, TFTP or DHCP
server. Attempts to do so will fail with an "address in use" error.
Dnsmasq running in --bind-interfaces or bind-dynamic mode allow other
instances of dnsmasq or other servers, as long as no two servers are
configured to listen on the same interface address.
The semantics of --interface varies subtly between wildcard or
bind-dynamic mode and bind-interfaces mode. The situation where this
matters is a request which arrives via one interface (A), but with a
destination address of a second interface (B) and when dnsmasq is
configured to listen only on B. In wildcard or bind-dynamic mode, such
a request will be ignored, in bind-interfaces mode, it will be
accepted.
The creation of new network interfaces after dnsmasq starts is ignored
by dnsmasq when in --bind-interfaces mode. In wildcard or bind-dynamic
mode, such interfaces are handled normally.
A --interface specification for a non-existent interface is a fatal
error at start-up when in --bind-interfaces mode, by just generates a
warning in wildcard or bind-dynamic mode.
Q: Why doesn't Kerberos work/why can't I get sensible answers to
queries for SRV records.
@@ -303,7 +320,7 @@ A: Yes, new releases of dnsmasq are always announced through
Q: What does the dhcp-authoritative option do?
A: See http://www.isc.org/index.pl?/sw/dhcp/authoritative.php - that's
A: See http://www.isc.org/files/auth.html - that's
for the ISC daemon, but the same applies to dnsmasq.
Q: Why does my Gentoo box pause for a minute before getting a new
@@ -354,7 +371,7 @@ A: Yes, from version-2.21. The support is only available running under
If a physical interface has more than one IP address or aliases
with extra IP addresses, then any dhcp-ranges corresponding to
these addresses can be used for address allocation. So if an
interface has addresses 192.168.1.0/24 and 192.68.2.0/24 and there
interface has addresses 192.168.1.0/24 and 192.168.2.0/24 and there
are DHCP ranges 192.168.1.100-192.168.1.200 and
192.168.2.100-192.168.2.200 then both ranges would be used for host
connected to the physical interface. A more typical use might be to
@@ -381,7 +398,7 @@ A: Probably the nameserver is an authoritative nameserver for a
Q: Does the dnsmasq DHCP server probe addresses before allocating
them, as recommended in RFC2131?
A: Yes, dynmaically allocated IP addresses are checked by sending an
A: Yes, dynamically allocated IP addresses are checked by sending an
ICMP echo request (ping). If a reply is received, then dnsmasq
assumes that the address is in use, and attempts to allocate an
different address. The wait for a reply is between two and three
@@ -413,10 +430,11 @@ A: Change your kernel configuration: either deselect CONFIG_SECURITY
_or_ select CONFIG_SECURITY_CAPABILITIES. Alternatively, you can
remove the need to set capabilities by running dnsmasq as root.
Q: Where can I get .rpms Suitable for Suse?
A: Dnsmasq is in Suse itself, and the latest releases are also
available at ftp://ftp.suse.com/pub/people/ug/
Q: Where can I get .rpms Suitable for openSUSE/SLES?
A: Dnsmasq is in openSUSE itself, and the latest releases are also
available at http://download.opensuse.org/repositories/network/
Q: Can I run dnsmasq in a Linux vserver?
@@ -466,8 +484,18 @@ A: The DHCP client on windows Vista (and possibly later versions)
work).
Q: DHCP doesn't work with windows 7 but everything else is fine.
A: There seems to be a problem if Windows 7 doesn't get a value for
DHCP option 252 in DHCP packets it gets from the server. The
symtoms have beeen variously reported as continual DHCPINFORM
requests in an attempt to get an option-252, or even ignoring DHCP
offers completely (and failing to get an IP address) if there is no
option-252 supplied. DHCP option 252 is for WPAD, WWW Proxy
Auto Detection and if you don't want or need to use that, then
simplest fix seems to be to supply an empty option with:
dhcp-option=252,"\n"

129
Makefile
View File

@@ -1,4 +1,4 @@
# dnsmasq is Copyright (c) 2000-2010 Simon Kelley
# dnsmasq is Copyright (c) 2000-2012 Simon Kelley
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -13,85 +13,122 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
PREFIX = /usr/local
BINDIR = ${PREFIX}/sbin
MANDIR = ${PREFIX}/share/man
LOCALEDIR = ${PREFIX}/share/locale
# NOTE: Building the i18n targets requires GNU-make
PKG_CONFIG = pkg-config
INSTALL = install
MSGMERGE = msgmerge
MSGFMT = msgfmt
XGETTEXT = xgettext
CFLAGS = -Wall -W -O2
# Variables you may well want to override.
PREFIX = /usr/local
BINDIR = $(PREFIX)/sbin
MANDIR = $(PREFIX)/share/man
LOCALEDIR = $(PREFIX)/share/locale
BUILDDIR = $(SRC)
DESTDIR =
CFLAGS = -Wall -W -O2
LDFLAGS =
COPTS =
RPM_OPT_FLAGS =
LIBS =
#################################################################
# Variables you might want to override.
PKG_CONFIG = pkg-config
INSTALL = install
MSGMERGE = msgmerge
MSGFMT = msgfmt
XGETTEXT = xgettext
SRC = src
PO = po
PO = po
MAN = man
DNSMASQ_CFLAGS=`echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
DNSMASQ_LIBS= `echo $(COPTS) | ../bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
SUNOS_LIBS= `if uname | grep SunOS 2>&1 >/dev/null; then echo -lsocket -lnsl -lposix4; fi`
#################################################################
OBJS = cache.o rfc1035.o util.o option.o forward.o network.o \
# pmake way. (NB no spaces to keep gmake 3.82 happy)
top!=pwd
# GNU make way.
top?=$(CURDIR)
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o
all :
@cd $(SRC) && $(MAKE) \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS)" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS)" \
-f ../Makefile dnsmasq
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs)" \
-f $(top)/Makefile dnsmasq
clean :
rm -f *~ $(SRC)/*.mo contrib/*/*~ */*~ $(SRC)/*.pot
rm -f $(SRC)/*.o $(SRC)/dnsmasq.a $(SRC)/dnsmasq core */core
rm -f *~ $(BUILDDIR)/*.mo contrib/*/*~ */*~ $(BUILDDIR)/*.pot
rm -f $(BUILDDIR)/*.o $(BUILDDIR)/dnsmasq.a $(BUILDDIR)/dnsmasq core */core
install : all install-common
install-common :
$(INSTALL) -d $(DESTDIR)$(BINDIR) -d $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 644 $(MAN)/dnsmasq.8 $(DESTDIR)$(MANDIR)/man8
$(INSTALL) -m 755 $(SRC)/dnsmasq $(DESTDIR)$(BINDIR)
$(INSTALL) -m 755 $(BUILDDIR)/dnsmasq $(DESTDIR)$(BINDIR)
all-i18n :
@cd $(SRC) && $(MAKE) \
I18N=-DLOCALEDIR='\"$(LOCALEDIR)\"' \
BUILD_CFLAGS="$(DNSMASQ_CFLAGS) `$(PKG_CONFIG) --cflags libidn`" \
BUILD_LIBS="$(DNSMASQ_LIBS) $(SUNOS_LIBS) `$(PKG_CONFIG) --libs libidn`" \
-f ../Makefile dnsmasq
@cd $(PO); for f in *.po; do \
cd ../$(SRC) && $(MAKE) \
-f ../Makefile $${f%.po}.mo; \
all-i18n : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
i18n=-DLOCALEDIR=\'\"$(LOCALEDIR)\"\' \
build_cflags="$(version) $(dbus_cflags) $(ct_cflags) $(lua_cflags) `$(PKG_CONFIG) --cflags libidn`" \
build_libs="$(dbus_libs) $(ct_libs) $(lua_libs) $(sunos_libs) `$(PKG_CONFIG) --libs libidn`" \
-f $(top)/Makefile dnsmasq
for f in `cd $(PO); echo *.po`; do \
cd $(top) && cd $(BUILDDIR) && $(MAKE) top="$(top)" -f $(top)/Makefile $${f%.po}.mo; \
done
install-i18n : all-i18n install-common
cd $(SRC); ../bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
cd $(BUILDDIR); $(top)/bld/install-mo $(DESTDIR)$(LOCALEDIR) $(INSTALL)
cd $(MAN); ../bld/install-man $(DESTDIR)$(MANDIR) $(INSTALL)
merge :
@cd $(SRC) && $(MAKE) -f ../Makefile dnsmasq.pot
@cd $(PO); for f in *.po; do \
echo -n msgmerge $$f && $(MSGMERGE) --no-wrap -U $$f ../$(SRC)/dnsmasq.pot; \
merge :
@cd $(BUILDDIR) && $(MAKE) -f $(top)/Makefile dnsmasq.pot
for f in `cd $(PO); echo *.po`; do \
echo -n msgmerge $(PO)/$$f && $(MSGMERGE) --no-wrap -U $(PO)/$$f $(BUILDDIR)/dnsmasq.pot; \
done
$(BUILDDIR):
mkdir -p $(BUILDDIR)
# rules below are targets in recusive makes with cwd=$(SRC)
$(objs:.o=.c) $(hdrs):
ln -s $(top)/$(SRC)/$@ .
.c.o:
$(CC) $(CFLAGS) $(COPTS) $(I18N) $(BUILD_CFLAGS) $(RPM_OPT_FLAGS) -c $<
$(CC) $(CFLAGS) $(COPTS) $(i18n) $(build_cflags) $(RPM_OPT_FLAGS) -c $<
dnsmasq : $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(BUILD_LIBS) $(LIBS)
dnsmasq : $(hdrs) $(objs)
$(CC) $(LDFLAGS) -o $@ $(objs) $(build_libs) $(LIBS)
dnsmasq.pot : $(OBJS:.o=.c) dnsmasq.h config.h
$(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(OBJS:.o=.c)
dnsmasq.pot : $(objs:.o=.c) $(hdrs)
$(XGETTEXT) -d dnsmasq --foreign-user --omit-header --keyword=_ -o $@ -i $(objs:.o=.c)
%.mo : ../po/%.po dnsmasq.pot
$(MSGMERGE) -o - ../po/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo -
%.mo : $(top)/$(PO)/%.po dnsmasq.pot
$(MSGMERGE) -o - $(top)/$(PO)/$*.po dnsmasq.pot | $(MSGFMT) -o $*.mo -
.PHONY : all clean install install-common all-i18n install-i18n merge

1
VERSION Normal file
View File

@@ -0,0 +1 @@
$Format:%d$

22
bld/Android.mk Normal file
View File

@@ -0,0 +1,22 @@
LOCAL_PATH := external/dnsmasq/src
#########################
include $(CLEAR_VARS)
LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
forward.c helper.c lease.c log.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
radv.c slaac.c
LOCAL_MODULE := dnsmasq
LOCAL_C_INCLUDES := external/dnsmasq/src
LOCAL_CFLAGS := -O2 -g -W -Wall -D__ANDROID__ -DNO_IPV6 -DNO_TFTP -DNO_SCRIPT
LOCAL_SYSTEM_SHARED_LIBRARIES := libc libcutils
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
include $(BUILD_EXECUTABLE)

30
bld/get-version Executable file
View File

@@ -0,0 +1,30 @@
#!/bin/sh
# Determine the version string to build into a binary.
# When building in the git repository, we can use the output
# of "git describe" which gives an unequivocal answer.
#
# Failing that, we use the contents of the VERSION file
# which has a set of references substituted into it by git.
# If we can find one which matches $v[0-9].* then we assume it's
# a version-number tag, else we just use the whole string.
# If there is more than one v[0-9].* tag, sort them and use the
# first. This favours, eg v2.63 over 2.63rc6.
if which git >/dev/null 2>&1 && [ -d $1/.git ]; then
cd $1; git describe
elif grep '\$Format:%d\$' $1/VERSION >/dev/null 2>&1; then
# unsubstituted VERSION, but no git available.
echo UNKNOWN
else
vers=`cat $1/VERSION | sed 's/[(), ]/,/ g' | tr ',' '\n' | grep $v[0-9]`
if [ $? -eq 0 ]; then
echo "${vers}" | sort | head -n 1 | sed 's/^v//'
else
cat $1/VERSION
fi
fi
exit 0

View File

@@ -4,6 +4,6 @@ for f in *; do
if [ -d $f ]; then
$2 -m 755 -d $1/$f/man8
$2 -m 644 $f/dnsmasq.8 $1/$f/man8
echo installing $1/$f/man8/dnsmasq.8
echo installing $f/man8/dnsmasq.8
fi
done

View File

@@ -3,7 +3,7 @@
for f in *.mo; do
$2 -m 755 -d $1/${f%.mo}/LC_MESSAGES
$2 -m 644 $f $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
echo installing $1/${f%.mo}/LC_MESSAGES/dnsmasq.mo
echo installing ${f%.mo}/LC_MESSAGES/dnsmasq.mo
done

View File

@@ -3,8 +3,8 @@
search=$1
shift
if grep "^\#.*define.*$search" config.h 2>&1 >/dev/null || \
grep $search 2>&1 >/dev/null ; then
if grep "^\#[[:space:]]*define[[:space:]]*$search" config.h >/dev/null 2>&1 || \
grep $search >/dev/null 2>&1; then
exec $*
fi

View File

@@ -8,8 +8,6 @@
<array>
<string>/usr/local/sbin/dnsmasq</string>
<string>--keep-in-foreground</string>
<string>--log-queries</string>
<string>--log-facility=/var/log/dnsmasq.log</string>
</array>
<key>RunAtLoad</key>
<true/>

54
contrib/conntrack/README Normal file
View File

@@ -0,0 +1,54 @@
Linux iptables includes that ability to mark individual network packets
with a "firewall mark". Additionally there is a component called
"conntrack" which tries to string sequences of related packets together
into a "connection" (it even relates sequences of UDP and ICMP packets).
There is a related mark for a connection called a "connection mark".
Marks can be copied freely between the firewall and connection marks
Using these two features it become possible to tag all related traffic
in arbitrary ways, eg authenticated users, traffic from a particular IP,
port, etc. Unfortunately any kind of "proxy" breaks this relationship
because network packets go in one side of the proxy and a completely new
connection comes out of the other side. However, sometimes, we want to
maintain that relationship through the proxy and continue the connection
mark on packets upstream of our proxy
DNSMasq includes such a feature enabled by the --conntrack
option. This allows, for example, using iptables to mark traffic from
a particular IP, and that mark to be persisted to requests made *by*
DNSMasq. Such a feature could be useful for bandwidth accounting,
captive portals and the like. Note a similar feature has been
implemented in Squid 2.2
As an example consider the following iptables rules:
1) iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
2) iptables -t mangle -A PREROUTING -m mark --mark 0 -s 192.168.111.137
-j MARK --set-mark 137
3) iptables -t mangle -A PREROUTING -j CONNMARK --save-mark
4) iptables -t mangle -A OUTPUT -m mark ! --mark 0 -j CONNMARK --save-mark
1-3) are all applied to the PREROUTING table and affect all packets
entering the firewall.
1) copies any existing connection mark into the firewall mark. 2) Checks
the packet not already marked and if not applies an arbitrary mark based
on IP address. 3) Saves the firewall mark back to the connection mark
(which will persist it across related packets)
4) is applied to the OUTPUT table, which is where we first see packets
generated locally. DNSMasq will have already copied the firewall mark
from the request, across to the new packet, and so all that remains is
for iptables to copy it to the connection mark so it's persisted across
packets.
Note: iptables can be quite confusing to the beginner. The following
diagram is extremely helpful in understanding the flows
http://linux-ip.net/nf/nfk-traversal.png
Additionally the following URL contains a useful "starting guide" on
linux connection tracking/marking
http://home.regit.org/netfilter-en/netfilter-connmark/

View File

@@ -34,11 +34,21 @@ if [ ${DNSMASQ_OLD_HOSTNAME} ] && [ ${action} = old ] ; then
hostname=${DNSMASQ_OLD_HOSTNAME}
fi
# IPv6 leases are not our concern. no NAT there!
if [ ${DNSMASQ_IAID} ] ; then
exit 0
fi
# action init is not relevant, and will only be seen when leasefile-ro is set.
if [ ${action} = init ] ; then
exit 0
fi
# action tftp is not relevant.
if [ ${action} = tftp ] ; then
exit 0
fi
if [ ${hostname} ]; then
ports=$(sed -n -e "/^${hostname}\ .*/ s/^.* //p" ${PORTSFILE})

16
contrib/systemd/README Normal file
View File

@@ -0,0 +1,16 @@
Hello,
I created a systemd service file for dnsmasq.
systemd is a sysvinit replacement (see [1] for more information).
One of the goals of systemd is to encourage standardization between different
distributions. This means, while I also submitted a ticket in Debian GNU/Linux,
I would like to ask you to accept this service file as the upstream
distributor, so that other distributions can use the same service file and
dont have to ship their own.
Please include this file in your next release (just like in init script).
[1] http://en.wikipedia.org/wiki/Systemd

View File

@@ -0,0 +1,57 @@
To: dnsmasq-discuss@lists.thekelleys.org.uk
From: Alex Elsayed <eternaleye+usenet@gmail.com>
Date: Tue, 15 May 2012 01:53:54 -0700
Subject: [Dnsmasq-discuss] [PATCH] Support dbus activation
Introduce dbus service file and turn dbus on in the systemd
unit.
Note to packagers:
To add support for dbus activation, you must install the dbus
service file (dbus/uk.org.thekelleys.dnsmasq.service) into
$DATADIR/dbus-1/system-services.
---
contrib/systemd/dnsmasq.service | 2 +-
dbus/uk.org.thekelleys.dnsmasq.service | 7 +++++++
2 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 dbus/uk.org.thekelleys.dnsmasq.service
diff --git a/contrib/systemd/dnsmasq.service
b/contrib/systemd/dnsmasq.service
index a27fe6d..4a784d3 100644
--- a/contrib/systemd/dnsmasq.service
+++ b/contrib/systemd/dnsmasq.service
@@ -5,7 +5,7 @@ Description=A lightweight DHCP and caching DNS server
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
ExecStartPre=/usr/sbin/dnsmasq --test
-ExecStart=/usr/sbin/dnsmasq -k
+ExecStart=/usr/sbin/dnsmasq -k -1
ExecReload=/bin/kill -HUP $MAINPID
[Install]
diff --git a/dbus/uk.org.thekelleys.dnsmasq.service
b/dbus/uk.org.thekelleys.dnsmasq.service
new file mode 100644
index 0000000..f5fe98d
--- /dev/null
+++ b/dbus/uk.org.thekelleys.dnsmasq.service
@@ -0,0 +1,7 @@
+[D-BUS Service]
+Name=uk.org.thekelleys.dnsmasq
+Exec=/usr/sbin/dnsmasq -k -1
+User=root
+SystemdService=dnsmasq.service
+
+
--
1.7.10.2
_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

View File

@@ -0,0 +1,12 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
ExecStartPre=/usr/sbin/dnsmasq --test
ExecStart=/usr/sbin/dnsmasq -k
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,23 @@
.TH DHCP_LEASE_TIME 1
.SH NAME
dhcp_lease_time \- Query remaining time of a lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_lease_time <address>
.SH "DESCRIPTION"
Send a DHCPINFORM message to a dnsmasq server running on the local host
and print (to stdout) the time remaining in any lease for the given
address. The time is given as string printed to stdout.
If an error occurs or no lease exists for the given address,
nothing is sent to stdout a message is sent to stderr and a
non-zero error code is returned.
Requires dnsmasq 2.40 or later and may not work with other DHCP servers.
The address argument is a dotted-quad IP addresses and mandatory.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -0,0 +1,35 @@
.TH DHCP_RELEASE 1
.SH NAME
dhcp_release \- Release a DHCP lease on a the local dnsmasq DHCP server.
.SH SYNOPSIS
.B dhcp_release <interface> <address> <MAC address> <client_id>
.SH "DESCRIPTION"
A utility which forces the DHCP server running on this machine to release a
DHCP lease.
.PP
Send a DHCPRELEASE message via the specified interface to tell the
local DHCP server to delete a particular lease.
The interface argument is the interface in which a DHCP
request _would_ be received if it was coming from the client,
rather than being faked up here.
The address argument is a dotted-quad IP addresses and mandatory.
The MAC address is colon separated hex, and is mandatory. It may be
prefixed by an address-type byte followed by -, eg
10-11:22:33:44:55:66
but if the address-type byte is missing it is assumed to be 1, the type
for ethernet. This encoding is the one used in dnsmasq lease files.
The client-id is optional. If it is "*" then it treated as being missing.
.SH NOTES
MUST be run as root - will fail otherwise.
.SH SEE ALSO
.BR dnsmasq (8)
.SH AUTHOR
This manual page was written by Simon Kelley <simon@thekelleys.org.uk>.

View File

@@ -178,7 +178,7 @@ static int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
static struct in_addr find_interface(struct in_addr client, int fd, int index)
static struct in_addr find_interface(struct in_addr client, int fd, unsigned int index)
{
struct sockaddr_nl addr;
struct nlmsghdr *h;

View File

@@ -19,7 +19,8 @@ and avoids startup races with the provider of nameserver information.
Dnsmasq provides one service on the DBus: uk.org.thekelleys.dnsmasq
and a single object: /uk/org/thekelleys/dnsmasq
and a single object: /uk/org/thekelleys/dnsmasq
The name of the service may be changed by giving an argument to --enable-dbus.
1. METHODS
----------

1076
debian/changelog vendored Normal file

File diff suppressed because it is too large Load Diff

5
debian/conffiles vendored Normal file
View File

@@ -0,0 +1,5 @@
/etc/init.d/dnsmasq
/etc/default/dnsmasq
/etc/dnsmasq.conf
/etc/resolvconf/update.d/dnsmasq
/etc/insserv.conf.d/dnsmasq

42
debian/control vendored Normal file
View File

@@ -0,0 +1,42 @@
Source: dnsmasq
Section: net
Priority: optional
Build-depends: gettext, libnetfilter-conntrack-dev [linux-any], libidn11-dev, libdbus-1-dev (>=0.61)
Maintainer: Simon Kelley <simon@thekelleys.org.uk>
Standards-Version: 3.9.3
Package: dnsmasq
Architecture: all
Depends: netbase, dnsmasq-base(>= ${source:Version})
Suggests: resolvconf
Conflicts: resolvconf (<<1.15)
Description: Small caching DNS proxy and DHCP/TFTP server
Dnsmasq is a lightweight, easy to configure, DNS forwarder and DHCP
server. It is designed to provide DNS and optionally, DHCP, to a
small network. It can serve the names of local machines which are
not in the global DNS. The DHCP server integrates with the DNS
server and allows machines with DHCP-allocated addresses
to appear in the DNS with names configured either in each host or
in a central configuration file. Dnsmasq supports static and dynamic
DHCP leases and BOOTP/TFTP for network booting of diskless machines.
Package: dnsmasq-base
Architecture: any
Depends: adduser, ${shlibs:Depends}
Breaks: dnsmasq (<< 2.63-1~)
Replaces: dnsmasq (<< 2.63-1~)
Description: Small caching DNS proxy and DHCP/TFTP server
This package contains the dnsmasq executable and documentation, but
not the infrastructure required to run it as a system daemon. For
that, install the dnsmasq package.
Package: dnsmasq-utils
Architecture: linux-any
Depends: ${shlibs:Depends}
Conflicts: dnsmasq (<<2.40)
Description: Utilities for manipulating DHCP leases
Small utilities to query a DHCP server's lease database and
remove leases from it. These programs are distributed with dnsmasq
and may not work correctly with other DHCP servers.

21
debian/copyright vendored Normal file
View File

@@ -0,0 +1,21 @@
dnsmasq is Copyright (c) 2000-2012 Simon Kelley
It was downloaded from: http://www.thekelleys.org.uk/dnsmasq/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
On Debian GNU/Linux systems, the text of the GNU general public license is
available in the file /usr/share/common-licenses/GPL-2 or
/usr/share/common-licenses/GPL-3
The Debian package of dnsmasq was created by Simon Kelley with assistance
from Lars Bahner.

18
debian/dbus.conf vendored Normal file
View File

@@ -0,0 +1,18 @@
<!DOCTYPE busconfig PUBLIC
"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy user="root">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy user="dnsmasq">
<allow own="uk.org.thekelleys.dnsmasq"/>
<allow send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
<policy context="default">
<deny own="uk.org.thekelleys.dnsmasq"/>
<deny send_destination="uk.org.thekelleys.dnsmasq"/>
</policy>
</busconfig>

33
debian/default vendored Normal file
View File

@@ -0,0 +1,33 @@
# This file has five functions:
# 1) to completely disable starting dnsmasq,
# 2) to set DOMAIN_SUFFIX by running `dnsdomainname`
# 3) to select an alternative config file
# by setting DNSMASQ_OPTS to --conf-file=<file>
# 4) to tell dnsmasq to read the files in /etc/dnsmasq.d for
# more configuration variables.
# 5) to stop the resolvconf package from controlling dnsmasq's
# idea of which upstream nameservers to use.
# For upgraders from very old versions, all the shell variables set
# here in previous versions are still honored by the init script
# so if you just keep your old version of this file nothing will break.
#DOMAIN_SUFFIX=`dnsdomainname`
#DNSMASQ_OPTS="--conf-file=/etc/dnsmasq.alt"
# Whether or not to run the dnsmasq daemon; set to 0 to disable.
ENABLED=1
# By default search this drop directory for configuration options.
# Libvirt leaves a file here to make the system dnsmasq play nice.
# Comment out this line if you don't want this. The dpkg-* are file
# endings which cause dnsmasq to skip that file. This avoids pulling
# in backups made by dpkg.
CONFIG_DIR=/etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new
# If the resolvconf package is installed, dnsmasq will use its output
# rather than the contents of /etc/resolv.conf to find upstream
# nameservers. Uncommenting this line inhibits this behaviour.
# Not that including a "resolv-file=<filename>" line in
# /etc/dnsmasq.conf is not enough to override resolvconf if it is
# installed: the line below must be uncommented.
#IGNORE_RESOLVCONF=yes

1
debian/dnsmasq-base.conffiles vendored Normal file
View File

@@ -0,0 +1 @@
/etc/dbus-1/system.d/dnsmasq.conf

24
debian/dnsmasq-base.postinst vendored Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
set -e
# Create the dnsmasq user in dnsmasq-base, so that Dbus doesn't complain.
# create a user to run as (code stolen from dovecot-common)
if [ "$1" = "configure" ]; then
if [ -z "`id -u dnsmasq 2> /dev/null`" ]; then
adduser --system --home /var/lib/misc --gecos "dnsmasq" \
--no-create-home --disabled-password \
--quiet dnsmasq || true
fi
# Make the directory where we keep the pid file - this
# has to be owned by "dnsmasq" so that the file can be unlinked.
# This is only actually used by the dnsmasq binary package, not
# dnsmasq-base, but it's much easier to create it here so that
# we don't have synchronisation issues with the creation of the
# dnsmasq user.
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq
chown dnsmasq:nogroup /var/run/dnsmasq
fi
fi

11
debian/dnsmasq-base.postrm vendored Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -e
if [ purge = "$1" ]; then
if [ -x "$(command -v deluser)" ]; then
deluser --quiet --system dnsmasq > /dev/null || true
else
echo >&2 "not removing dnsmasq system account because deluser command was not found"
fi
rm -rf /var/run/dnsmasq
fi

290
debian/init vendored Normal file
View File

@@ -0,0 +1,290 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: dnsmasq
# Required-Start: $network $remote_fs $syslog
# Required-Stop: $network $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: DHCP and DNS server
### END INIT INFO
set +e # Don't exit on error status
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/dnsmasq
NAME=dnsmasq
DESC="DNS forwarder and DHCP server"
# Most configuration options in /etc/default/dnsmasq are deprecated
# but still honoured.
ENABLED=1
if [ -r /etc/default/$NAME ]; then
. /etc/default/$NAME
fi
# Get the system locale, so that messages are in the correct language, and the
# charset for IDN is correct
if [ -r /etc/default/locale ]; then
. /etc/default/locale
export LANG
fi
test -x $DAEMON || exit 0
# Provide skeleton LSB log functions for backports which don't have LSB functions.
if [ -f /lib/lsb/init-functions ]; then
. /lib/lsb/init-functions
else
log_warning_msg () {
echo "${@}."
}
log_success_msg () {
echo "${@}."
}
log_daemon_msg () {
echo -n "${1}: $2"
}
log_end_msg () {
if [ $1 -eq 0 ]; then
echo "."
elif [ $1 -eq 255 ]; then
/bin/echo -e " (warning)."
else
/bin/echo -e " failed!"
fi
}
fi
# RESOLV_CONF:
# If the resolvconf package is installed then use the resolv conf file
# that it provides as the default. Otherwise use /etc/resolv.conf as
# the default.
#
# If IGNORE_RESOLVCONF is set in /etc/default/dnsmasq or an explicit
# filename is set there then this inhibits the use of the resolvconf-provided
# information.
#
# Note that if the resolvconf package is installed it is not possible to
# override it just by configuration in /etc/dnsmasq.conf, it is necessary
# to set IGNORE_RESOLVCONF=yes in /etc/default/dnsmasq.
if [ ! "$RESOLV_CONF" ] &&
[ "$IGNORE_RESOLVCONF" != "yes" ] &&
[ -x /sbin/resolvconf ]
then
RESOLV_CONF=/var/run/dnsmasq/resolv.conf
fi
for INTERFACE in $DNSMASQ_INTERFACE; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -i $INTERFACE"
done
for INTERFACE in $DNSMASQ_EXCEPT; do
DNSMASQ_INTERFACES="$DNSMASQ_INTERFACES -I $INTERFACE"
done
if [ ! "$DNSMASQ_USER" ]; then
DNSMASQ_USER="dnsmasq"
fi
start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
# /var/run may be volatile, so we need to ensure that
# /var/run/dnsmasq exists here as well as in postinst
if [ ! -d /var/run/dnsmasq ]; then
mkdir /var/run/dnsmasq || return 2
chown dnsmasq:nogroup /var/run/dnsmasq || return 2
fi
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null || return 1
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON -- \
-x /var/run/dnsmasq/$NAME.pid \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
${DNSMASQ_INTERFACES:+ $DNSMASQ_INTERFACES} \
${DHCP_LEASE:+ -l $DHCP_LEASE} \
${DOMAIN_SUFFIX:+ -s $DOMAIN_SUFFIX} \
${RESOLV_CONF:+ -r $RESOLV_CONF} \
${CACHESIZE:+ -c $CACHESIZE} \
${CONFIG_DIR:+ -7 $CONFIG_DIR} \
${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} \
|| return 2
}
start_resolvconf()
{
# If interface "lo" is explicitly disabled in /etc/default/dnsmasq
# Then dnsmasq won't be providing local DNS, so don't add it to
# the resolvconf server set.
for interface in $DNSMASQ_EXCEPT
do
[ $interface = lo ] && return
done
if [ -x /sbin/resolvconf ] ; then
echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.$NAME
fi
return 0
}
stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile /var/run/dnsmasq/$NAME.pid --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
return "$RETVAL"
}
stop_resolvconf()
{
if [ -x /sbin/resolvconf ] ; then
/sbin/resolvconf -d lo.$NAME
fi
return 0
}
status()
{
# Return
# 0 if daemon is running
# 1 if daemon is dead and pid file exists
# 3 if daemon is not running
# 4 if daemon status is unknown
start-stop-daemon --start --quiet --pidfile /var/run/dnsmasq/$NAME.pid --exec $DAEMON --test > /dev/null
case "$?" in
0) [ -e "/var/run/dnsmasq/$NAME.pid" ] && return 1 ; return 3 ;;
1) return 0 ;;
*) return 4 ;;
esac
}
case "$1" in
start)
test "$ENABLED" != "0" || exit 0
log_daemon_msg "Starting $DESC" "$NAME"
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
1)
log_success_msg "(already running)"
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
stop)
stop_resolvconf
if [ "$ENABLED" != "0" ]; then
log_daemon_msg "Stopping $DESC" "$NAME"
fi
stop
RETVAL="$?"
if [ "$ENABLED" = "0" ]; then
case "$RETVAL" in
0) log_daemon_msg "Stopping $DESC" "$NAME"; log_end_msg 0 ;;
esac
exit 0
fi
case "$RETVAL" in
0) log_end_msg 0 ; exit 0 ;;
1) log_warning_msg "(not running)" ; exit 0 ;;
*) log_end_msg 1; exit 1 ;;
esac
;;
restart|force-reload)
test "$ENABLED" != "0" || exit 1
$DAEMON --test ${CONFIG_DIR:+ -7 $CONFIG_DIR} ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} >/dev/null 2>&1
if [ $? -ne 0 ]; then
NAME="configuration syntax check"
RETVAL="2"
else
stop_resolvconf
stop
RETVAL="$?"
fi
log_daemon_msg "Restarting $DESC" "$NAME"
case "$RETVAL" in
0|1)
sleep 2
start
case "$?" in
0)
log_end_msg 0
start_resolvconf
exit 0
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
*)
log_end_msg 1
exit 1
;;
esac
;;
status)
log_daemon_msg "Checking $DESC" "$NAME"
status
case "$?" in
0) log_success_msg "(running)" ; exit 0 ;;
1) log_success_msg "(dead, pid file exists)" ; exit 1 ;;
3) log_success_msg "(not running)" ; exit 3 ;;
*) log_success_msg "(unknown)" ; exit 4 ;;
esac
;;
dump-stats)
kill -s USR1 `cat /var/run/dnsmasq/$NAME.pid`
;;
systemd-start-resolvconf)
start_resolvconf
;;
systemd-stop-resolvconf)
stop_resolvconf
;;
systemd-exec)
# --pid-file without argument disables writing a PIDfile, we don't need one with sytemd.
# Enable DBus by default because we use DBus activation with systemd.
exec $DAEMON --keep-in-foreground --pid-file --enable-dbus \
${MAILHOSTNAME:+ -m $MAILHOSTNAME} \
${MAILTARGET:+ -t $MAILTARGET} \
${DNSMASQ_USER:+ -u $DNSMASQ_USER} \
${DNSMASQ_INTERFACES:+ $DNSMASQ_INTERFACES} \
${DHCP_LEASE:+ -l $DHCP_LEASE} \
${DOMAIN_SUFFIX:+ -s $DOMAIN_SUFFIX} \
${RESOLV_CONF:+ -r $RESOLV_CONF} \
${CACHESIZE:+ -c $CACHESIZE} \
${CONFIG_DIR:+ -7 $CONFIG_DIR} \
${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload|dump-stats|status}" >&2
exit 3
;;
esac
exit 0

1
debian/insserv vendored Normal file
View File

@@ -0,0 +1 @@
$named dnsmasq

22
debian/postinst vendored Normal file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
set -e
if [ -x /etc/init.d/dnsmasq ]; then
update-rc.d dnsmasq defaults 15 85 >/dev/null
if [ "$1" = "configure" ] || [ "$1" = "abort-upgrade" ]; then
if [ -e /var/run/dnsmasq/dnsmasq.pid ]; then
ACTION=restart
else
ACTION=start
fi
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq $ACTION || true
else
/etc/init.d/dnsmasq $ACTION || true
fi
fi
fi

6
debian/postrm vendored Normal file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
set -e
if [ purge = "$1" ]; then
update-rc.d dnsmasq remove >/dev/null
fi

14
debian/prerm vendored Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ "$1" = "remove" ]; then
if [ -x /usr/sbin/invoke-rc.d ] ; then
invoke-rc.d dnsmasq stop || true
else
/etc/init.d/dnsmasq stop || true
fi
fi
exit 0

76
debian/readme vendored Normal file
View File

@@ -0,0 +1,76 @@
Notes on configuring dnsmasq as packaged for Debian.
(1) To configure dnsmasq edit /etc/dnsmasq.conf. The file is well
commented; see also the dnsmasq.8 man page for explanation of
the options. The file /etc/default/dnsmasq also exists but it
shouldn't need to be touched in most cases. To set up DHCP
options you might need to refer to a copy of RFC 2132. This is
available on Debian systems in the package doc-rfc-std as the file
/usr/share/doc/RFC/draft-standard/rfc2132.txt.gz .
(2) Installing the dnsmasq package also creates the directory
/etc/dnsmasq.d which is searched by dnsmasq for configuration file
fragments. This behaviour can be disabled by editing
/etc/default/dnsmasq.
(3) If the Debian resolvconf package is installed then, regardless
of what interface configuration daemons are employed, the list of
nameservers to which dnsmasq should forward queries can be found
in /var/run/dnsmasq/resolv.conf; also, 127.0.0.1 is listed as the
first nameserver address in /etc/resolv.conf. This works using the
default configurations of resolvconf and dnsmasq.
(4) In the absence of resolvconf, if you are using dhcpcd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/dhcpc/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(5) In the absence of resolvconf, if you are using pppd then
dnsmasq should read the list of nameservers from the automatically
generated file /etc/ppp/resolv.conf. You should list 127.0.0.1
as the first nameserver address in /etc/resolv.conf.
(6) In the absence of resolvconf, dns-nameservers lines in
/etc/network/interfaces are ignored. If you do do not use
resolvconf, list 127.0.0.1 as the first nameserver address
in /etc/resolv.conf and configure your nameservers using
"server=<IP-address>" lines in /etc/dnsmasq.conf.
(7) If you run multiple DNS servers on a single machine, each
listening on a different interface, then it is necessary to use
the bind-interfaces option by uncommenting "bind-interfaces" in
/etc/dnsmasq.conf. This option stops dnsmasq from binding the
wildcard address and allows servers listening on port 53 on
interfaces not in use by dnsmasq to work. The Debian
libvirt package will add a configuration file in /etc/dnsmasq.d
which does this so that the "system" dnsmasq and "private" dnsmasq
instances started by libvirt do not clash.
(8) The following options are supported in DEB_BUILD_OPTIONS
noopt : compile without optimisation.
nostrip : don't remove symbols from binary.
nodocs : omit documentation.
notftp : omit TFTP support.
nodhcp : omit DHCP support.
nodhcp6 : omit DHCPv6 support.
noscript : omit lease-change script support.
use_lua : provide support for lease-change scripts written
in Lua.
noipv6 : omit IPv6 support.
nodbus : omit DBus support.
noconntrack : omit connection tracking support.
nortc : compile alternate mode suitable for systems without an RTC.
noi18n : omit translations and internationalisation support.
noidn : omit international domain name support, must be
combined with noi18n to be effective.
(9) Dnsmasq comes as three packages - dnsmasq-utils, dnsmasq-base and
dnsmasq. Dnsmasq-base provides the dnsmasq executable and
documentation (including this file). Dnsmasq, which depends on
dnsmasq-base, provides the init script and configuration
infrastructure. This file assumes that both are installed. It is
possible to install only dnsmasq-base and use dnsmasq as a
non-"system" daemon. Libvirt, for instance, does this.
Dnsmasq-utils provides the utilities dhcp_release and
dhcp_lease_time.

7
debian/readme.dnsmasq.d vendored Normal file
View File

@@ -0,0 +1,7 @@
# All files in this directory will be read by dnsmasq as
# configuration files, except if their names end in
# ".dpkg-dist",".dpkg-old" or ".dpkg-new"
#
# This can be changed by editing /etc/default/dnsmasq

70
debian/resolvconf vendored Normal file
View File

@@ -0,0 +1,70 @@
#!/bin/bash
#
# Script to update the resolver list for dnsmasq
#
# N.B. Resolvconf may run us even if dnsmasq is not running.
# If dnsmasq is installed then we go ahead and update
# the resolver list in case dnsmasq is started later.
#
# Assumption: On entry, PWD contains the resolv.conf-type files
#
# Requires bash because it uses a non-POSIX printf extension.
#
# Licensed under the GNU GPL. See /usr/share/common-licenses/GPL.
#
set -e
RUN_DIR="/var/run/dnsmasq"
RSLVRLIST_FILE="${RUN_DIR}/resolv.conf"
TMP_FILE="${RSLVRLIST_FILE}_new.$$"
[ -x /usr/sbin/dnsmasq ] || exit 0
[ -x /lib/resolvconf/list-records ] || exit 1
PATH=/bin:/sbin
report_err() { echo "$0: Error: $*" >&2 ; }
# Stores arguments (minus duplicates) in RSLT, separated by spaces
# Doesn't work properly if an argument itself contain whitespace
uniquify()
{
RSLT=""
while [ "$1" ] ; do
for E in $RSLT ; do
[ "$1" = "$E" ] && { shift ; continue 2 ; }
done
RSLT="${RSLT:+$RSLT }$1"
shift
done
}
if [ ! -d "$RUN_DIR" ] && ! mkdir --parents --mode=0755 "$RUN_DIR" ; then
report_err "Failed trying to create directory $RUN_DIR"
exit 1
fi
RSLVCNFFILES="$(/lib/resolvconf/list-records | sed -e '/^lo.dnsmasq$/d')"
NMSRVRS=""
if [ "$RSLVCNFFILES" ] ; then
uniquify $(sed -n -e 's/^[[:space:]]*nameserver[[:space:]]\+//p' $RSLVCNFFILES)
NMSRVRS="$RSLT"
fi
# Dnsmasq uses the mtime of $RSLVRLIST_FILE, with a resolution of one second,
# to detect changes in the file. This means that if a resolvconf update occurs
# within one second of the previous one then dnsmasq may fail to notice the
# more recent change. To work around this problem we sleep here to ensure
# that the new mtime is different.
if [ -f "$RSLVRLIST_FILE" ] && [ "$(ls -go --time-style='+%s' "$RSLVRLIST_FILE" | { read p h s t n ; echo "$t" ; })" = "$(date +%s)" ] ; then
sleep 1
fi
clean_up() { rm -f "$TMP_FILE" ; }
trap clean_up EXIT
: >| "$TMP_FILE"
for N in $NMSRVRS ; do echo "nameserver $N" >> "$TMP_FILE" ; done
mv -f "$TMP_FILE" "$RSLVRLIST_FILE"

13
debian/resolvconf-package vendored Normal file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Resolvconf packaging event hook script for the dnsmasq package
restart_dnsmasq() {
if which invoke-rc.d >/dev/null 2>&1 ; then
invoke-rc.d dnsmasq restart
elif [ -x /etc/init.d/dnsmasq ] ; then
/etc/init.d/dnsmasq restart
fi
}
case "$1" in
install) restart_dnsmasq ;;
esac

200
debian/rules vendored Executable file
View File

@@ -0,0 +1,200 @@
#!/usr/bin/make -f
# debian/rules file - for dnsmasq.
# Copyright 2001-2011 by Simon Kelley
# Based on the sample in the debian hello package which carries the following:
# Copyright 1994,1995 by Ian Jackson.
# I hereby give you perpetual unlimited permission to copy,
# modify and relicense this file, provided that you do not remove
# my name from the file itself. (I assert my moral right of
# paternity under the Copyright, Designs and Patents Act 1988.)
# This file may have to be extensively modified
package=dnsmasq-base
CFLAGS = $(shell export DEB_BUILD_OPTIONS=$(DEB_BUILD_OPTIONS); dpkg-buildflags --get CFLAGS)
CFLAGS += $(shell dpkg-buildflags --get CPPFLAGS)
CFLAGS += -Wall -W
LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
COPTS =
TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
ifeq (,$(filter nodbus,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_DBUS
endif
ifeq (,$(filter noconntrack,$(DEB_BUILD_OPTIONS)))
ifeq ($(DEB_BUILD_ARCH_OS),linux)
COPTS += -DHAVE_CONNTRACK
endif
endif
ifneq (,$(filter nodhcp6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP6
endif
ifneq (,$(filter noipv6,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_IPV6
endif
ifneq (,$(filter notftp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_TFTP
endif
ifneq (,$(filter nodhcp,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_DHCP
endif
ifneq (,$(filter noscript,$(DEB_BUILD_OPTIONS)))
COPTS += -DNO_SCRIPT
endif
ifneq (,$(filter nortc,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_BROKEN_RTC
endif
ifneq (,$(filter noi18n,$(DEB_BUILD_OPTIONS)))
TARGET = install
ifeq (,$(filter noidn, $(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_IDN
endif
endif
ifneq (,$(filter uselua,$(DEB_BUILD_OPTIONS)))
COPTS += -DHAVE_LUASCRIPT
endif
clean:
$(checkdir)
rm -rf debian/daemon debian/base debian/utils debian/*~ debian/files debian/substvars debian/utils-substvars
make clean
make -C contrib/wrt clean
binary-indep: checkroot
$(checkdir)
rm -rf debian/daemon
install -m 755 \
-d debian/daemon/DEBIAN \
-d debian/daemon/usr/share/doc \
-d debian/daemon/etc/init.d \
-d debian/daemon/etc/dnsmasq.d \
-d debian/daemon/etc/resolvconf/update.d \
-d debian/daemon/usr/lib/resolvconf/dpkg-event.d \
-d debian/daemon/etc/default \
-d debian/daemon/lib/systemd/system \
-d debian/daemon/etc/insserv.conf.d
install -m 644 debian/conffiles debian/daemon/DEBIAN
install -m 755 debian/postinst debian/postrm debian/prerm debian/daemon/DEBIAN
install -m 755 debian/init debian/daemon/etc/init.d/dnsmasq
install -m 755 debian/resolvconf debian/daemon/etc/resolvconf/update.d/dnsmasq
install -m 755 debian/resolvconf-package debian/daemon/usr/lib/resolvconf/dpkg-event.d/dnsmasq
install -m 644 debian/default debian/daemon/etc/default/dnsmasq
install -m 644 dnsmasq.conf.example debian/daemon/etc/dnsmasq.conf
install -m 644 debian/readme.dnsmasq.d debian/daemon/etc/dnsmasq.d/README
install -m 644 debian/systemd.service debian/daemon/lib/systemd/system/dnsmasq.service
install -m 644 debian/insserv debian/daemon/etc/insserv.conf.d/dnsmasq
ln -s $(package) debian/daemon/usr/share/doc/dnsmasq
cd debian/daemon && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-gencontrol -pdnsmasq -Pdebian/daemon
chown -R root.root debian/daemon
chmod -R g-ws debian/daemon
dpkg --build debian/daemon ..
binary-arch: checkroot
$(checkdir)
rm -rf debian/base
install -m 755 \
-d debian/base/DEBIAN \
-d debian/base/etc/dbus-1/system.d \
-d debian/base/usr/share/doc/$(package) \
-d debian/base/usr/share/doc/$(package)/examples \
-d debian/base/var/run \
-d debian/base/var/lib/misc
make $(TARGET) PREFIX=/usr DESTDIR=`pwd`/debian/base CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
ifeq (,$(findstring nodocs,$(DEB_BUILD_OPTIONS)))
install -m 644 doc.html debian/base/usr/share/doc/$(package)/.
install -m 644 setup.html debian/base/usr/share/doc/$(package)/.
install -m 644 dnsmasq.conf.example debian/base/usr/share/doc/$(package)/examples/.
install -m 644 FAQ debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/FAQ
install -m 644 CHANGELOG debian/base/usr/share/doc/$(package)/changelog
gzip -9 debian/base/usr/share/doc/$(package)/changelog
install -m 644 CHANGELOG.archive debian/base/usr/share/doc/$(package)/changelog.archive
gzip -9 debian/base/usr/share/doc/$(package)/changelog.archive
install -m 644 dbus/DBus-interface debian/base/usr/share/doc/$(package)/.
gzip -9 debian/base/usr/share/doc/$(package)/DBus-interface
endif
install -m 644 debian/dnsmasq-base.conffiles debian/base/DEBIAN/conffiles
install -m 755 debian/dnsmasq-base.postinst debian/base/DEBIAN/postinst
install -m 755 debian/dnsmasq-base.postrm debian/base/DEBIAN/postrm
install -m 644 debian/changelog debian/base/usr/share/doc/$(package)/changelog.Debian
gzip -9 debian/base/usr/share/doc/$(package)/changelog.Debian
install -m 644 debian/readme debian/base/usr/share/doc/$(package)/README.Debian
install -m 644 debian/copyright debian/base/usr/share/doc/$(package)/copyright
install -m 644 debian/dbus.conf debian/base/etc/dbus-1/system.d/dnsmasq.conf
gzip -9 debian/base/usr/share/man/man8/dnsmasq.8
for f in debian/base/usr/share/man/*; do \
if [ -f $$f/man8/dnsmasq.8 ]; then \
gzip -9 $$f/man8/dnsmasq.8 ; \
fi \
done
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/base/usr/sbin/dnsmasq
endif
cd debian/base && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps debian/base/usr/sbin/dnsmasq
dpkg-gencontrol -pdnsmasq-base -Pdebian/base
chown -R root.root debian/base
chmod -R g-ws debian/base
dpkg --build debian/base ..
ifeq ($(DEB_BUILD_ARCH_OS),linux)
rm -rf debian/utils
install -m 755 -d debian/utils/DEBIAN \
-d debian/utils/usr/share/man/man1 \
-d debian/utils/usr/bin \
-d debian/utils/usr/share/doc/dnsmasq-utils
make -C contrib/wrt PREFIX=/usr DESTDIR=`pwd`/debian/utils CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" COPTS="$(COPTS)" CC=gcc
install -m 755 contrib/wrt/dhcp_release debian/utils/usr/bin/dhcp_release
install -m 644 contrib/wrt/dhcp_release.1 debian/utils/usr/share/man/man1/dhcp_release.1
gzip -9 debian/utils/usr/share/man/man1/dhcp_release.1
install -m 755 contrib/wrt/dhcp_lease_time debian/utils/usr/bin/dhcp_lease_time
install -m 644 contrib/wrt/dhcp_lease_time.1 debian/utils/usr/share/man/man1/dhcp_lease_time.1
install -m 644 debian/copyright debian/utils/usr/share/doc/dnsmasq-utils/copyright
install -m 644 debian/changelog debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/doc/dnsmasq-utils/changelog.Debian
gzip -9 debian/utils/usr/share/man/man1/dhcp_lease_time.1
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
strip -R .note -R .comment debian/utils/usr/bin/dhcp_release
strip -R .note -R .comment debian/utils/usr/bin/dhcp_lease_time
endif
cd debian/utils && find . -type f ! -regex '.*DEBIAN/.*' -printf '%P\0' | xargs -r0 md5sum > DEBIAN/md5sums
dpkg-shlibdeps -Tdebian/utils-substvars debian/utils/usr/bin/dhcp_release debian/utils/usr/bin/dhcp_lease_time
dpkg-gencontrol -Tdebian/utils-substvars -pdnsmasq-utils -Pdebian/utils
chown -R root.root debian/utils
chmod -R g-ws debian/utils
dpkg --build debian/utils ..
endif
define checkdir
test -f Makefile -a -f debian/rules
endef
# Below here is fairly generic really
binary: binary-arch binary-indep
build:
build-arch:
build-indep:
checkroot:
test root = "`whoami`"
.PHONY: binary binary-arch binary-indep clean checkroot

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
1.0

33
debian/systemd.service vendored Normal file
View File

@@ -0,0 +1,33 @@
[Unit]
Description=A lightweight DHCP and caching DNS server
[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq
# Test the config file and refuse starting if it is not valid.
ExecStartPre=/usr/sbin/dnsmasq --test
# We run dnsmasq via the /etc/init.d/dnsmasq script which acts as a
# wrapper picking up extra configuration files and then execs dnsmasq
# itself, when called with the "systemd-exec" function.
#
# It also adds the command-line flags
# --keep-in-foreground --pid-file --enable-dbus
# to disable writing a pid-file (not needed with systemd) and
# enable DBus by default because we use DBus activation.
#
ExecStart=/etc/init.d/dnsmasq systemd-exec
# The systemd-*-resolvconf functions configure (and deconfigure)
# resolvconf to work with the dnsmasq DNS server. They're called liek
# this to get correct error handling (ie don't start-resolvconf if the
# dnsmasq daemon fails to start.
ExecStartPost=/etc/init.d/dnsmasq systemd-start-resolvconf
ExecStop=/etc/init.d/dnsmasq systemd-stop-resolvconf
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target

View File

@@ -4,11 +4,16 @@
# as the long options legal on the command line. See
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
# Listen on this specific port instead of the standard DNS port
# (53). Setting this to zero completely disables DNS function,
# leaving only DHCP and/or TFTP.
#port=5353
# The following two options make you a better netizen, since they
# tell dnsmasq to filter out queries which the public DNS cannot
# answer, and which load the servers (especially the root servers)
# uneccessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link uneccessarily.
# unnecessarily. If you have a dial-on-demand link they also stop
# these requests from bringing up the link unnecessarily.
# Never forward plain names (without a dot or domain part)
#domain-needed
@@ -48,7 +53,7 @@
# non-public domains.
#server=/localnet/192.168.0.1
# Example of routing PTR queries to nameservers: this will send all
# Example of routing PTR queries to nameservers: this will send all
# address->name queries for 192.168.3/24 to nameserver 10.1.2.3
#server=/3.168.192.in-addr.arpa/10.1.2.3
@@ -57,14 +62,14 @@
#local=/localnet/
# Add domains which you want to force to an IP address here.
# The example below send any host in doubleclick.net to a local
# webserver.
#address=/doubleclick.net/127.0.0.1
# The example below send any host in double-click.net to a local
# web-server.
#address=/double-click.net/127.0.0.1
# --address (and --server) work with IPv6 addresses too.
#address=/www.thekelleys.org.uk/fe80::20d:60ff:fe36:f83
# You can control how dnsmasq talks to a server: this forces
# You can control how dnsmasq talks to a server: this forces
# queries to 10.1.2.3 to be routed via eth1
# server=10.1.2.3@eth1
@@ -90,7 +95,7 @@
#listen-address=
# If you want dnsmasq to provide only DNS service on an interface,
# configure it as shown above, and then use the following line to
# disable DHCP on it.
# disable DHCP and TFTP on it.
#no-dhcp-interface=
# On systems which support it, dnsmasq binds the wildcard address,
@@ -145,7 +150,7 @@
# some DHCP options may be set only for this network.
#dhcp-range=set:red,192.168.0.50,192.168.0.150
# Use this DHCP range only when the tag "green" is set.
# Use this DHCP range only when the tag "green" is set.
#dhcp-range=tag:green,192.168.0.50,192.168.0.150,12h
# Specify a subnet which can't be used for dynamic address allocation,
@@ -153,17 +158,56 @@
# dhcp-host declarations will be ignored unless there is a dhcp-range
# of some type for the subnet in question.
# In this case the netmask is implied (it comes from the network
# configuration on the machine running dnsmasq) it is possible to give
# an explict netmask instead.
# configuration on the machine running dnsmasq) it is possible to give
# an explicit netmask instead.
#dhcp-range=192.168.0.0,static
# Enable DHCPv6. Note that the prefix-length does not need to be specified
# and defaults to 64 if missing/
#dhcp-range=1234::2, 1234::500, 64, 12h
# Do Router Advertisements, BUT NOT DHCP for this subnet.
#dhcp-range=1234::, ra-only
# Do Router Advertisements, BUT NOT DHCP for this subnet, also try and
# add names to the DNS for the IPv6 address of SLAAC-configured dual-stack
# hosts. Use the DHCPv4 lease to derive the name, network segment and
# MAC address and assume that the host will also have an
# IPv6 address calculated using the SLAAC alogrithm.
#dhcp-range=1234::, ra-names
# Do Router Advertisements, BUT NOT DHCP for this subnet.
# Set the lifetime to 46 hours. (Note: minimum lifetime is 2 hours.)
#dhcp-range=1234::, ra-only, 48h
# Do DHCP and Router Advertisements for this subnet. Set the A bit in the RA
# so that clients can use SLAAC addresses as well as DHCP ones.
#dhcp-range=1234::2, 1234::500, slaac
# Do Router Advertisements and stateless DHCP for this subnet. Clients will
# not get addresses from DHCP, but they will get other configuration information.
# They will use SLAAC for addresses.
#dhcp-range=1234::, ra-stateless
# Do stateless DHCP, SLAAC, and generate DNS names for SLAAC addresses
# from DHCPv4 leases.
#dhcp-range=1234::, ra-stateless, ra-names
# Do router advertisements for all subnets where we're doing DHCPv6
# Unless overriden by ra-stateless, ra-names, et al, the router
# advertisements will have the M and O bits set, so that the clients
# get addresses and configuration from DHCPv6, and the A bit reset, so the
# clients don't use SLAAC addresses.
#enable-ra
# 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
# need to be on the same network. The order of the parameters in these
# do not matter, it's permissble to give name,adddress and MAC in any order
# do not matter, it's permissible to give name, address and MAC in any
# order.
# Always allocate the host with ethernet address 11:22:33:44:55:66
# Always allocate the host with Ethernet address 11:22:33:44:55:66
# The IP address 192.168.0.60
#dhcp-host=11:22:33:44:55:66,192.168.0.60
@@ -171,13 +215,13 @@
# 11:22:33:44:55:66 to be "fred"
#dhcp-host=11:22:33:44:55:66,fred
# Always give the host with ethernet address 11:22:33:44:55:66
# Always give the host with Ethernet address 11:22:33:44:55:66
# the name fred and IP address 192.168.0.60 and lease time 45 minutes
#dhcp-host=11:22:33:44:55:66,fred,192.168.0.60,45m
# Give a host with ethernet address 11:22:33:44:55:66 or
# Give a host with Ethernet address 11:22:33:44:55:66 or
# 12:34:56:78:90:12 the IP address 192.168.0.60. Dnsmasq will assume
# that these two ethernet interfaces will never be in use at the same
# that these two Ethernet interfaces will never be in use at the same
# time, and give the IP address to the second, even if it is already
# in use by the first. Useful for laptops with wired and wireless
# addresses.
@@ -200,27 +244,33 @@
# it asks for a DHCP lease.
#dhcp-host=judge
# Never offer DHCP service to a machine whose ethernet
# Never offer DHCP service to a machine whose Ethernet
# address is 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,ignore
# Ignore any client-id presented by the machine with ethernet
# Ignore any client-id presented by the machine with Ethernet
# address 11:22:33:44:55:66. This is useful to prevent a machine
# being treated differently when running under different OS's or
# between PXE boot and OS boot.
#dhcp-host=11:22:33:44:55:66,id:*
# Send extra options which are tagged as "red" to
# the machine with ethernet address 11:22:33:44:55:66
# the machine with Ethernet address 11:22:33:44:55:66
#dhcp-host=11:22:33:44:55:66,set:red
# Send extra options which are tagged as "red" to
# any machine with ethernet address starting 11:22:33:
# any machine with Ethernet address starting 11:22:33:
#dhcp-host=11:22:33:*:*:*,set:red
# Ignore any clients which are specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unkown-clients".
# This relies on the special "known" tag which is set when
# Give a fixed IPv6 address and name to client with
# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
# Note also the they [] around the IPv6 address are obilgatory.
#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5]
# Ignore any clients which are not specified in dhcp-host lines
# or /etc/ethers. Equivalent to ISC "deny unknown-clients".
# This relies on the special "known" tag which is set when
# a host is matched.
#dhcp-ignore=tag:!known
@@ -244,11 +294,11 @@
# Send options to hosts which ask for a DHCP lease.
# See RFC 2132 for details of available options.
# Common options can be given to dnsmasq by name:
# Common options can be given to dnsmasq by name:
# run "dnsmasq --help dhcp" to get a list.
# Note that all the common settings, such as netmask and
# broadcast address, DNS server and default route, are given
# sane defaults by dnsmasq. You very likely will not need
# sane defaults by dnsmasq. You very likely will not need
# any dhcp-options. If you use Windows clients and Samba, there
# are some options which are recommended, they are detailed at the
# end of this section.
@@ -262,13 +312,23 @@
# Override the default route supplied by dnsmasq and send no default
# route at all. Note that this only works for the options sent by
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# default (1, 3, 6, 12, 28) the same line will send a zero-length option
# for all other option numbers.
#dhcp-option=3
# Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
#dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
# Send DHCPv6 option. Note [] around IPv6 addresses.
#dhcp-option=option6:dns-server,[1234::77],[1234::88]
# Send DHCPv6 option for namservers as the machine running
# dnsmasq and another.
#dhcp-option=option6:dns-server,[::],[1234::88]
# Ask client to poll for option changes every six hours. (RFC4242)
#dhcp-option=option6:information-refresh-time,6h
# Set the NTP time server address to be the same machine as
# is running dnsmasq
#dhcp-option=42,0.0.0.0
@@ -296,13 +356,16 @@
# http://www.samba.org/samba/ftp/docs/textdocs/DHCP-Server-Configuration.txt
# adapted for a typical dnsmasq installation where the host running
# dnsmasq is also the host running samba.
# you may want to uncomment some or all of them if you use
# you may want to uncomment some or all of them if you use
# Windows clients and Samba.
#dhcp-option=19,0 # option ip-forwarding off
#dhcp-option=44,0.0.0.0 # set netbios-over-TCP/IP nameserver(s) aka WINS server(s)
#dhcp-option=45,0.0.0.0 # netbios datagram distribution server
#dhcp-option=46,8 # netbios node type
# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
#dhcp-option=252,"\n"
# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
# probably doesn't support this......
#dhcp-option=option:domain-search,eng.apple.com,marketing.apple.com
@@ -310,10 +373,10 @@
# Send RFC-3442 classless static routes (note the netmask encoding)
#dhcp-option=121,192.168.1.0/24,1.2.3.4,10.0.0.0/8,5.6.7.8
# Send vendor-class specific options encapsulated in DHCP option 43.
# Send vendor-class specific options encapsulated in DHCP option 43.
# The meaning of the options is defined by the vendor-class so
# options are sent only when the client supplied vendor class
# matches the class given here. (A substring match is OK, so "MSFT"
# matches the class given here. (A substring match is OK, so "MSFT"
# matches "MSFT" and "MSFT 5.0"). This example sets the
# mtftp address to 0.0.0.0 for PXEClients.
#dhcp-option=vendor:PXEClient,1,0.0.0.0
@@ -330,7 +393,7 @@
# Send options to PXELinux. Note that we need to send the options even
# though they don't appear in the parameter request list, so we need
# to use dhcp-option-force here.
# to use dhcp-option-force here.
# See http://syslinux.zytor.com/pxe.php#special for details.
# Magic number - needed before anything else is recognised
#dhcp-option-force=208,f1:00:74:7e
@@ -341,24 +404,27 @@
# Reboot time. (Note 'i' to send 32-bit value)
#dhcp-option-force=211,30i
# Set the boot filename for netboot/PXE. You will only need
# Set the boot filename for netboot/PXE. You will only need
# this is you want to boot machines over the network and you will need
# a TFTP server; either dnsmasq's built in TFTP server or an
# external one. (See below for how to enable the TFTP server.)
#dhcp-boot=pxelinux.0
# The same as above, but use custom tftp-server instead machine running dnsmasq
#dhcp-boot=pxelinux,server.name,192.168.1.100
# Boot for Etherboot gPXE. The idea is to send two different
# filenames, the first loads gPXE, and the second tells gPXE what to
# load. The dhcp-match sets the gpxe tag for requests from gPXE.
#dhcp-match=set:gpxe,175 # gPXE sends a 175 option.
#dhcp-boot=tag:!gpxe,undionly.kpxe
#dhcp-boot=mybootimage
# Encapsulated options for Etherboot gPXE. All the options are
# encapsulated within option 175
#dhcp-option=encap:175, 1, 5b # priority code
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 176, 1b # no-proxydhcp
#dhcp-option=encap:175, 177, string # bus-id
#dhcp-option=encap:175, 189, 1b # BIOS drive code
#dhcp-option=encap:175, 190, user # iSCSI username
#dhcp-option=encap:175, 191, pass # iSCSI password
@@ -368,7 +434,7 @@
#dhcp-match=peecees, option:client-arch, 0 #x86-32
#dhcp-match=itanics, option:client-arch, 2 #IA64
#dhcp-match=hammers, option:client-arch, 6 #x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
#dhcp-match=mactels, option:client-arch, 7 #EFI x86-64
# Do real PXE, rather than just booting a single file, this is an
# alternative to dhcp-boot.
@@ -380,11 +446,11 @@
#pxe-service=x86PC, "Boot from local disk"
# Loads <tftp-root>/pxelinux.0 from dnsmasq TFTP server.
#pxe-service=x86PC, "Install Linux", pxelinux
#pxe-service=x86PC, "Install Linux", pxelinux
# Loads <tftp-root>/pxelinux.0 from TFTP server at 1.2.3.4.
# Beware this fails on old PXE ROMS.
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
#pxe-service=x86PC, "Install Linux", pxelinux, 1.2.3.4
# Use bootserver on network, found my multicast or broadcast.
#pxe-service=x86PC, "Install windows from RIS server", 1
@@ -395,20 +461,20 @@
# If you have multicast-FTP available,
# information for that can be passed in a similar way using options 1
# to 5. See page 19 of
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# http://download.intel.com/design/archives/wfm/downloads/pxespec.pdf
# Enable dnsmasq's built-in TFTP server
#enable-tftp
# Set the root directory for files availble via FTP.
# Set the root directory for files available via FTP.
#tftp-root=/var/ftpd
# Make the TFTP server more secure: with this set, only files owned by
# the user dnsmasq is running as will be send over the net.
#tftp-secure
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# This option stops dnsmasq from negotiating a larger blocksize for TFTP
# transfers. It will slow things down, but may rescue some broken TFTP
# clients.
#tftp-no-blocksize
@@ -421,6 +487,14 @@
# Can fail with old PXE ROMS. Overridden by --pxe-service.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,192.168.0.3
# If there are multiple external tftp servers having a same name
# (using /etc/hosts) then that name can be specified as the
# tftp_servername (the third option to dhcp-boot) and in that
# case dnsmasq resolves this name and returns the resultant IP
# addresses in round robin fasion. This facility can be used to
# load balance the tftp load among a set of servers.
#dhcp-boot=/var/ftpd/pxelinux.0,boothost,tftp_server_name
# Set the limit on DHCP leases, the default is 150
#dhcp-lease-max=150
@@ -433,16 +507,16 @@
# and take over the lease for any client which broadcasts on the network,
# whether it has a record of the lease or not. This avoids long timeouts
# when a machine wakes up on a new network. DO NOT enable this if there's
# the slighest chance that you might end up accidentally configuring a DHCP
# the slightest chance that you might end up accidentally configuring a DHCP
# server for your campus/company accidentally. The ISC server uses
# the same option, and this URL provides more information:
# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
# http://www.isc.org/files/auth.html
#dhcp-authoritative
# Run an executable when a DHCP lease is created or destroyed.
# The arguments sent to the script are "add" or "del",
# The arguments sent to the script are "add" or "del",
# then the MAC address, the IP address and finally the hostname
# if there is one.
# if there is one.
#dhcp-script=/bin/echo
# Set the cachesize here.
@@ -451,7 +525,7 @@
# If you want to disable negative caching, uncomment this.
#no-negcache
# Normally responses which come form /etc/hosts and the DHCP lease
# Normally responses which come from /etc/hosts and the DHCP lease
# file have Time-To-Live set as zero, which conventionally means
# do not cache further. If you are happy to trade lower load on the
# server for potentially stale date, you can set a time-to-live (in

View File

@@ -1,9 +1,17 @@
<HTML>
<HEAD>
<TITLE> Dnsmasq - a DNS forwarder for NAT firewalls.</TITLE>
<link rel="icon"
href="http://www.thekelleys.org.uk/dnsmasq/images/favicon.ico">
</HEAD>
<BODY BGCOLOR="WHITE">
<H1 ALIGN=center>Dnsmasq</H1>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="left" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td>
<td align="middle" valign="middle"><h1>Dnsmasq</h1></td>
<td align="right" valign="middle"><img border="0" src="http://www.thekelleys.org.uk/dnsmasq/images/icon.png" /></td></tr>
</table>
Dnsmasq is a lightweight, easy to configure DNS forwarder and DHCP
server. It is designed to provide DNS and, optionally, DHCP, to a
small network. It can serve the names of local machines which are
@@ -19,7 +27,7 @@ connection but would be a good choice for any smallish network (up to
1000 clients is known to work) where low
resource use and ease of configuration are important.
<P>
Supported platforms include Linux (with glibc and uclibc), *BSD,
Supported platforms include Linux (with glibc and uclibc), Android, *BSD,
Solaris and Mac OS X.
Dnsmasq is included in at least the following Linux distributions:
Gentoo, Debian, Slackware, Suse, Fedora,
@@ -80,24 +88,19 @@ for any or all local machines.
</LI>
</DIR>
<H2>Download.</H2>
<H2>Get code.</H2>
<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
<A HREF="http://www.thekelleys.org.uk/dnsmasq/">Download</A> dnsmasq here.
The tarball includes this documentation, source, and manpage.
There is also a <A HREF="CHANGELOG"> CHANGELOG</A> and a <A HREF="FAQ">FAQ</A>.
Dnsmasq is part of the Debian distribution, it can be downloaded from
<A HREF="http://ftp.debian.org/debian/pool/main/d/dnsmasq/"> here</A> or installed using <TT>apt</TT>.
<H2>Links.</H2>
Damien Raude-Morvan has an article in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
There is a good article about dnsmasq at <A
HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
and another at <A
HREF="http://www.linux.com/articles/149040">http://www.linux.com/articles/149040</A>
and Ilya Evseev has an article in Russian about dnsmasq to be found at
<A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq">
http://ilya-evseev.narod.ru/articles/dnsmasq</A>. Ismael Ull has an
article about dnsmasq in Spanish at <A HREF="http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq">http://www.mey-online.com.ar/blog/index.php/archives/guia-rapida-de-dnsmasq</A>
Dnsmasq has a git repository which contains the complete release
history of version 2 and development history from 2.60. You can
<A HREF="http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=summary">browse</A>
the repo, or get a copy using git protocol with the command
<PRE><TT>git clone git://thekelleys.org.uk/dnsmasq.git </TT></PRE>
<H2>License.</H2>
Dnsmasq is distributed under the GPL. See the file COPYING in the distribution
for details.

12
logo/README Normal file
View File

@@ -0,0 +1,12 @@
Dnsmasq logo, contributed by Justin Clift.
The source format is Inkscape SVG vector format, which is scalable and
easy to export to other formats. For convenience I've included a 56x31
png export and a 16x16 ico suitable for use as a web favicon.
Simon Kelley, 22/10/2010

BIN
logo/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
logo/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

157
logo/icon.svg Normal file
View File

@@ -0,0 +1,157 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
width="56"
height="31"
viewBox="0 0 56 31"
enable-background="new 0 0 72.833 46.667"
xml:space="preserve"
id="svg2"
inkscape:version="0.47 r22583"
sodipodi:docname="dnsmasq_icon.svg"
inkscape:export-filename="/x/centos_home/jc/workspace/git_repos/libvirt-media/libvirt-media/png/dnsmasq_icon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><metadata
id="metadata27"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs25"><inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 23.3335 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="72.833 : 23.3335 : 1"
inkscape:persp3d-origin="36.4165 : 15.555667 : 1"
id="perspective4857" />
<filter
id="filter3802"
inkscape:label="filter1"
color-interpolation-filters="sRGB" /><linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient4929"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" />
<linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient5798"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" /><linearGradient
inkscape:collect="always"
xlink:href="#SVGID_3_"
id="linearGradient5812"
gradientUnits="userSpaceOnUse"
x1="30.564501"
y1="-8.8144999"
x2="32.937"
y2="32.715599" /><filter
id="filter6262"
inkscape:label="Drop shadow"
width="1.5"
height="1.5"
x="-0.25"
y="-0.25"
color-interpolation-filters="sRGB"><feGaussianBlur
id="feGaussianBlur6264"
in="SourceAlpha"
stdDeviation="2.500000"
result="blur" /><feColorMatrix
id="feColorMatrix6266"
result="bluralpha"
type="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.500000 0 " /><feOffset
id="feOffset6268"
in="bluralpha"
dx="2.700000"
dy="2.600000"
result="offsetBlur" /><feMerge
id="feMerge6270"><feMergeNode
id="feMergeNode6272"
in="offsetBlur" /><feMergeNode
id="feMergeNode6274"
in="SourceGraphic" /></feMerge></filter></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1568"
inkscape:window-height="1076"
id="namedview23"
showgrid="false"
inkscape:zoom="8"
inkscape:cx="31.966768"
inkscape:cy="21.211869"
inkscape:window-x="567"
inkscape:window-y="328"
inkscape:window-maximized="0"
inkscape:current-layer="layer1"
inkscape:showpageshadow="false"
showborder="true" />
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="dnsmasq"
style="display:inline"
transform="translate(5.2838057,-15.545371)"><g
id="g3790"
transform="matrix(0.8183832,0,0,0.8183832,65.304897,9.8747678)"
style="filter:url(#filter6262)"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90"><g
transform="translate(-91.018462,1.0687099)"
id="g9">
<path
style="fill:#6700ad"
inkscape:connector-curvature="0"
id="path11"
d="M 54.997,12.151 C 50.083,9.132 43.29,7.266 35.791,7.266 c -7.5,0 -14.29,1.866 -19.204,4.885 -4.915,3.016 -7.956,7.184 -7.956,11.789 0,4.604 3.041,8.772 7.956,11.788 4.914,3.02 11.704,-4.271 19.204,-4.271 7.499,0 14.292,7.291 19.206,4.271 4.914,-3.016 7.955,-7.185 7.955,-11.788 0,-4.606 -3.041,-8.773 -7.955,-11.789 z M 24.996,24.318 c -2.698,0 -4.885,-0.922 -4.885,-2.061 0,-1.14 2.187,-2.063 4.885,-2.063 2.697,0 4.885,0.924 4.885,2.063 0,1.139 -2.188,2.061 -4.885,2.061 z m 21.501,0.191 c -2.686,0 -4.861,-0.856 -4.861,-1.912 0,-1.054 2.176,-1.911 4.861,-1.911 2.685,0 4.863,0.857 4.863,1.911 0,1.056 -2.178,1.912 -4.863,1.912 z" />
<path
style="fill:none;stroke:#ffb616;stroke-width:1.85353255"
inkscape:connector-curvature="0"
id="path13"
d="M 54.997,12.151 C 50.083,9.132 43.29,7.266 35.791,7.266 c -7.5,0 -14.29,1.866 -19.204,4.885 -4.915,3.016 -7.956,7.184 -7.956,11.789 0,4.604 3.041,8.772 7.956,11.788 4.914,3.02 11.704,-4.271 19.204,-4.271 7.499,0 14.292,7.291 19.206,4.271 4.914,-3.016 7.955,-7.185 7.955,-11.788 0,-4.606 -3.041,-8.773 -7.955,-11.789 z M 24.996,24.318 c -2.698,0 -4.885,-0.922 -4.885,-2.061 0,-1.14 2.187,-2.063 4.885,-2.063 2.697,0 4.885,0.924 4.885,2.063 0,1.139 -2.188,2.061 -4.885,2.061 z m 21.501,0.191 c -2.686,0 -4.861,-0.856 -4.861,-1.912 0,-1.054 2.176,-1.911 4.861,-1.911 2.685,0 4.863,0.857 4.863,1.911 0,1.056 -2.178,1.912 -4.863,1.912 z" />
</g><g
transform="translate(-91.018462,1.0687099)"
id="Layer_2">
<linearGradient
y2="32.715599"
x2="32.937"
y1="-8.8144999"
x1="30.564501"
gradientUnits="userSpaceOnUse"
id="SVGID_3_">
<stop
id="stop17"
style="stop-color:#FFFFFF;stop-opacity:0.73"
offset="0" />
<stop
id="stop19"
style="stop-color:#FFFFFF;stop-opacity:0"
offset="1" />
</linearGradient>
<path
inkscape:connector-curvature="0"
style="fill:url(#linearGradient5812)"
id="path21"
d="m 54.1,15.361 c -0.924,1.078 -2.782,1.265 -3.857,1.06 C 38,14.083 22.75,12.75 16.027,23.031 14.858,24.819 11.992,25.39 10.293,23.887 8.631,22.417 13.105,15.804 17.646,13.033 22.194,10.252 28.474,8.53 35.41,8.53 c 6.936,0 13.215,1.722 17.756,4.502 0.731,0.442 1.627,1.52 0.934,2.329 z" />
</g></g></g></svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -23,7 +23,7 @@ options. It includes a secure, read-only,
TFTP server to allow net/PXE boot of DHCP hosts and also supports BOOTP.
.PP
Dnsmasq
supports IPv6 for DNS, but not DHCP.
supports IPv6 for all functions and a minimal router-advertisement daemon.
.SH OPTIONS
Note that in general missing parameters are allowed and switch off
functions, for instance "--pid-file" disables writing a PID file. On
@@ -51,7 +51,7 @@ apply to domain names in cnames, PTR records, TXT records etc.
.B \-T, --local-ttl=<time>
When replying with information from /etc/hosts or the DHCP leases
file dnsmasq by default sets the time-to-live field to zero, meaning
that the requestor should not itself cache the information. This is
that the requester should not itself cache the information. This is
the correct thing to do in almost all situations. This option allows a
time-to-live (in seconds) to be given for these replies. This will
reduce the load on the server at the expense of clients using stale
@@ -204,6 +204,17 @@ running another nameserver (or another instance of dnsmasq) on the
same machine. Setting this option also enables multiple instances of
dnsmasq which provide DHCP service to run in the same machine.
.TP
.B --bind-dynamic
Enable a network mode which is a hybrid between
.B --bind-interfaces
and the default. Dnsmasq binds the address of individual interfaces,
allowing multiple dnsmasq instances, but if new interfaces or
addresses appear, it automatically listens on those (subject to any
access-control configuration). This makes dynamically created
interfaces work in the same way as the default. Implementing this
option requires non-standard networking APIs and it is only available
under Linux. On other platforms it falls-back to --bind-interfaces mode.
.TP
.B \-y, --localise-queries
Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
received. If a name in /etc/hosts has more than one address associated with
@@ -249,8 +260,8 @@ requested name has underscores, to catch LDAP requests.
.B \-r, --resolv-file=<file>
Read the IP addresses of the upstream nameservers from <file>, instead of
/etc/resolv.conf. For the format of this file see
.BR resolv.conf (5)
the only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
.BR resolv.conf (5).
The only lines relevant to dnsmasq are nameserver ones. Dnsmasq can
be told to poll more than one resolv.conf file, the first file name specified
overrides the default, subsequent ones add to the list. This is only
allowed when polling; the file with the currently latest modification
@@ -260,11 +271,13 @@ time is the one used.
Don't read /etc/resolv.conf. Get upstream servers only from the command
line or the dnsmasq configuration file.
.TP
.B \-1, --enable-dbus
.B \-1, --enable-dbus[=<service-name>]
Allow dnsmasq configuration to be updated via DBus method calls. The
configuration which can be changed is upstream DNS servers (and
corresponding domains) and cache clear. Requires that dnsmasq has
been built with DBus support.
been built with DBus support. If the service name is given, dnsmasq
provides service at that name, rather than the default which is
.B uk.org.thekelleys.dnsmasq
.TP
.B \-o, --strict-order
By default, dnsmasq will send queries to any of the upstream servers
@@ -276,7 +289,7 @@ server strictly in the order they appear in /etc/resolv.conf
By default, when dnsmasq has more than one upstream server available,
it will send queries to just one server. Setting this flag forces
dnsmasq to send all queries to all available servers. The reply from
the server which answers first will be returned to the original requestor.
the server which answers first will be returned to the original requester.
.TP
.B --stop-dns-rebind
Reject (and log) addresses from upstream nameservers which are in the
@@ -303,7 +316,7 @@ This is useful when new nameservers may have different
data than that held in cache.
.TP
.B \-D, --domain-needed
Tells dnsmasq to never forward queries for plain names, without dots
Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
@@ -350,6 +363,9 @@ is a synonym for
.B server
to make configuration files clearer in this case.
IPv6 addresses may include a %interface scope-id, eg
fe80::202:a412:4512:7bbf%eth0.
The optional string after the @ character tells
dnsmasq how to set the source of the queries to this
nameserver. It should be an ip-address, which should belong to the machine on which
@@ -413,9 +429,29 @@ zone files: the port, weight and priority numbers are in a different
order. More than one SRV record for a given service/domain is allowed,
all that match are returned.
.TP
.B --host-record=<name>[,<name>....][<IPv4-address>],[<IPv6-address>]
Add A, AAAA and PTR records to the DNS. This adds one or more names to
the DNS with associated IPv4 (A) and IPv6 (AAAA) records. A name may
appear in more than one
.B host-record
and therefore be assigned more than one address. Only the first
address creates a PTR record linking the address to the name. This is
the same rule as is used reading hosts-files.
.B host-record
options are considered to be read before host-files, so a name
appearing there inhibits PTR-record creation if it appears in
hosts-file also. Unlike hosts-files, names are not expanded, even when
.B expand-hosts
is in effect. Short and long names may appear in the same
.B host-record,
eg.
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
so any number may be included, split by commas.
so any number may be included, delimited by commas; use quotes to put
commas into a string. Note that the maximum length of a single string
is 255 characters, longer strings are split into 255 character chunks.
.TP
.B --ptr-record=<name>[,<target>]
Return a PTR DNS record.
@@ -427,10 +463,18 @@ Return an NAPTR DNS record, as specified in RFC3403.
Return a CNAME record which indicates that <cname> is really
<target>. There are significant limitations on the target; it must be a
DNS name which is known to dnsmasq from /etc/hosts (or additional
hosts files) or from DHCP. If the target does not satisfy this
hosts files), from DHCP or from another
.B --cname.
If the target does not satisfy this
criteria, the whole cname is ignored. The cname must be unique, but it
is permissable to have more than one cname pointing to the same target.
.TP
.B --dns-rr=<name>,<RR-number>,[<hex data>]
Return an arbitrary DNS Resource Record. The number is the type of the
record (which is always in the C_IN class). The value of the record is
given by the hex data, which may be of the form 01:23:45 or 01 23 45 or
012345 or any mixture of these.
.TP
.B --interface-name=<name>,<interface>
Return a DNS record associating the name with the primary address on
the given interface. This flag specifies an A record for the given
@@ -442,6 +486,15 @@ the name. More than one name may be associated with an interface
address by repeating the flag; in that case the first instance is used
for the reverse address-to-name mapping.
.TP
.B --add-mac
Add the MAC address of the requestor to DNS queries which are
forwarded upstream. This may be used to DNS filtering by the upstream
server. The MAC address can only be added if the requestor is on the same
subnet as the dnsmasq server. Note that the mechanism used to achieve this (an EDNS0 option)
is not yet standardised, so this should be considered
experimental. Also note that exposing MAC addresses in this way may
have security and privacy implications.
.TP
.B \-c, --cache-size=<cachesize>
Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
.TP
@@ -456,7 +509,34 @@ Set the maximum number of concurrent DNS queries. The default value is
where this needs to be increased is when using web-server log file
resolvers, which can generate large numbers of concurrent queries.
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>,<end-addr>[,<netmask>[,<broadcast>]][,<lease time>]
.B --proxy-dnssec
A resolver on a client machine can do DNSSEC validation in two ways: it
can perform the cryptograhic operations on the reply it receives, or
it can rely on the upstream recursive nameserver to do the validation
and set a bit in the reply if it succeeds. Dnsmasq is not a DNSSEC
validator, so it cannot perform the validation role of the recursive nameserver,
but it can pass through the validation results from its own upstream
nameservers. This option enables this behaviour. You should only do
this if you trust all the configured upstream nameservers
.I and the network between you and them.
If you use the first DNSSEC mode, validating resolvers in clients,
this option is not required. Dnsmasq always returns all the data
needed for a client to do validation itself.
.TP
.B --conntrack
Read the Linux connection track mark associated with incoming DNS
queries and set the same mark value on upstream traffic used to answer
those queries. This allows traffic generated by dnsmasq to be
associated with the queries which cause it, useful for bandwidth
accounting and firewalling. Dnsmasq must have conntrack support
compiled in and the kernel must have conntrack support
included and configured. This option cannot be combined with
--query-port.
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-addr>[,<end-addr>][,<mode>][,<netmask>[,<broadcast>]][,<lease time>]
.TP
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag],]<start-IPv6addr>[,<end-IPv6addr>][,<mode>][,<prefix-len>][,<lease time>]
Enable the DHCP server. Addresses will be given out from the range
<start-addr> to <end-addr> and from statically defined addresses given
in
@@ -465,29 +545,45 @@ options. If the lease time is given, then leases
will be given for that length of time. The lease time is in seconds,
or minutes (eg 45m) or hours (eg 1h) or "infinite". If not given,
the default lease time is one hour. The
minimum lease time is two minutes. This
option may be repeated, with different addresses, to enable DHCP
minimum lease time is two minutes. For IPv6 ranges, the lease time
maybe "deprecated"; this sets the preferred lifetime sent in a DHCP
lease or router advertisement to zero, which causes clients to use
other addresses, if available, for new connections as a prelude to renumbering.
This option may be repeated, with different addresses, to enable DHCP
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
receive DHCP service via a relay agent. The broadcast address is
netmask is optional: dnsmasq will determine it from the interface
configuration. For networks which receive DHCP service via a relay
agent, dnsmasq cannot determine the netmask itself, so it should be
specified, otherwise dnsmasq will have to guess, based on the class (A, B or
C) of the network address. The broadcast address is
always optional. It is always
allowed to have more than one dhcp-range in a single subnet.
For IPv6, the parameters are slightly different: instead of netmask
and broadcast address, there is an optional prefix length. If not
given, this defaults to 64. Unlike the IPv4 case, the prefix length is not
automatically derived from the interface configuration. The mimimum
size of the prefix length is 64.
The optional
.B set:<tag>
sets an alphanumeric label which marks this network so that
dhcp options may be specified on a per-network basis.
When it is prefixed with 'tag:' instead, then its meaning changes from setting
a tag to matching it. Only one tag may be set, but more than one tag may be matched.
The end address may be replaced by the keyword
a tag to matching it. Only one tag may be set, but more than one tag
may be matched.
The optional <mode> keyword may be
.B static
which tells dnsmasq to enable DHCP for the network specified, but not
to dynamically allocate IP addresses: only hosts which have static
addresses given via
.B dhcp-host
or from /etc/ethers will be served. The end address may be replaced by
the keyword
or from /etc/ethers will be served.
For IPv4, the <mode> may be
.B proxy
in which case dnsmasq will provide proxy-DHCP on the specified
subnet. (See
@@ -496,15 +592,49 @@ and
.B pxe-service
for details.)
The interface:<interface name> section is not normally used. See the
NOTES section for details of this.
For IPv6, the mode may be some combination of
.B ra-only, slaac, ra-names, ra-stateless.
.B ra-only
tells dnsmasq to offer Router Advertisement only on this subnet,
and not DHCP.
.B slaac
tells dnsmasq to offer Router Advertisement on this subnet and to set
the A bit in the router advertisement, so that the client will use
SLAAC addresses. When used with a DHCP range or static DHCP address
this results in the client having both a DHCP-assigned and a SLAAC
address.
.B ra-stateless
sends router advertisements with the O and A bits set, and provides a
stateless DHCP service. The client will use a SLAAC address, and use
DHCP for other configuration information.
.B ra-names
enables a mode
which gives DNS names to dual-stack hosts which do SLAAC for
IPv6. Dnsmasq uses the host's IPv4 lease to derive the name, network
segment and MAC address and assumes that the host will also have an
IPv6 address calculated using the SLAAC algorithm, on the same network
segment. The address is pinged, and if a reply is received, an AAAA
record is added to the DNS for this IPv6
address. Note that this is only happens for directly-connected
networks, (not one doing DHCP via a relay) and it will not work
if a host is using privacy extensions.
.B ra-names
can be combined with
.B ra-stateless
and
.B slaac.
.TP
.B \-G, --dhcp-host=[<hwaddr>][,id:<client_id>|*][,set:<tag>][,<ipaddr>][,<hostname>][,<lease_time>][,ignore]
Specify per host parameters for the DHCP server. This allows a machine
with a particular hardware address to be always allocated the same
hostname, IP address and lease time. A hostname specified like this
overrides any supplied by the DHCP client on the machine. It is also
allowable to ommit the hardware address and include the hostname, in
allowable to omit the hardware address and include the hostname, in
which case the IP address and lease times will apply to any machine
claiming that name. For example
.B --dhcp-host=00:20:e0:3b:13:af,wap,infinite
@@ -529,6 +659,12 @@ refers to the host with client identifier 01:02:03:04. It is also
allowed to specify the client ID as text, like this:
.B --dhcp-host=id:clientidastext,.....
A single
.B dhcp-host
may contain an IPv4 address or an IPv6 address, or both. IPv6 addresses must be bracketed by square brackets thus:
.B --dhcp-host=laptop,[1234::56]
Note that in IPv6 DHCP, the hardware address is not normally available, so a client must be identified by client-id (called client DUID in IPv6-land) or hostname.
The special option id:* means "ignore any client-id
and use MAC addresses only." This is useful when a client presents a client-id sometimes
but not others.
@@ -576,7 +712,7 @@ will only match a
Token-Ring hardware address, since the ARP-address type for token ring
is 6.
As a special case, it is possible to include more than one
As a special case, in DHCPv4, it is possible to include more than one
hardware address. eg:
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2
This allows an IP address to be associated with
@@ -588,15 +724,17 @@ time and there is no way for dnsmasq to enforce this. It is, for instance,
useful to allocate a stable IP address to a laptop which
has both wired and wireless interfaces.
.TP
.B --dhcp-hostsfile=<file>
Read DHCP host information from the specified file. The file contains
.B --dhcp-hostsfile=<path>
Read DHCP host information from the specified file. If a directory
is given, then read all the files contained in that directory. The file contains
information about one host per line. The format of a line is the same
as text to the right of '=' in --dhcp-host. The advantage of storing DHCP host information
in this file is that it can be changed without re-starting dnsmasq:
the file will be re-read when dnsmasq receives SIGHUP.
.TP
.B --dhcp-optsfile=<file>
Read DHCP option information from the specified file. The advantage of
.B --dhcp-optsfile=<path>
Read DHCP option information from the specified file. If a directory
is given, then read all the files contained in that directory. The advantage of
using this option is the same as for --dhcp-hostsfile: the
dhcp-optsfile will be re-read when dnsmasq receives SIGHUP. Note that
it is possible to encode the information in a
@@ -612,14 +750,14 @@ 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. /etc/ethers is re-read when
dnsmasq receives SIGHUP.
dnsmasq receives SIGHUP. IPv6 addresses are NOT read from /etc/ethers.
.TP
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>],[<value>[,<value>]]
.B \-O, --dhcp-option=[tag:<tag>,[tag:<tag>,]][encap:<opt>,][vi-encap:<enterprise>,][vendor:[<vendor-class>],][<opt>|option:<opt-name>|option6:<opt>|option6:<opt-name>],[<value>[,<value>]]
Specify 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
the DNS server and default route are set to the address of the machine
running dnsmasq. If the domain name option has been set, that is sent.
running dnsmasq. (Equivalent rules apply for IPv6.) If the domain name option has been set, that is sent.
This configuration allows these defaults to be overridden,
or other options specified. The option, to be sent may be given as a
decimal number or as "option:<option-name>" The option numbers are
@@ -634,7 +772,7 @@ and to set the time-server address to 192.168.0.4, do
.B --dhcp-option = 42,192.168.0.4
or
.B --dhcp-option = option:ntp-server, 192.168.0.4
The special address 0.0.0.0 is taken to mean "the address of the
The special address 0.0.0.0 (or [::] for DHCPv6) is taken to mean "the address of the
machine running dnsmasq". Data types allowed are comma separated
dotted-quad IP addresses, a decimal number, colon-separated hex digits
and a text string. If the optional tags are given then
@@ -646,6 +784,14 @@ to option 120 are handled as per RFC 3361. Dotted-quad IP addresses
which are followed by a slash and then a netmask size are encoded as
described in RFC 3442.
IPv6 options are specified using the
.B option6:
keyword, followed by the option number or option name. The IPv6 option
name space is disjoint from the IPv4 option name space. IPv6 addresses
in options must be bracketed with square brackets, eg.
.B --dhcp-option=option6:ntp-server,[1234::56]
Be careful: no checking is done that the correct type of data for the
option number is sent, it is quite possible to
persuade dnsmasq to generate illegal DHCP packets with injudicious use
@@ -661,7 +807,7 @@ literal string, use quotes. For instance when using option 66 to send
a literal IP address as TFTP server name, it is necessary to do
.B --dhcp-option=66,"1.2.3.4"
Encapsulated Vendor-class options may also be specified using
Encapsulated Vendor-class options may also be specified (IPv4 only) using
--dhcp-option: for instance
.B --dhcp-option=vendor:PXEClient,1,0.0.0.0
sends the encapsulated vendor
@@ -673,9 +819,9 @@ for selecting encapsulated options in preference to any sent by the
client. It is
possible to omit the vendorclass completely;
.B --dhcp-option=vendor:,1,0.0.0.0
in which case the encapsulated option is always sent.
in which case the encapsulated option is always sent.
Options may be encapsulated within other options: for instance
Options may be encapsulated (IPv4 only) within other options: for instance
.B --dhcp-option=encap:175, 190, "iscsi-client0"
will send option 175, within which is the option 190. If multiple
options are given which are encapsulated with the same option number
@@ -686,8 +832,9 @@ The final variant on encapsulated options is "Vendor-Identifying
Vendor Options" as specified by RFC3925. These are denoted like this:
.B --dhcp-option=vi-encap:2, 10, "text"
The number in the vi-encap: section is the IANA enterprise number
used to identify this option.
used to identify this option. This form of encapsulation is supported
in IPv6.
The address 0.0.0.0 is not treated specially in
encapsulated options.
.TP
@@ -699,14 +846,14 @@ not ask for it in the parameter request list. This is sometimes
needed, for example when sending options to PXELinux.
.TP
.B --dhcp-no-override
Disable re-use of the DHCP servername and filename fields as extra
(IPv4 only) Disable re-use of the DHCP servername and filename fields as extra
option space. If it can, dnsmasq moves the boot server and filename
information (from dhcp-boot) out of their dedicated fields into
DHCP options. This make extra space available in the DHCP packet for
options but can, rarely, confuse old or broken clients. This flag
forces "simple and safe" behaviour to avoid problems in such a case.
.TP
.B \-U, --dhcp-vendorclass=set:<tag>,<vendor-class>
.B \-U, --dhcp-vendorclass=set:<tag>,[enterprise:<IANA-enterprise number>,]<vendor-class>
Map from a vendor-class string to a tag. Most DHCP clients provide a
"vendor class" which represents, in some sense, the type of host. This option
maps vendor classes to tags, so that DHCP options may be selectively delivered
@@ -716,7 +863,13 @@ will allow options to be set only for HP printers like so:
.B --dhcp-option=tag:printers,3,192.168.4.4
The vendor-class string is
substring matched against the vendor-class supplied by the client, to
allow fuzzy matching. The set: prefix is optional but allowed for consistency.
allow fuzzy matching. The set: prefix is optional but allowed for
consistency.
Note that in IPv6 only, vendorclasses are namespaced with an
IANA-allocated enterprise number. This is given with enterprise:
keyword and specifies that only vendorclasses matching the specified
number should be searched.
.TP
.B \-j, --dhcp-userclass=set:<tag>,<user-class>
Map from a user-class string to a tag (with substring
@@ -728,7 +881,7 @@ this to set a different printer server for hosts in the class
"accounts" than for hosts in the class "engineering".
.TP
.B \-4, --dhcp-mac=set:<tag>,<MAC address>
Map from a MAC address to a tag. The MAC address may include
(IPv4 only) Map from a MAC address to a tag. The MAC address may include
wildcards. For example
.B --dhcp-mac=set:3com,01:34:23:*:*:*
will set the tag "3com" for any host whose MAC address matches the pattern.
@@ -738,13 +891,16 @@ Map from RFC3046 relay agent options to tags. This data may
be provided by DHCP relay agents. The circuit-id or remote-id is
normally given as colon-separated hex, but is also allowed to be a
simple string. If an exact match is achieved between the circuit or
agent ID and one provided by a relay agent, the tag is set.
agent ID and one provided by a relay agent, the tag is set.
.B dhcp-remoteid
(but not dhcp-circuitid) is supported in IPv6.
.TP
.B --dhcp-subscrid=set:<tag>,<subscriber-id>
Map from RFC3993 subscriber-id relay agent options to tags.
(IPv4 and IPv6) Map from RFC3993 subscriber-id relay agent options to tags.
.TP
.B --dhcp-proxy[=<ip addr>]......
A normal DHCP relay agent is only used to forward the initial parts of
(IPv4 only) A normal DHCP relay agent is only used to forward the initial parts of
a DHCP interaction to the DHCP server. Once a client is configured, it
communicates directly with the server. This is undesirable if the
relay agent is addding extra information to the DHCP packets, such as
@@ -804,7 +960,7 @@ dhcp-host configuration in dnsmasq and the contents of /etc/hosts and
/etc/ethers.
.TP
.B --dhcp-generate-names=tag:<tag>[,tag:<tag>]
Generate a name for DHCP clients which do not otherwise have one,
(IPv4 only) Generate a name for DHCP clients which do not otherwise have one,
using the MAC address expressed in hex, seperated by dashes. Note that
if a host provides a name, it will be used by preference to this,
unless
@@ -812,14 +968,14 @@ unless
is set.
.TP
.B --dhcp-broadcast[=tag:<tag>[,tag:<tag>]]
When all the given tags appear in the tag set, always use broadcast to
(IPv4 only) When all the given tags appear in the tag set, always use broadcast to
communicate with the host when it is unconfigured. It is permissible
to supply no tags, in which case this is unconditional. Most DHCP clients which
need broadcast replies set a flag in their requests so that this
happens automatically, some old BOOTP clients do not.
.TP
.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>]]
Set BOOTP options to be returned by the DHCP server. Server name and
.B \-M, --dhcp-boot=[tag:<tag>,]<filename>,[<servername>[,<server address>|<tftp_servername>]]
(IPv4 only) Set BOOTP options to be returned by the DHCP server. Server name and
address are optional: if not provided, the name is left empty, and the
address set to the address of the machine running dnsmasq. If dnsmasq
is providing a TFTP service (see
@@ -827,8 +983,25 @@ is providing a TFTP service (see
) then only the filename is required here to enable network booting.
If the optional tag(s) are given,
they must match for this configuration to be sent.
Instead of an IP address, the TFTP server address can be given as a domain
name which is looked up in /etc/hosts. This name can be associated in
/etc/hosts with multiple IP addresses, which are used round-robin.
This facility can be used to load balance the tftp load among a set of servers.
.TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>]
.B --dhcp-sequential-ip
Dnsmasq is designed to choose IP addresses for DHCP clients using a
hash of the client's MAC address. This normally allows a client's
address to remain stable long-term, even if the client sometimes allows its DHCP
lease to expire. In this default mode IP addresses are distributed
pseudo-randomly over the entire available address range. There are
sometimes circumstances (typically server deployment) where it is more
convenient to have IP
addresses allocated sequentially, starting from the lowest available
address, and setting this flag enables this mode. Note that in the
sequential mode, clients which allow a lease to expire are much more
likely to move IP address; for this reason it should not be generally used.
.TP
.B --pxe-service=[tag:<tag>,]<CSA>,<menu text>[,<basename>|<bootservicetype>][,<server address>|<server_name>]
Most uses of PXE boot-ROMS simply allow the PXE
system to obtain an IP address and then download the file specified by
.B dhcp-boot
@@ -844,17 +1017,19 @@ parameter after the menu text may be a file name, in which case dnsmasq acts as
boot server and directs the PXE client to download the file by TFTP,
either from itself (
.B enable-tftp
must be set for this to work) or another TFTP server if the final IP
address is given.
must be set for this to work) or another TFTP server if the final server
address/name is given.
Note that the "layer"
suffix (normally ".0") is supplied by PXE, and should not be added to
the basename. If an integer boot service type, rather than a basename
is given, then the PXE client will search for a
suitable boot service for that type on the network. This search may be done
by broadcast, or direct to a server if its IP address is provided.
by broadcast, or direct to a server if its IP address/name is provided.
If no boot service type or filename is provided (or a boot service type of 0 is specified)
then the menu entry will abort the net boot procedure and
continue booting from local media.
continue booting from local media. The server address can be given as a domain
name which is looked up in /etc/hosts. This name can be associated in
/etc/hosts with multiple IP addresses, which are used round-robin.
.TP
.B --pxe-prompt=[tag:<tag>,]<prompt>[,<timeout>]
Setting this provides a prompt to be displayed after PXE boot. If the
@@ -887,7 +1062,7 @@ create thousands of leases and use lots of memory in the dnsmasq
process.
.TP
.B \-K, --dhcp-authoritative
Should be set when dnsmasq is definitely the only DHCP server on a network.
(IPv4 only) Should be set when dnsmasq is definitely the only DHCP server on a network.
It changes the behaviour from strict RFC compliance so that DHCP requests on
unknown leases from unknown hosts are not ignored. This allows new hosts
to get a lease without a tedious timeout under all circumstances. It also
@@ -895,7 +1070,7 @@ allows dnsmasq to rebuild its lease database without each client needing to
reacquire a lease, if the database is lost.
.TP
.B --dhcp-alternate-port[=<server port>[,<client port>]]
Change the ports used for DHCP from the default. If this option is
(IPv4 only) Change the ports used for DHCP from the default. If this option is
given alone, without arguments, it changes the ports used for DHCP
from 67 and 68 to 1067 and 1068. If a single argument is given, that
port number is used for the server and the port number plus one used
@@ -903,7 +1078,7 @@ for the client. Finally, two port numbers allows arbitrary
specification of both server and client ports for DHCP.
.TP
.B \-3, --bootp-dynamic[=<network-id>[,<network-id>]]
Enable dynamic allocation of IP addresses to BOOTP clients. Use this
(IPv4 only) Enable dynamic allocation of IP addresses to BOOTP clients. Use this
with care, since each address allocated to a BOOTP client is leased
forever, and therefore becomes permanently unavailable for re-use by
other hosts. if this is given without tags, then it unconditionally
@@ -911,7 +1086,7 @@ enables dynamic allocation. With tags, only when the tags are all
set. It may be repeated with different tag sets.
.TP
.B \-5, --no-ping
By default, the DHCP server will attempt to ensure that an address in
(IPv4 only) By default, the DHCP server will attempt to ensure that an address in
not in use before allocating it to a host. It does this by sending an
ICMP echo request (aka "ping") to the address in question. If it gets
a reply, then the address must already be in use, and another is
@@ -924,13 +1099,24 @@ the tags used to determine them.
.B \-l, --dhcp-leasefile=<path>
Use the specified file to store DHCP lease information.
.TP
.B --dhcp-duid=<enterprise-id>,<uid>
(IPv6 only) Specify the server persistent UID which the DHCPv6 server
will use. This option is not normally required as dnsmasq creates a
DUID automatically when it is first needed. When given, this option
provides dnsmasq the data required to create a DUID-EN type DUID. Note
that once set, the DUID is stored in the lease database, so to change between DUID-EN and
automatically created DUIDs or vice-versa, the lease database must be
re-intialised. The enterprise-id is assigned by IANA, and the uid is a
string of hex octets unique to a particular device.
.TP
.B \-6 --dhcp-script=<path>
Whenever a new DHCP lease is created, or an old one destroyed, the
Whenever a new DHCP lease is created, or an old one destroyed, or a
TFTP file transfer completes, the
executable specified by this option is run. <path>
must be an absolute pathname, no PATH search occurs.
The arguments to the process
are "add", "old" or "del", the MAC
address of the host, the IP address, and the hostname,
address of the host (or DUID for IPv6) , the IP address, and the hostname,
if known. "add" means a lease has been created, "del" means it has
been destroyed, "old" is a notification of an existing lease when
dnsmasq starts or a change to MAC address or hostname of an existing
@@ -941,20 +1127,17 @@ token ring. The process is run as root (assuming that dnsmasq was originally run
root) even if dnsmasq is configured to change UID to an unprivileged user.
The environment is inherited from the invoker of dnsmasq, with some or
all of the following variables added.
all of the following variables added
DNSMASQ_CLIENT_ID if the host provided a client-id.
For both IPv4 and IPv6:
DNSMASQ_DOMAIN if the fully-qualified domain name of the host is
known, this is set to the domain part.
known, this is set to the domain part. (Note that the hostname passed
to the script as an argument is never fully-qualified.)
If the client provides vendor-class, hostname or user-class,
these are provided in DNSMASQ_VENDOR_CLASS
DNSMASQ_SUPPLIED_HOSTNAME and
DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn variables, but only for
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database.
If the client provides a hostname, DNSMASQ_SUPPLIED_HOSTNAME
If the client provides user-classes, DNSMASQ_USER_CLASS0..DNSMASQ_USER_CLASSn
If dnsmasq was compiled with HAVE_BROKEN_RTC, then
the length of the lease (in seconds) is stored in
@@ -978,6 +1161,38 @@ is known.
DNSMASQ_TAGS contains all the tags set during the
DHCP transaction, separated by spaces.
DNSMASQ_LOG_DHCP is set if
.B --log-dhcp
is in effect.
For IPv4 only:
DNSMASQ_CLIENT_ID if the host provided a client-id.
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS.
For IPv6 only:
If the client provides vendor-class, DNSMASQ_VENDOR_CLASS_ID,
containing the IANA enterprise id for the class, and
DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn for the data.
DNSMASQ_SERVER_DUID containing the DUID of the server: this is the same for
every call to the script.
DNSMASQ_IAID containing the IAID for the lease. If the lease is a
temporary allocation, this is prefixed to 'T'.
Note that the supplied hostname, vendorclass and userclass data is
only supplied for
"add" actions or "old" actions when a host resumes an existing lease,
since these data are not held in dnsmasq's lease
database.
All file descriptors are
closed except stdin, stdout and stderr which are open to /dev/null
(except in debug mode).
@@ -996,9 +1211,66 @@ all existing leases as they are read from the lease file. Expired
leases will be called with "del" and others with "old". When dnsmasq
receives a HUP signal, the script will be invoked for existing leases
with an "old " event.
There are two further actions which may appear as the first argument
to the script, "init" and "tftp". More may be added in the future, so
scripts should be written to ignore unknown actions. "init" is
described below in
.B --leasefile-ro
The "tftp" action is invoked when a TFTP file transfer completes: the
arguments are the file size in bytes, the address to which the file
was sent, and the complete pathname of the file.
.TP
.B --dhcp-luascript=<path>
Specify a script written in Lua, to be run when leases are created,
destroyed or changed. To use this option, dnsmasq must be compiled
with the correct support. The Lua interpreter is intialised once, when
dnsmasq starts, so that global variables persist between lease
events. The Lua code must define a
.B lease
function, and may provide
.B init
and
.B shutdown
functions, which are called, without arguments when dnsmasq starts up
and terminates. It may also provide a
.B tftp
function.
The
.B lease
function receives the information detailed in
.B --dhcp-script.
It gets two arguments, firstly the action, which is a string
containing, "add", "old" or "del", and secondly a table of tag value
pairs. The tags mostly correspond to the environment variables
detailed above, for instance the tag "domain" holds the same data as
the environment variable DNSMASQ_DOMAIN. There are a few extra tags
which hold the data supplied as arguments to
.B --dhcp-script.
These are
.B mac_address, ip_address
and
.B hostname
for IPv4, and
.B client_duid, ip_address
and
.B hostname
for IPv6.
The
.B tftp
function is called in the same way as the lease function, and the
table holds the tags
.B destination_address,
.B file_name
and
.B file_size.
.TP
.B --dhcp-scriptuser
Specify the user as which to run the lease-change script. This defaults to root, but can be changed to another user using this flag.
Specify the user as which to run the lease-change script or Lua script. This defaults to root, but can be changed to another user using this flag.
.TP
.B \-9, --leasefile-ro
Completely suppress use of the lease database file. The file will not
@@ -1020,7 +1292,7 @@ as if they had arrived at <interface>. This option is necessary when
using "old style" bridging on BSD platforms, since
packets arrive at tap interfaces which don't have an IP address.
.TP
.B \-s, --domain=<domain>[,<address range>]
.B \-s, --domain=<domain>[,<address range>[,local]]
Specifies DNS domains for the DHCP server. Domains may be be given
unconditionally (without the IP range) or for limited IP ranges. This has two effects;
firstly it causes the DHCP server to return the domain to any hosts
@@ -1039,11 +1311,22 @@ and have a machine whose DHCP hostname is "laptop". The IP address for that mach
.B dnsmasq
both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
given as "#" then the domain is read from the first "search" directive
in /etc/resolv.conf (or equivalent). The address range can be of the form
in /etc/resolv.conf (or equivalent).
The address range can be of the form
<ip address>,<ip address> or <ip address>/<netmask> or just a single
<ip address>. See
.B --dhcp-fqdn
which can change the behaviour of dnsmasq with domains.
If the address range is given as ip-address/network-size, then a
additional flag "local" may be supplied which has the effect of adding
--local declarations for forward and reverse DNS queries. Eg.
.B --domain=thekelleys.org.uk,192.168.0.0/24,local
is identical to
.B --domain=thekelleys.org.uk,192.168.0.0/24
--local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
The network size must be 8, 16 or 24 for this to be legal.
.TP
.B --dhcp-fqdn
In the default mode, dnsmasq inserts the unqualified names of
@@ -1062,12 +1345,37 @@ without an address specified when
.B --dhcp-fqdn
is set.
.TP
.B --enable-tftp[=<interface>]
.B --dhcp-client-update
Normally, when giving a DHCP lease, dnsmasq sets flags in the FQDN
option to tell the client not to attempt a DDNS update with its name
and IP address. This is because the name-IP pair is automatically
added into dnsmasq's DNS view. This flag suppresses that behaviour,
this is useful, for instance, to allow Windows clients to update
Active Directory servers. See RFC 4702 for details.
.TP
.B --enable-ra
Enable dnsmasq's IPv6 Router Advertisement feature. DHCPv6 doesn't
handle complete network configuration in the same way as DHCPv4. Router
discovery and (possibly) prefix discovery for autonomous address
creation are handled by a different protocol. When DHCP is in use,
only a subset of this is needed, and dnsmasq can handle it, using
existing DHCP configuration to provide most data. When RA is enabled,
dnsmasq will advertise a prefix for each dhcp-range, with default
router and recursive DNS server as the relevant link-local address on
the machine running dnsmasq. By default, he "managed address" bits are set, and
the "use SLAAC" bit is reset. This can be changed for individual
subnets with the mode keywords described in
.B --dhcp-range.
RFC6106 DNS parameters are included in the advertisements. By default,
the relevant link-local address of the machine running dnsmasq is sent
as recursive DNS server. If provided, the DHCPv6 options dns-server and
domain-search are used for RDNSS and DNSSL.
.TP
.B --enable-tftp
Enable the TFTP server function. This is deliberately limited to that
needed to net-boot a client. Only reading is allowed; the tsize and
blksize extensions are supported (tsize is only supported in octet
mode). See NOTES section for use of the interface argument.
mode).
.TP
.B --tftp-root=<directory>[,<interface>]
Look for files to transfer using TFTP relative to the given
@@ -1095,6 +1403,12 @@ are accessible. It is not recommended to run dnsmasq as root with TFTP
enabled, and certainly not without specifying --tftp-root. Doing so
can expose any world-readable file on the server to any host on the net.
.TP
.B --tftp-lowercase
Convert filenames in TFTP requests to all lowercase. This is useful
for requests from Windows machines, which have case-insensitive
filesystems and tend to play fast-and-loose with case in filenames.
Note that dnsmasq's tftp server always converts "\\" to "/" in filenames.
.TP
.B --tftp-max=<connections>
Set the maximum number of concurrent TFTP connections allowed. This
defaults to 50. When serving a large number of TFTP connections,
@@ -1123,7 +1437,8 @@ of concurrent TFTP connections is limited by the size of the port range.
.TP
.B \-C, --conf-file=<file>
Specify a different configuration file. The conf-file option is also allowed in
configuration files, to include multiple configuration files.
configuration files, to include multiple configuration files. A
filename of "-" causes dnsmasq to read configuration from stdin.
.TP
.B \-7, --conf-dir=<directory>[,<file-extension>......]
Read all the files in the given directory as configuration
@@ -1284,6 +1599,17 @@ so --dhcp=option=tag:!purple,3,1.2.3.4 sends the option when the
tag purple is not in the set of valid tags. (If using this in a
command line rather than a configuration file, be sure to escape !,
which is a shell metacharacter)
When selecting dhcp-options, a tag from dhcp-range is second class
relative to other tags, to make it easy to override options for
individual hosts, so
.B dhcp-range=set:interface1,......
.B dhcp-host=set:myhost,.....
.B dhcp-option=tag:interface1,option:nis-domain,"domain1"
.B dhcp-option=tag:myhost,option:nis-domain,"domain2"
will set the NIS-domain to domain1 for hosts in the range, but
override that to domain2 for a particular host.
.PP
Note that for
.B dhcp-range
@@ -1313,52 +1639,6 @@ parameter in a BOOTP request is used as a tag,
as is the tag "bootp", allowing some control over the options returned to
different classes of hosts.
.B dhcp-range
may have an interface name supplied as
"interface:<interface-name>". The semantics if this are as follows:
For DHCP, if any other dhcp-range exists _without_ an interface name,
then the interface name is ignored and and dnsmasq behaves as if the
interface parts did not exist, otherwise DHCP is only provided to
interfaces mentioned in dhcp-range
declarations. For DNS, if there are no
.B --interface
or
.B --listen-address
flags, behaviour is unchanged by the interface part. If either of
these flags are present, the interfaces mentioned in
dhcp-ranges are added to the set which get DNS service.
Similarly,
.B enable-tftp
may take an interface name, which enables TFTP only for a particular
interface, ignoring
.B --interface
or
.B --listen-address
flags. In addition
.B --tftp-secure
and
.B --tftp-unique-root
and
.B --tftp-no-blocksize
are ignored for requests from such interfaces. (A
.B --tftp-root
directive giving a root path and an interface should be
provided too.)
These rules may seem odd at first sight, but they
allow a single line of the form "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200"
to be added to dnsmasq configuration which then supplies
DHCP and DNS services to that interface, without affecting
what services are supplied to other interfaces and irrespective of
the existance or lack of "interface=<interface>"
lines elsewhere in the dnsmasq configuration.
"enable-tftp=virt0" and "tftp-root=<root>,virt0" do the same job for TFTP.
The idea is
that such a line can be added automatically by libvirt
or equivalent systems, without disturbing any manual
configuration.
.SH EXIT CODES
.PP
0 - Dnsmasq successfully forked into the background, or terminated
@@ -1445,6 +1725,9 @@ assume that it is the system default.
.IR /usr/local/etc/dnsmasq.conf
.IR /etc/resolv.conf
.IR /var/run/dnsmasq/resolv.conf
.IR /etc/ppp/resolv.conf
.IR /etc/dhcpc/resolv.conf
.IR /etc/hosts

View File

@@ -22,7 +22,8 @@ peut être configuré pour envoyer n'importe quel option DHCP.
Il inclut un serveur TFTP sécurisé en lecture seule permettant le démarrage via
le réseau/PXE de clients DHCP et supporte également le protocole BOOTP.
.PP
Dnsmasq supporte IPv6 pour le DNS mais pas pour le DHCP.
Dnsmasq supporte IPv6 et contient un démon minimaliste capable de faire des
annonces routeurs ("router-advertisements").
.SH OPTIONS
Notes : Il est possible d'utiliser des options sans leur donner de paramètre.
Dans ce cas, la fonction correspondante sera désactivée. Par exemple
@@ -231,6 +232,18 @@ autre serveur de nom (ou une autre instance de Dnsmasq) tourne sur la même
machine. Utiliser cette option permet également d'avoir plusieurs instances de
Dnsmasq fournissant un service DHCP sur la même machine.
.TP
.B --bind-dynamic
Autorise un mode réseau intermédiaire entre
.B --bind-interfaces
et le mode par défaut. Dnsmasq s'associe à une seule interface, ce qui permet
plusieurs instances de dnsmasq, mais si une interface ou adresse apparaissent,
il se mettra automatiquement à écouter sur celles-ci (les règles de contrôle
d'accès s'appliquent).
De fait, les interfaces créées dynamiquement fonctionnent de la même façon que
dans le comportement par défaut. Ce fonctionnement nécessite des APIs réseau
non standard et n'est disponible que sous Linux. Sur les autres plateformes,
le fonctionnement est celui du mode --bind-interfaces.
.TP
.B \-y, --localise-queries
Retourne des réponses aux requêtes DNS dépendantes de l'interface sur laquelle
la requête a été reçue, à partir du fichier /etc/hosts. Si un nom dans
@@ -347,10 +360,10 @@ Cela est utile si les nouveaux serveurs sont susceptibles d'avoir des données
différentes de celles stockées dans le cache.
.TP
.B \-D, --domain-needed
Indique à Dnsmasq de ne jamais transmettre en amont de requêtes pour des noms
simples, ne comprenant donc ni points ni nom de domaine. Si un nom n'est pas
dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse de type
"non trouvé" est renvoyée.
Indique à Dnsmasq de ne jamais transmettre en amont de requêtes A ou AAAA pour
des noms simples, c'est à dire ne comprenant ni points ni nom de domaine. Si un
nom n'est pas dans /etc/hosts ou dans la liste des baux DHCP, alors une réponse
de type "non trouvé" est renvoyée.
.TP
.B \-S, --local, --server=[/[<domaine>]/[domaine/]][<Adresse IP>[#<port>][@<Adresse IP source>|<interface>[#<port>]]]
Spécifie directement l'adresse IP d'un serveur de nom amont. Cette option ne
@@ -402,6 +415,10 @@ est synonyme de
("serveur") afin de rendre plus claire l'utilisation de cette option pour cet
usage particulier.
Les adresses IPv6 peuvent inclure un identifiant de zone sous la forme
%interface tel que par exemple
fe80::202:a412:4512:7bbf%eth0.
La chaîne de caractères optionnelle suivant le caractère @ permet de définir
la source que Dnsmasq doit utiliser pour les réponses à ce
serveur de nom. Il doit s'agir d'une des adresses IP appartenant à la machine sur
@@ -488,10 +505,34 @@ dans un ordre différents. Pour un service/domaine donné, plus d'un
enregistrement SRV est autorisé et tous les enregistrements qui coïncident sont
retournés dans la réponse.
.TP
.B --host-record=<nom>[,<nom>....][<adresse IPv4>],[<adresse IPv6>]
Ajoute des enregistrements A, AAAA et PTR dans le DNS. Ceci permet d'ajouter
un ou plusieurs noms dans le DNS et de les associer à des enregistrements IPv4
(A) ou IPv6 (AAAA). Un nom peut apparaître dans plus d'une entrée
.B host-record
et de fait être associé à plus d'une adresse. Seule la première entrée créée
l'enregistrement PTR associée au nom. Ceci correspond à la même règle que celle
utilisée lors de la lecture du fichier hosts.
Les options
.B host-record
sont considérées lues avant le fichier hosts, ainsi un nom apparaissant dans
une option host-record et dans le fichier hosts n'aura pas d'enregistrement
PTR associé à l'entrée dans le fichier hosts. A l'inverse du fichier hosts, les
noms ne sont pas étendus, même lorsque l'option
.B expand-hosts
est activée. Les noms longs et les noms courts peuvent apparaitre dans la même
entrée
.B host-record,
c-à-d
.B --host-record=laptop,laptop.thekelleys.org,192.168.0.1,1234::100
.TP
.B \-Y, --txt-record=<nom>[[,<texte>],<texte>]
Définit un enregistrement DNS de type TXT. La valeur de l'enregistrement TXT est
un ensemble de chaînes de caractères, donc un nombre variable de chaînes de
caractères peuvent être spécifiées, séparées par des virgules.
caractères peuvent être spécifiées, séparées par des virgules. Utilisez des
guillemets pour mettre une virgule dans une chaîne de caractères. Notez que la
longueur maximale pour une chaîne est de 255 caractères, les chaînes plus
longues étant découpées en morceaux de 255 caractères de longs.
.TP
.B --ptr-record=<nom>[,<cible>]
Définit un enregistrement DNS de type PTR.
@@ -503,9 +544,17 @@ Retourne un enregistrement de type NAPTR, tel que spécifié dans le RFC3403.
Retourne un enregistrement de type CNAME qui indique que <cname> est en
réalité <cible>. Il existe des contraintes significatives sur la valeur
de cible; il doit s'agir d'un nom DNS qui est connu de dnsmasq via /etc/hosts
(ou un fichier hôtes additionnel) ou via DHCP. Si une cible ne satisfait
pas ces critères, le CNAME est ignoré. Le CNAME doit être unique, mais
il est autorisé d'avoir plus d'un CNAME pointant vers la même cible.
(ou un fichier hôtes additionnel), ou via DHCP, ou par un autre
.B --cname.
Si une cible ne satisfait pas ces critères, le CNAME est ignoré. Le CNAME
doit être unique, mais il est autorisé d'avoir plus d'un CNAME pointant
vers la même cible.
.TP
.B --dns-rr=<nom>,<numéro-RR>,[<données hexadécimales>]
Retourne un enregistrement DNS arbitraire. Le numéro correspond au type
d'enregistrement (qui est toujours de la classe C_IN). La valeur de
l'enregistrement est donnée dans les données hexadécimales, qui peuvent
être de la forme 01:23:45, 01 23 45,+012345 ou n'importe quelle combinaison.
.TP
.B --interface-name=<nom>,<interface>
Définit un entregistrement DNS associant le nom avec l'adresse primaire sur
@@ -519,6 +568,16 @@ Plus d'un nom peut être associé à une interface donnée en répétant cette o
plusieurs fois; dans ce cas, l'enregistrement inverse pointe vers le nom fourni
dans la première instance de cette option.
.TP
.B --add-mac
Ajoute l'adresse MAC du requêteur aux requêtes DNS transmises aux serveurs
amonts. Cela peut être utilisé dans un but de filtrage DNS par les serveurs
amonts. L'adresse MAC peut uniquement être ajoutée si le requêteur est sur le
même sous-réseau que le serveur dnsmasq. Veuillez noter que le mécanisme
utilisé pour effectuer cela (une option EDNS0) n'est pas encore standardisée,
aussi cette fonctionalité doit être considérée comme expérimentale. Notez
également qu'exposer les adresses MAC de la sorte peut avoir des implications
en termes de sécurité et de vie privée.
.TP
.B \-c, --cache-size=<taille>
Définit la taille du cache de Dnsmasq. La valeur par défaut est de 150 noms.
Définir une valeur de zéro désactive le cache.
@@ -537,7 +596,34 @@ lorsqu'un serveur web a la résolution de nom activée pour l'enregistrement de
son journal des requêtes, ce qui peut générer un nombre important de requêtes
simultanées.
.TP
.B \-F, --dhcp-range=[interface:<interface>,][tag:<label>[,tag:<label>],][set:<label],]<adresse de début>,<adresse de fin>[,<masque de réseau>[,<broadcast>]][,<durée de bail>]
.B --proxy-dnssec
Un resolveur sur une machine cliente peut effectuer la validation DNSSEC de
deux façons : il peut effectuer lui-même les opérations de chiffrements sur
la réponse reçue, ou il peut laisser le serveur récursif amont faire la
validation et positionner un drapeau dans la réponse au cas où celle-ci est
correcte. Dnsmasq n'est pas un validateur DNSSEC, aussi il ne peut effectuer
la validation comme un serveur de nom récursif, cependant il peut retransmettre
les résultats de validation de ses serveurs amonts. Cette option permet
l'activation de cette fonctionalité. Vous ne devriez utiliser cela que si vous
faites confiance aux serveurs amonts
.I ainsi que le réseau entre vous et eux.
Si vous utilisez le premier mode DNSSEC, la validation par le resolveur des
clients, cette option n'est pas requise. Dnsmasq retourne toujours toutes les
données nécessaires par un client pour effectuer la validation lui-même.
.TP
.B --conntrack
Lis le marquage de suivi de connexion Linux associé aux requêtes DNS entrantes
et positionne la même marque au trafic amont utilisé pour répondre à ces
requétes. Cela permet au trafic généré par Dnsmasq d'étre associé aux requêtes
l'ayant déclenché, ce qui est pratique pour la gestion de la bande passante
(accounting) et le filtrage (firewall). Dnsmasq doit pour cela être compilé
avec le support conntrack, le noyau doit également inclure conntrack et être
configuré pour cela. Cette option ne peut pas être combinée avec
--query-port.
.TP
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<adresse de début>[,<adresse de fin>][,<mode>][,<masque de réseau>[,<broadcast>]][,<durée de bail>]
.TP
.B \-F, --dhcp-range=[tag:<label>[,tag:<label>],][set:<label>],]<addresse IPv6 de début>[,<adresse IPv6 de fin>][,<mode>][,<longueur de préfixe>][,<durée de bail>]
Active le serveur DHCP. Les adresses seront données dans la plage comprise entre
<adresse de début> et <adresse de fin> et à partir des adresses définies
statiquement dans l'option
@@ -548,15 +634,35 @@ en heures (exemple : 1h) ou être la chaine de caractère "infinite" pour une
durée indéterminée. Si aucune valeur n'est donnée, une durée de bail par défaut
de une heure est appliquée. La valeur minimum pour un bail DHCP est de 2
minutes.
Pour les plages IPv6, la durée de bail peut-être égale au mot-clef "deprecated"
(obsolète); Cela positionne la durée de vie préférée envoyée dans les baux DHCP
ou les annonces routeurs à zéro, ce qui incite les clients à utiliser d'autres
adresses autant que possible, pour toute nouvelle connexion, en préalable à
la renumérotation.
Cette option peut être répétée, avec différentes adresses,
pour activer le service DHCP sur plus d'un réseau. Pour des réseaux directement
connectés (c'est-à-dire des réseaux dans lesquels la machine sur laquelle tourne
Dnsmasq possède une interface), le masque de réseau est optionnel. Il est par
contre requis pour les réseaux pour lesquels le service DHCP se fait via un
relais DHCP ("relay agent"). L'adresse de broadcast est toujours optionnelle.
Dnsmasq possède une interface), le masque de réseau est optionnel : Dnsmasq la
déterminera à partir de la configuration des interfaces.
Pour les réseaux pour lesquels le service DHCP se fait via un relais DHCP
("relay agent"), Dnsmasq est incapable de déterminer le masque par lui-même,
aussi il doit être spécifié, faute de quoi Dnsmasq essaiera de le deviner en
fonction de la classe (A, B ou C) de l'adresse réseau. L'adresse de broadcast
est toujours optionnelle.
Il est toujours possible d'avoir plus d'une plage DHCP pour un même
sous-réseau.
Pour IPv6, les paramètres sont légèrement différents : au lieu d'un masque de
réseau et d'une adresse de broadcast, il existe une longueur de préfixe
optionnelle. Si elle est omise, la valeur par défaut est 64. À la différence
d'IPv4, la longueur de préfixe n'est pas automatiquement déduite de la
configuration de l'interface. La taille minimale pour la longueur de préfixe
est 64.
L'identifiant de label optionnel
.B set:<label>
fournie une étiquette alphanumérique qui identifie ce réseau, afin de permettre
@@ -565,15 +671,15 @@ Lorsque préfixé par 'tag:', la signification change, et au lieu de définir un
label, il définit le label pour laquelle la règle s'applique. Un seul label peut-
être défini mais plusieurs labels peuvent coïncider.
L'adresse de fin peut être remplacée par le mot-clef
Le mot clef optionnel <mode> peut être égal à
.B static
("statique") qui indique à Dnsmasq d'activer le service DHCP pour le réseau
("statique") ce qui indique à Dnsmasq d'activer le service DHCP pour le réseau
spécifié, mais de ne pas activer l'allocation dynamique d'adresses IP : Seuls
les hôtes possédant des adresses IP statiques fournies via
.B dhcp-host
ou présentes dans le fichier /etc/ethers seront alors servis par le DHCP.
L'adresse de fin peut-être remplacée par le mot-clef
Pour IPv4, le <mode> peut est égal à
.B proxy
, auquel cas Dnsmasq fournira un service de DHCP proxy pour le sous-réseau
spécifié. (voir
@@ -582,8 +688,43 @@ et
.B pxe-service
pour plus de détails).
La section interface:<nom d'interface> n'est normalement pas utilisée. Se
référer aux indications de la section NOTES pour plus de détail à ce sujet.
Pour IPv6, le mode peut-être une combinaison des valeurs
.B ra-only, slaac, ra-names, ra-stateless.
.B ra-only
indique à dnsmasq de n'effectuer que des annonces de routeur (Router
Advertisement, RA) sur ce sous-réseau, et de ne pas faire de DHCP.
.B slaac
indique à dnsmasq d'effectuer des annonces de routeur sur ce sous-réseau
et de positionner dans celles-ci le bit A, afin que les clients utilisent
des adresses SLAAC. Lorsqu'utilisé conjointement avec une plage DHCP ou des
affectations statiques d'adresses DHCP, les clients disposeront à la fois
d'adresses DHCP assignées et d'adresses SLAAC.
.B ra-stateless
indique à dnsmasq d'effectuer des annonces de routeur avec les bits 0 et A
positionnés, et de fournir un service DHCP sans état ("stateless"). Les clients
utiliseront des adresses SLAAC, et utiliseront DHCP pour toutes les autres
informations de configuration.
.B ra-names
active un mode qui fourni des noms DNS aux hôtes fonctionnant en double pile
("dual stack") et configurés pour faire du SLAAC en IPv6. Dnsmasq utilise le
bail IPv4 de l'hôte afin de dériver le nom, le segment de réseau et l'adresse
MAC et assume que l'hôte disposera d'une adresse IPv6 calculée via l'algorithme
SLAAC, sur le même segment de réseau. Un ping est envoyé à l'adresse, et si une
réponse est obtenue, un enregistrement AAAA est rajouté dans le DNS pour cette
adresse IPv6. Veuillez-noter que cela n'arrive que pour les réseaux directement
connectés (et non ceux pour lesquels DHCP se fait via relai), et ne
fonctionnera pas si un hôte utilise les "extensions de vie privée"
("privacy extensions").
.B ra-names
peut-être combiné avec
.B ra-stateless
et
.B slaac.
.TP
.B \-G, --dhcp-host=[<adresse matérielle>][,id:<identifiant client>|*][,set:<label>][,<adresse IP>][,<nom d'hôte>][,<durée de bail>][,ignore]
Spécifie les paramètres DHCP relatifs à un hôte. Cela permet à une machine
@@ -616,6 +757,15 @@ spécifier l'identifiant client sous la forme d'une chaîne de caractères, comm
ceci :
.B --dhcp-host=id:identifiantclientsousformedechaine,.....
Un seul
.B dhcp-host
peut contenir une adresse IPv4, une adresse IPv6, ou les deux en même temps.
Les adresses IPv6 doivent-être mises entre crochets comme suit :
.B --dhcp-host=laptop,[1234::56]
A noter que pour le DHCP IPv6, l'adresse matérielle n'est en principe pas
disponible, aussi un client doit-être identifié par un identifiant de client
(appellé "DUID client") ou un nom d'hôte.
L'option spéciale id:* signifie : "ignorer tout identifiant client et n'utiliser
que l'adresse matérielle". Cela est utile lorsqu'un client présente un
identifiant client mais pas les autres.
@@ -662,7 +812,7 @@ ARP en les précédant du type ARP (en Hexadécimal) et de "-". Ainsi
coïncidera uniquement avec des adresses matérielles Token-Ring, puisque le type
ARP pour une adresse Token-Ring est 6.
Un cas spécial correspond à l'inclusion d'une ou plusieurs adresses
Un cas spécial, pour IPv4, correspond à l'inclusion d'une ou plusieurs adresses
matérielles, c-à-d :
.B --dhcp-host=11:22:33:44:55:66,12:34:56:78:90:12,192.168.0.2.
Cela permet à une adresse IP d'être associé à plusieurs adresses
@@ -674,20 +824,24 @@ donné et dnsmasq n'a aucun moyen de s'assurer de cela. Cela est utile,
par exemple, pour allouer une adresse IP stable à un laptop qui
aurait à la fois une connexion filaire et sans-fil.
.TP
.B --dhcp-hostsfile=<fichier>
Lis les informations d'hôtes DHCP dans le fichier spécifié. Le fichier contient
des informations à raison d'un hôte par ligne. Le format d'une ligne est la même
que le texte fourni à la droite sur caractère "=" dans l'option
.B --dhcp-hostsfile=<chemin>
Lis les informations d'hôtes DHCP dans le fichier spécifié. Si l'argument est
un chemin vers un répertoire, lis tous les fichiers de ce répertoire. Le
fichier contient des informations à raison d'un hôte par ligne. Le format
d'une ligne est la même que le texte fourni à la droite sur caractère "=" dans
l'option
.B --dhcp-host.
L'avantage de stocker les informations sur les hôtes DHCP dans ce fichier est
que celles-ci peuvent être modifiées sans recharger Dnsmasq; le fichier sera
relu lorsque Dnsmasq reçoit un signal SIGHUP.
.TP
.B --dhcp-optsfile=<fichier>
Lis les informations relatives aux options DHCP dans le fichier spécifié.
L'intérêt d'utiliser cette option est le même que pour --dhcp-hostsfile : le
fichier spécifié sera rechargé à la réception par dnsmasq d'un signal SIGHUP.
Notez qu'il est possible d'encoder l'information via
.B --dhcp-optsfile=<chemin>
Lis les informations relatives aux options DHCP dans le fichier spécifié. Si
l'argument est un chemin vers un répertoire, lis tous les fichiers de ce
répertoire. L'intérêt d'utiliser cette option est le même que pour
--dhcp-hostsfile : le fichier spécifié sera rechargé à la réception par
dnsmasq d'un signal SIGHUP. Notez qu'il est possible d'encoder l'information
via
.B --dhcp-boot
en utilisant les noms optionnels bootfile-name, server-ip-address et
tftp-server. Ceci permet d'inclure ces options dans un fichier "dhcp-optsfile".DNSMASQ_SUPPLIED_HOSTNAME
@@ -699,22 +853,22 @@ une adresse IP sous la forme de 4 chiffres séparés par des points. Lorsque lu
par Dnsmasq, ces lignes ont exactement le même effet que l'option
.B --dhcp-host
contenant les mêmes informations. /etc/ethers est relu à la réception d'un
signal SIGHUP par Dnsmasq.
signal SIGHUP par Dnsmasq. Les adresses IPv6 ne sont PAS lues dans /etc/ethers.
.TP
.B \-O, --dhcp-option=[tag:<label>,[tag:<label>]][encap:<option>,][vi-encap:<entreprise>,][vendor:[<classe_vendeur>],][<option>|option:<nom d'option>],[<valeur>[,<valeur>]]
.B \-O, --dhcp-option=[tag:<label>,[tag:<label>]][encap:<option>,][vi-encap:<entreprise>,][vendor:[<classe_vendeur>],][<option>|option:<nom d'option>|option6:<option>|option6:<nom d'option>],[<valeur>[,<valeur>]]
Spécifie des options différentes ou supplémentaires pour des clients DHCP. Par
défaut, Dnsmasq envoie un ensemble standard d'options aux clients DHCP : le
masque de réseau et l'adresse de broadcast sont les mêmes que pour l'hôte
sur lequel tourne Dnsmasq, et le serveur DNS ainsi que la route par défaut
prennent comme valeur l'adresse de la machine sur laquelle tourne Dnsmasq. Si
une option de nom de domaine a été définie, son contenu est transmis. Cette
option de configuration permet de changer toutes ces valeurs par défaut, ou de
spécifier d'autres options. L'option DHCP à transmettre peut être fournie sous
forme d'un nombre décimal ou sous la forme "option:<nom d'option>". Les nombres
correspondants aux options sont définis dans la RFC2132 et suivants. Les noms
d'options connus par Dnsmasq peuvent être obtenus via "Dnsmasq --help dhcp".
Par exemple, pour définir la route par défaut à 192.168.4.4, il est possible de
faire
prennent comme valeur l'adresse de la machine sur laquelle tourne Dnsmasq.
(Des règles équivalentes s'appliquent en IPv6). Si une option de nom de domaine
a été définie, son contenu est transmis. Cette option de configuration permet
de changer toutes ces valeurs par défaut, ou de spécifier d'autres options.
L'option DHCP à transmettre peut être fournie sous forme d'un nombre décimal
ou sous la forme "option:<nom d'option>". Les nombres correspondants aux options
sont définis dans la RFC2132 et suivants. Les noms d'options connus par Dnsmasq
peuvent être obtenus via "Dnsmasq --help dhcp". Par exemple, pour définir la
route par défaut à 192.168.4.4, il est possible de faire
.B --dhcp-option=3,192.168.4.4
ou
.B --dhcp-option = option:router, 192.168.4.4
@@ -737,6 +891,13 @@ de l'option 120 sont traités conforméments à la RFC 3361. Les adresses IP sou
forme de 4 chiffres séparés par des points suivies par une barre montante "/",
puis une taille de masque sont encodés conforméments à la RFC 3442.
Les options IPv6 sont fournies en utilisant le mot-clef
.B option6:
suivi par le numéro d'option ou le nom d'option. L'espace de nommage des options
IPv6 est disjint de l'espace de nommage des options IPv4. Les adresses IPv6
en option doivent être entourées de crochets, comme par exemple :
.B --dhcp-option=option6:ntp-server,[1234::56]
Attention : aucun test n'étant fait pour vérifier que des données d'un type
adéquat sont envoyées pour un numéro d'option donné, il est tout à fait possible
de persuader Dnsmasq de générer des paquets DHCP illégaux par une utilisation
@@ -755,8 +916,8 @@ d'une chaîne de caractères comme nom de serveur TFTP, il est nécessaire de fa
comme suit :
.B --dhcp-option=66,"1.2.3.4"
Les options encapsulées de classes de vendeurs peuvent-être aussi spécifiées en
utilisant
Les options encapsulées de classes de vendeurs peuvent-être aussi spécifiées
(pour IPv4 seulement) en utilisant
.B --dhcp-option
: par exemple
.B --dhcp-option=vendor:PXEClient,1,0.0.0.0
@@ -772,7 +933,7 @@ par le client. Il est possible d'omettre complètement une classe de vendeur :
.B --dhcp-option=vendor:,1,0.0.0.0
Dans ce cas l'option encapsulée est toujours envoyée.
Les options peuvent-être encapsulées au sein d'autres options :
En IPv4, les options peuvent-être encapsulées au sein d'autres options :
par exemple
.B --dhcp-option=encap:175, 190, "iscsi-client0"
enverra l'option 175, au sein de laquelle se trouve l'option 190.
@@ -785,7 +946,8 @@ identifiant le vendeur" ("Vendor-Identifying Vendor Options") telle que
décrite dans le RFC3925. Celles-ci sont spécifiées comme suit :
.B --dhcp-option=vi-encap:2, 10, "text"
Le numéro dans la section vi-encap: est le numéro IANA de l'entreprise servant
à identifier cette option.
à identifier cette option. Cette forme d'encapsulation est également supportée
en IPv6.
L'adresse 0.0.0.0 n'est pas traitée de manière particulière lorsque fournie dans
une option encapsulée.
@@ -798,8 +960,8 @@ dans la liste de paramêtres requis. Cela est parfois nécessaire, par exemple l
de la fourniture d'options à PXELinux.
.TP
.B --dhcp-no-override
Désactive la réutilisation des champs DHCP nom de serveur et nom de
fichier comme espace supplémentaire pour les options. Si cela est
(IPv4 seulement) Désactive la réutilisation des champs DHCP nom de serveur et
nom de fichier comme espace supplémentaire pour les options. Si cela est
possible, dnsmasq déplace les informations sur le serveur de démarrage
et le nom de fichier (fournis par 'dhcp-boot') en dehors des champs
dédiés à cet usage dans les options DHCP. Cet espace supplémentaire est
@@ -808,7 +970,8 @@ quelques rares cas, perturber des clients vieux ou défectueux. Cette
option force le comportement à l'utilisation des valeurs "simples et sûres"
afin d'éviter des problèmes dans de tels cas.
.TP
.B \-U, --dhcp-vendorclass=set:<label>,<classe de vendeur>
.B \-U, --dhcp-vendorclass=set:<label>,[enterprise:<numéro IANA d'enterprise>,]<classe de vendeur>
Associe une chaîne de classe de vendeur à un label. La plupart
des clients DHCP fournissent une "classe de vendeur" ("vendor class") qui
représente, d'une certaine façon, le type d'hôte. Cette option associe des
@@ -824,6 +987,11 @@ en temps que sous-chaîne de caractères au sein de la classe de vendeur fournie
par le client, de façon à permettre la recherche d'un sous-ensemble de la chaîne
de caractères ("fuzzy matching"). Le préfixe set: est optionnel mais autorisé
afin de conserver une certaine homogénéité.
Notez qu'en IPv6 (et seulement en IPv6), les noms de classes de vendeurs
sont dans un espace de nom associé au numéro attribué à l'entreprise par
l'IANA. Ce numéro est fourni par le biais du mot-clef enterprise: et seules
les classes de vendeurs associées au numéro spécifié seront cherchées.
.TP
.B \-j, --dhcp-userclass=set:<label>,<classe utilisateur>
Associe une chaîne de classe d'utilisateur à un label (effectue la
@@ -836,7 +1004,7 @@ d'impression différent pour les hôtes de la classe "comptes" et ceux de la
classe "ingénierie".
.TP
.B \-4, --dhcp-mac=set:<label>,<adresse MAC>
Associe une adresse matérielle (MAC) à un label. L'adresse
(IPv4 uniquement) Associe une adresse matérielle (MAC) à un label. L'adresse
matérielle peut inclure des jokers. Par exemple
.B --dhcp-mac=set:3com,01:34:23:*:*:*
permet de définir le label "3com" pour n'importe quel hôte dont l'adresse
@@ -850,16 +1018,19 @@ chaîne de valeurs hexadécimales séparées par des ":", mais il est également
possible qu'elle le soit sous la forme d'une simple chaîne de caractères. Si
l'identifiant de circuit ou d'agent correspond exactement à celui fourni par le
relais DHCP, alors le label est apposé.
.B dhcp-remoteid
est supporté en IPv6 (mais non dhcp-circuitid).
.TP
.B --dhcp-subscrid=set:<label>,<identifiant d'abonné>
Associe des options de relais DHCP issues de la RFC3993 à des labels.
(IPv4 et IPv6) Associe des options de relais DHCP issues de la RFC3993 à des
labels.
.TP
.B --dhcp-proxy[=<adresse ip>]......
Un agent relai DHCP normal est uniquement utilisé pour faire suivre les
éléments initiaux de l'interaction avec le serveur DHCP. Une fois que le
client est configuré, il communique directement avec le serveur. Cela n'est pas
souhaitable si le relais rajoute des informations supplémentaires aux paquets
DHCP, telles que celles utilisées dans
(IPv4 seulement) Un agent relai DHCP normal est uniquement utilisé pour faire
suivre les éléments initiaux de l'interaction avec le serveur DHCP. Une fois
que le client est configuré, il communique directement avec le serveur. Cela
n'est pas souhaitable si le relais rajoute des informations supplémentaires
aux paquets DHCP, telles que celles utilisées dans
.B dhcp-circuitid
et
.B dhcp-remoteid.
@@ -922,33 +1093,54 @@ configuration dhcp-host de Dnsmasq, ainsi que le contenu des fichiers /etc/hosts
et /etc/ethers.
.TP
.B --dhcp-generate-names=tag:<label>[,tag:<label>]
Générer un nom pour les clients DHCP qui autrement n'en aurait pas, en
utilisant l'adresse MAC sous sa forme hexadécimale, séparée par des tirets.
(IPv4 seulement) Générer un nom pour les clients DHCP qui autrement n'en aurait
pas, en utilisant l'adresse MAC sous sa forme hexadécimale, séparée par des
tirets.
Noter que si un hôte fourni un nom, celui-ci sera utilisé de préférence au nom
autogénéré, à moins que
.B --dhcp-ignore-names
ne soit positionné.
.TP
.B --dhcp-broadcast=[tag:<label>[,tag:<label>]]
Lorsque tous les labels fournis dans l'option sont présents, toujours utiliser
le broadcast pour communiquer avec l'hôte lorsque celui-ci n'est
pas configuré. Il est possible de ne spécifier aucun label, auquel cas cette
option s'applique inconditionnellement. La plupart des clients DHCP nécessitant une réponse par le biais
d'un broadcast activent une option dans leur requête, ce qui fait que cela
se fait automatiquement, mais ce n'est pas la cas de certains vieux clients BOOTP.
(IPv4 seulement) Lorsque tous les labels fournis dans l'option sont présents,
toujours utiliser le broadcast pour communiquer avec l'hôte lorsque celui-ci
n'est pas configuré. Il est possible de ne spécifier aucun label, auquel cas
cette option s'applique inconditionnellement. La plupart des clients DHCP
nécessitant une réponse par le biais d'un broadcast activent une option dans
leur requête, ce qui fait que cela se fait automatiquement, mais ce n'est pas
le cas de certains vieux clients BOOTP.
.TP
.B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>]]
Spécifie les options BOOTP devant être retournées par le serveur DHCP. Le nom de
serveur ainsi que l'adresse sont optionnels : s'ils ne sont pas fournis, le nom
est laissé vide et l'adresse fournie est celle de la machine sur laquelle
s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
.B \-M, --dhcp-boot=[tag:<label>,]<nom de fichier>,[<nom de serveur>[,<adresse de serveur>|<nom du serveur tftp>]]
(IPv4 seulement) Spécifie les options BOOTP devant être retournées par le
serveur DHCP. Le nom de serveur ainsi que l'adresse sont optionnels : s'ils
ne sont pas fournis, le nom est laissé vide et l'adresse fournie est celle de
la machine sur laquelle s'exécute Dnsmasq. Si Dnsmasq founit un service TFTP (voir
.B --enable-tftp
), alors seul un nom de fichier est requis ici pour permettre un démarrage par
le réseau.
Si d'éventuels labels sont fournis, ils doivent coïncider avec
ceux du client pour que cet élement de configuration lui soit envoyé.
Une adresse de serveur TFTP peut être spécifiée à la place de l'adresse IP,
sous la forme d'un nom de domaine qui sera cherché dans le fichier /etc/hosts.
Ce nom peut être associé dans /etc/hosts avec plusieurs adresses IP, auquel cas
celles-ci seront utilisées tour à tour (algorithme round-robin).
Cela peut-être utiliser pour équilibrer la charge tftp sur plusieurs serveurs.
.TP
.B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>]
.B --dhcp-sequential-ip
Dnsmasq est conçu pour choisir l'adresse IP des clients DHCP en utilisant
un hachage de l'adresse MAC du client. Cela permet en général à l'adresse
IP du client de rester stable au fil du temps, même lorsque le client laisse
expirer son bail DHCP de temps en temps. Dans ce mode de fonctionnement par
défaut, les adresses IP sont distribuées de façon pseudo-aléatoire dans la
totalité de la plage d'adresses utilisable. Il existe des circonstances (par
exemples pour du déploiement de serveur) où il est plus pratique d'allouer les
adresses IP de manière séquentielle, en commençant par la plus petite adresse
disponible, et c'est ce mode de fonctionnement qui est permis par cette option.
Veuillez noter que dans ce mode séquentiel, les clients qui laissent expirer
leur bail ont beaucoup plus de chance de voir leur adresse IP changer, aussi
cette option ne devrait pas être utilisée dans un cas général.
.TP
.B --pxe-service=[tag:<label>,]<CSA>,<entrée de menu>[,<nom de fichier>|<type de service de démarrage>][,<adresse de serveur>|<nom de serveur>]
La plupart des ROMS de démarrage PXE ne permettent au système PXE que la simple
obtention d'une adresse IP, le téléchargement du fichier spécifié dans
.B dhcp-boot
@@ -966,17 +1158,20 @@ client PXE qu'il faut télécharger ce fichier via TFTP, soit depuis ce serveur
(l'option
.B enable-tftp
doit être spécifiée pour que cela marche), soit depuis un autre serveur TFTP
si une adresse de serveur est fournie.
si une adresse ou un nom de serveur est fournie.
Veuillez noter que le suffixe de "couche" (en principe ".0") est fourni par PXE
et ne doit pas être rajouté au nom de fichier. Si une valeur numérique entière
est fournir pour le type de démarrage, en remplacement du nom de fichier, le
client PXE devra chercher un service de démarrage de ce type sur le réseau.
Cette recherche peut être faite via broadcast ou directement auprès d'un
serveur si son adresse IP est fournie dans l'option.
serveur si son adresse IP ou son nom sont fournis dans l'option.
Si aucun nom de fichier n'est donné ni aucune valeur de type de service de
démarrage n'est fournie (ou qu'une valeur de 0 est donnée pour le type de
service), alors l'entrée de menu provoque l'interruption du démarrage par
le réseau et la poursuite du démarrage sur un média local.
le réseau et la poursuite du démarrage sur un média local. L'adresse de serveur
peut être donnée sous la forme de nom de domaine qui est recherché dans
/etc/hosts. Ce nom peut-être associé à plusieurs adresses IP, qui dans ce cas
sont utilisées à tour de rôle (en "round-robin").
.TP
.B --pxe-prompt=[tag:<label>,]<invite>[,<délai>]
Cette option permet d'afficher une invite à la suite du démarrage PXE. Si un
@@ -1009,38 +1204,39 @@ créant des milliers de baux et utilisant beaucoup de mémoire dans le processus
Dnsmasq.
.TP
.B \-K, --dhcp-authoritative
Cette option doit être donnée lorsque Dnsmasq est le seul serveur DHCP sur le
réseau. Cela change le comportement par défaut qui est celui d'un strict respect
des RFC, afin que les requêtes DHCP pour des baux inconnus par des hôtes
inconnus ne soient pas ignorées. Cela permet à de nouveaux hôtes d'obtenir des
baux sans tenir compte de fastidieuses temporisations ("timeout"). Cela permet
également à Dnsmasq de reconstruire sa base de donnée contenant les baux sans
que les clients n'aient besoin de redemander un bail, si celle-ci est perdue.
(IPv4 seulement) Cette option doit être donnée lorsque Dnsmasq est le seul
serveur DHCP sur le réseau. Cela change le comportement par défaut qui est
celui d'un strict respect des RFC, afin que les requêtes DHCP pour des baux
inconnus par des hôtes inconnus ne soient pas ignorées. Cela permet à de
nouveaux hôtes d'obtenir des baux sans tenir compte de fastidieuses
temporisations ("timeout"). Cela permet également à Dnsmasq de reconstruire
sa base de données contenant les baux sans que les clients n'aient besoin de
redemander un bail, si celle-ci est perdue.
.TP
.B --dhcp-alternate-port[=<port serveur>[,<port client>]]
Change les ports utilisés par défaut pour le DHCP. Si cette option est donnée
toute seule sans arguments, alors change les ports utilisés pour le DHCP
de 67 et 68 respectivement à 1067 et 1068. Si un seul argument est donné, ce
(IPv4 seulement) Change les ports utilisés par défaut pour le DHCP. Si cette
option est donnée seule sans argument, alors change les ports utilisés pour le
DHCP de 67 et 68 respectivement à 1067 et 1068. Si un seul argument est donné, ce
numéro est utilisé pour le port serveur et ce numéro plus 1 est utilisé pour le
port client. Enfin, en fournissant deux numéros de ports, il est possible de
spécifier arbitrairement 2 ports à la fois pour le serveur et pour le client DHCP.
.TP
.B \-3, --bootp-dynamic[=<identifiant de réseau>[,<identifiant de réseau>]]
Permet l'allocation dynamique d'adresses IP à des clients BOOTP. Utiliser cette
option avec précaution, une adresse allouée à un client BOOTP étant perpétuelle,
et de fait n'est plus disponibles pour d'autres hôtes. Si aucun argument n'est
donné, alors cette option permet une allocation dynamique dans tous les cas. Si
des arguments sont spécifiés, alors l'allocation ne se fait que lorsque tous
les identifiants coïncident. Il est possible de répeter cette option avec
plusieurs jeux d'arguments.
(IPv4 seulement) Permet l'allocation dynamique d'adresses IP à des clients BOOTP.
Utiliser cette option avec précaution, une adresse allouée à un client BOOTP
étant perpétuelle, et de fait n'est plus disponibles pour d'autres hôtes. Si
aucun argument n'est donné, alors cette option permet une allocation dynamique
dans tous les cas. Si des arguments sont spécifiés, alors l'allocation ne se
fait que lorsque tous les identifiants coïncident. Il est possible de répeter
cette option avec plusieurs jeux d'arguments.
.TP
.B \-5, --no-ping
Par défaut, le serveur DHCP tente de s'assurer qu'une adresse n'est pas utilisée
avant de l'allouer à un hôte. Cela est fait en envoyant une requête ICMP de type
"echo request" (aussi connue sous le nom de "ping") à l'adresse en question. Si
le serveur obtient une réponse, alors l'adresse doit déjà être utilisée et une
autre est essayée. Cette option permet de supprimer cette vérification. A
utiliser avec précaution.
(IPv4 seulement) Par défaut, le serveur DHCP tente de s'assurer qu'une adresse
n'est pas utilisée avant de l'allouer à un hôte. Cela est fait en envoyant une
requête ICMP de type "echo request" (aussi connue sous le nom de "ping") à
l'adresse en question. Si le serveur obtient une réponse, alors l'adresse doit
déjà être utilisée et une autre est essayée. Cette option permet de supprimer
cette vérification. A utiliser avec précaution.
.TP
.B --log-dhcp
Traces additionnelles pour le service DHCP : enregistre toutes les options
@@ -1050,18 +1246,30 @@ détermination de celles-ci.
.B \-l, --dhcp-leasefile=<chemin de fichier>
Utilise le fichier dont le chemin est fourni pour stocker les informations de
baux DHCP.
.TP
.TP
.B --dhcp-duid=<ID d'entreprise>,<uid>
(IPv6 seulement) Spécifie le numéro d'UID de serveur persistant que le serveur
DHCPv6 doit utiliser. Cette option n'est normalement pas requise, Dnsmasq
créant un DUID automatiquement lorsque cela est nécessaire. Lorsque cette
option est positionnée, elle fournit à Dnsmasq les données nécessaires à la
création d'un DUID de type DUID-EN. Veuillez noter qu'une fois créé, le DUID
est stocké dans la base des baux, aussi changer entre un DUID créé
automatiquement et un DUID-EN et vice-versa impose de réinitialiser la base de
baux. Le numéro d'ID d'entreprise est assigné par l'IANA, et l'uid est une
chaine hexadécimale unique à chaque serveur.
.TP
.B \-6 --dhcp-script=<chemin de fichier>
Lorsqu'un bail DHCP est créé, ou qu'un ancien est supprimé, le fichier dont le
Lorsqu'un bail DHCP est créé, qu'un ancien est supprimé, ou qu'un transfert
TFTP est terminé, le fichier dont le
chemin est spécifié est exécuté. Le <chemin de fichier> doit être un chemin
absolu, aucune recherche n'est effectuée via la variable d'environnement PATH.
Les arguments fournis à celui-ci sont soit
"add" ("ajouter"), "old" ("ancien") ou "del" ("supprimer"), suivi de l'adresse
MAC de l'hôte puis l'adresse IP et le nom d'hôte si celui-ci est
connu."add" signifie qu'un bail a été créé, "del" signifie qu'il a été supprimé,
"old" notifie que le bail existait au lancement de Dnsmasq, ou un changement
d'adresse MAC ou de nom d'hôte pour un bail existant (ou, dans le cas
leasefile-ro est spécifié, un changement de durée de bail ou d'identifiant
MAC de l'hôte (ou le DUID pour IPv6) puis l'adresse IP et le nom d'hôte si
celui-ci est connu."add" signifie qu'un bail a été créé, "del" signifie qu'il a
été supprimé, "old" notifie que le bail existait au lancement de Dnsmasq, ou un
changement d'adresse MAC ou de nom d'hôte pour un bail existant (ou, dans le cas
leasefile-ro est spécifié, un changement de durée de bail ou d'identifiant
d'hôte). Si l'adresse Mac est d'un type de réseau autre qu'ethernet, il est
nécessaire de la préceder du type de réseau, par exemple "06-01:23:45:67:89:ab"
pour du token ring. Le processus est exécuté en temps que super-utilisateur
@@ -1071,17 +1279,16 @@ pour changer son UID pour celle d'un utilisateur non-privilégié.
L'environnement est hérité de celui de l'invocation du processus Dnsmasq,
auquel se rajoute quelques unes ou toutes les variables décrites ci-dessous :
DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
Pour IPv4 et IPv6 :
DNSMASQ_DOMAIN si le nom de domaine pleinement qualifié de l'hôte est connu, la
part relative au domaine y est stockée.
part relative au domaine y est stockée. (Notez que le nom d'hôte transmis comme
argument au script n'est jamais pleinement qualifié).
Si le client fournit une information de classe de vendeur, un nom d'hôte, ou
des classes d'utilisateur, celles-ci sont fournies dans les
variables DNSMASQ_VENDOR_CLASS et DNSMASQ_USER_CLASS0 à DNSMASQ_USER_CLASSn
et DNSMASQ_SUPPLIED_HOSTNAME respectivement, mais seulement pour les actions
"add" et "old" lorsqu'un hôte reprend un bail existant, ces variables n'étant
pas stockées dans la base de baux de Dnsmasq.
Si le client fournit un nom d'hôte, DNSMASQ_SUPPLIED_HOSTNAME.
Si le client fournit des classes d'utilisateur, DNSMASQ_USER_CLASS0 à
DNSMASQ_USER_CLASSn.
Si Dnsmasq a été compilé avec l'option HAVE_BROKEN_RTC ("horloge RTC
défectueuse"), alors la durée du bail (en secondes) est stockée dans la
@@ -1104,6 +1311,33 @@ relai DHCP pour contacter Dnsmasq, si l'adresse IP du relai est connue.
DNSMASQ_TAGS contient tous les labels fournis pendant la transaction DHCP,
séparés par des espaces.
DNSMASQ_LOG_DHCP est positionné si
.B --log-dhcp
est activé.
Pour IPv4 seulement :
DNSMASQ_CLIENT_ID, si l'hôte a fourni un identifiant de client.
Si le client fournit une information de classe de vendeur, DNSMASQ_VENDOR_CLASS.
Pour IPv6 seulement :
Si le client fournit une classe de vendeur (vendor-class), positionne
DNSMASQ_VENDOR_CLASS_ID avec comme contenu le numéro IANA de l'entreprise pour
la classe, et DNSMASQ_VENDOR_CLASS0..DNSMASQ_VENDOR_CLASSn pour les données.
DNSMASQ_SERVER_DUID contient le DUID du serveur : cette valeur est la même
pour chaque appel au script.
DNSMASQ_IAID contenant l'IAID pour le bail. Si le bail est une allocation
temporaire, cela est préfixé par le caractère 'T'.
A noter que le nom d'hôte fourni, la classe de vendeur ou les données de classe
d'utilisateur sont uniquement fournies pour les actions "add" ou l'action "old"
lorsqu'un hôte reprend un bail existant, puisque ces informations ne sont pas
conservées dans la base de baux de dnsmasq.
Tous les descripteurs de fichiers sont fermés, sauf stdin, stdout et stderr qui
sont ouverts sur /dev/null (sauf en mode déverminage).
@@ -1120,11 +1354,72 @@ Au démarrage de Dnsmasq, le script sera invoqué pour chacun des baux existants
dans le fichier des baux. Le script sera lancé avec l'action "del" pour les
baux expirés, et "old" pour les autres. Lorsque Dnsmasq reçoit un signal HUP,
le script sera invoqué avec une action "old" pour tous les baux existants.
.TP
Il existe deux autres actions pouvant apparaître comme argument au script :
"init" et "tftp". D'autres sont susceptibles d'être rajoutées dans le futur,
aussi les scripts devraient-être écrits de sorte à ignorer les actions
inconnues. "init" est décrite ci-dessous dans
.B --leasefile-ro.
L'action "tftp" est invoquée lorsqu'un transfert de fichier TFTP s'est
terminé. Ses arguments sont la taille du fichier en octets, l'adresse à
laquelle le fichier a été envoyé, ainsi que le chemin complet du fichier.
.TP
.B --dhcp-luascript=<chemin>
Spécifie un script écrit en Lua, devant être exécuté lorsque des baux sont
créés, détruits ou modifiés. Pour utiliser cette option, dnsmasq doit être
compilé avec avec le support de Lua. L'interpréteur Lua est initialisé une
seule fois, lorsque dnsmasq démarre, ce qui fait que les variables globales
persistent entre les évênements liés aux baux. Le code Lua doit définir une
fonction
.B lease
et peut fournir des fonctions
.B init
et
.B shutdown
qui sont appellées, sans arguments, lorsque dnsmasq démarre ou s'arrête.
Il peut également fournir une fonction
.B tftp.
La fonction
.B lease
reçoit les informations détaillées dans
.B --dhcp-script.
Il reçoit deux arguments. Le premier spécifie l'action, qui est une chaîne de
caractères contenant les valeurs "add" (ajout), "old" (réactivation d'un bail
existant) ou "del" (suppression). Le deuxième est une table contenant des
paires de valeurs de labels. Les labels correspondent pour l'essentiel aux
valeurs d'environnement détaillées ci-dessus, ainsi le label "domain" (domaine)
contient les mêmes données que la variable d'environnement DNSMASQ_DOMAIN. Il
existe quelques labels supplémentaires contenant les données fournies comme
arguments à
.B --dhcp-script.
Ces labels sont
.B mac_address, ip_address
(pour respectivement l'adresse MAC et l'adresse IP)
et
.B hostname
(le nom d'hôte) dans le cas d'IPv4, et
.B client_duid, ip_address
(valeur DUID du client et adresse IP respectivement)
ainsi que
.B hostname
(le nom d'hôte) dans le cas d'IPv6.
La fonction
.B tftp
est appelée de la même façon que la fonction "lease", et la table contient les
labels
.B destination_address,
.B file_name
et
.B file_size
(respectivement "adresse de destination", "nom de fichier" et "taille de fichier").
.TP
.B --dhcp-scriptuser
Spécifie l'utilisateur sous lequel le script lease-change doit être exécuté. La
valeur par défaut correspond à l'utilisateur root mais peut-être changée par le
biais de cette option.
Spécifie l'utilisateur sous lequel le script shell lease-change ou le script
doivent être exécutés. La valeur par défaut correspond à l'utilisateur root
mais peut-être changée par le biais de cette option.
.TP
.B \-9, --leasefile-ro
Supprimer complètement l'usage du fichier servant de base de donnée pour les
@@ -1151,7 +1446,7 @@ nécessaire lors de l'utilisation de pont ethernet "ancien mode" sur plate-forme
BSD, puisque dans ce cas les paquets arrivent sur des interfaces "tap" n'ont
pas d'adresse IP.
.TP
.B \-s, --domain=<domaine>[,<gamme d'adresses>]
.B \-s, --domain=<domaine>[,<gamme d'adresses>[,local]]
Spécifie le domaine du serveur DHCP. Le domaine peut être donné de manière
inconditionnelle (sans spécifier de gamme d'adresses IP) ou pour des gammes
d'adresses IP limitées. Cela a deux effets; tout d'abord, le
@@ -1173,11 +1468,23 @@ et avoir une machine dont le nom DHCP serait "laptop". L'adresse IP de cette
machine sera disponible à la fois pour "laptop" et "laptop.thekelleys.org.uk".
Si la valeur fournie pour <domaine> est "#", alors le nom de domaine est
positionné à la première valeur de la directive "search" du fichier
/etc/resolv.conf (ou équivalent). La gamme d'adresses peut être de la forme
/etc/resolv.conf (ou équivalent).
La gamme d'adresses peut être de la forme
<adresse ip>,<adresse ip> ou <adresse ip>/<masque de réseau> voire une simple
<adresse ip>. Voir
.B --dhcp-fqdn
qui peut changer le comportement de dnsmasq relatif aux domaines.
Si la gamme d'adresse est fournie sous la forme
<adresse ip>/<taille de réseau>, alors le drapeau "local" peut-être rajouté
qui a pour effect d'ajouter --local-declarations aux requêtes DNS directes et
inverses. C-à-d
.B --domain=thekelleys.org.uk,192.168.0.0/24,local
est identique à
.B --domain=thekelleys.org.uk,192.168.0.0/24
--local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
La taille de réseau doit-être de 8, 16 ou 24 pour être valide.
.TP
.B --dhcp-fqdn
Dans le mode par défaut, dnsmasq insère les noms non-qualifiés des clients
@@ -1197,12 +1504,42 @@ sans gamme d'adresses de spécifié lorsque l'option
.B --dhcp-fqdn
est configurée.
.TP
.B --enable-tftp[=<interface>]
.B --dhcp-client-update
Normalement, lorsque dnsmasq fournit un bail DHCP, il positionne un label
dans l'option FQDN pour indiquer au client qu'il ne doit pas tenter de faire
une mise à jour DDNS avec son nom et son adresse IP. Ceci parce que la paire
Nom-IP est rajoutée automatiquement dans la partie DNS de dnsmasq. Cette option
inhibe ce comportement ce qui est utile, par exemple, pour permettre aux clients
Windows de la mise à jour de serveurs Active Directory. Voir la RFC 4702 pour
plus de détails.
.TP
.B --enable-ra
Active la fonctionalité d'annonces routeurs IPv6 ("IPv6 Router Advertisement").
DHCPv6 ne gère pas la configuration complète du réseau de la même façon que
DHCPv4. La découverte de routeurs et la découverte (éventuelle) de préfixes pour
la création autonome d'adresse sont gérées par un protocole différent.
Lorsque DHCP est utilisé, seul un sous-ensemble de tout ceci est nécessaire et
dnsmasq est à même de le gérer, en utilisant la configuration DHCP présente pour
fournir la majorité des données. Lorsque les annonces routeurs (RA pour "Router
Advertisement") sont activées, dnsmasq va annoncer un préfixe pour chaque
dhcp-range et, par défaut, fournir comme valeur de routeur et de DNS récursif
la valeur d'adresse link-local appropriée parmi celles de la machine sur
laquelle tourne dnsmasq.
Par défaut, les bits "managed address" sont positionnés, et le bit "use SLAAC"
("utiliser SLAAC") est réinitialisé. Cela peut-être changé pour des
sous-réseaux donnés par le biais du mot clef de mode décris dans
.B --dhcp-range.
Les paramètres DNS du RFC6106 sont inclus dans les annonces. Par défaut,
l'adresse link-local appropriée parmi celles de la machine sur laquelle tourne
dnsmasq est spécifiée comme DNS récursif. Si elles sont fournies, les
options dns-server et domain-search sont utilisées respectivement pour RDNSS et
DNSSL.
.TP
.B --enable-tftp
Active la fonction serveur TFTP. Celui-ci est de manière délibérée limité aux
fonctions nécessaires au démarrage par le réseau ("net-boot") d'un client. Seul
un accès en lecture est possible; les extensions tsize et blksize sont supportées
(tsize est seulement supporté en mode octet). Voir dans la section NOTES les
informations relatives à la spécification de l'interface.
(tsize est seulement supporté en mode octet).
.TP
.B --tftp-root=<répertoire>[,<interface>]
Les fichiers à fournir dans les transferts TFTP seront cherchés en prenant le
@@ -1240,6 +1577,13 @@ Sans cela, en effet, l'accès de tous les fichiers du serveur pour lequel le
droit de lecture pour tout le monde est positionné ("world-readable") devient
possible par n'importe quel hôte sur le réseau.
.TP
.B --tftp-lowercase
Converti les noms de fichiers des requêtes TFTP en minuscules. Cela est utile
pour les requêtes effectuées depuis les machines Windows, dont les systèmes
de fichiers sont insensibles à la casse et pour lesquels la détermination
de la casse est parfois un peu aléatoire. A noter que le serveur tftp de
dnsmasq converti systématiquement les "\\" en "/" dans les noms de fichiers.
.TP
.B --tftp-max=<connexions>
Définit le nombre maximum de connexions TFTP simultanées autorisées. La valeur
par défaut est de 50. Lorsqu'un grand nombre de connexions TFTP est spécifié,
@@ -1284,7 +1628,9 @@ est limitée par la taille de la plage de ports ainsi définie.
.B \-C, --conf-file=<fichier>
Spécifie un fichier de configuration différent. L'option "conf-file" est
également autorisée dans des fichiers de configuration, ce qui permet
l'inclusion de multiples fichiers de configuration.
l'inclusion de multiples fichiers de configuration. L'utilisation de "-" comme
nom de fichier permet la lecture par dnsmasq de sa configuration sur l'entrée standard
stdin.
.TP
.B \-7, --conf-dir=<répertoire>[,<extension de fichier>...]
Lis tous les fichiers du répertoire spécifié et les traite comme des fichiers de
@@ -1370,7 +1716,6 @@ Dans le cas de l'utilisation du logiciel logrotate, les options requises sont
.B create
et
.B delaycompress.
.PP
Dnsmasq est un logiciel de transmission de requêtes DNS : il n'est pas capable
@@ -1462,6 +1807,17 @@ labels définis pour l'hôte considéré. (dans le cas de l'utilisation dans une
ligne de commande au lieu d'un fichier de configuration, ne pas oublier
d'échapper le caractère !, qui est un méta-caractère d'interpréteur de commande
shell).
Lors de la sélection d'une option, une étiquette spécifiée par dhcp-range
passe après les autres étiquettes, ce qui permet de facilement remplacer des
option génériques pour des hôtes spécifiques, ainsi :
.B dhcp-range=set:interface1,......
.B dhcp-host=set:monhote,.....
.B dhcp-option=tag:interface1,option:nis-domain,"domaine1"
.B dhcp-option=tag:monhote,option:nis-domain,"domaine2"
va positionner l'option NIS-domain à domaine1 pour les hôtes dans la plage
d'adresse, sauf pour monhote pour lequel cette valeur sera domaine2.
.PP
Veuillez noter que pour
.B dhcp-range
@@ -1491,51 +1847,6 @@ supprime la nécessité des associations statiques). Le paramètre
que le label "bootp", permettant un certain contrôle sur les options retournées
aux différentes classes d'hôtes.
Il est possible de spécifier un nom d'interface à
.B dhcp-range
sous la forme "interface:<nom d'interface>". La sémantique est comme suit :
Pour le DHCP, s'il existe une autre valeur de dhcp-range pour laquelle
_aucun_ nom d'interface n'est donné, alors le nom d'interface est ignoré
et dnsmasq se comporte comme si la partie spécifiant l'interface n'existait
pas, sinon le service DHCP n'est fourni qu'aux interfaces mentionnées dans
les déclarations dhcp-range. Pour le DNS, si il n'y a pas d'option
.B --interface
ou
.B --listen-address
, alors le comportement n'est pas impacté par la spécification d'interface. Si
l'une ou l'autre de ces options est présente, alors les interfaces mentionnées
dans les plages d'adresses dhcp-range sont rajoutées à la liste de celles
où le service DNS est assuré.
De manière similaire,
.B enable-tftp
peut prendre un nom d'interface, ce qui active le TFTP pour cette seule
interface, en ignorant les options
.B --interface
ou
.B --listen-address
De plus,
.B --tftp-secure
,
.B --tftp-unique-root
et
.B --tftp-no-blocksize
sont ignorées pour les requêtes sur de telles interfaces. (une directive
.B --tftp-root
donnant le chemin de la racine et une interface doit-être fournie).
Ces règles peuvent paraître étrange à première vue, mais elles permettent
d'ajouter à la configuration de dnsmasq des lignes de configuration de la
forme "dhcp-range=interface:virt0,192.168.0.4,192.168.0.200" afin de fournir
un service DHCP et DNS sur cette interface, sans pour autant affecter les
services fournis sur d'autres interfaces, malgré l'absence de paramètres
"interface=<interface>" sur les autres lignes de configuration.
"enable-tftp=virt0" et "tftp-root=<root>,virt0" effectuent la même chose pour
TFTP.
L'idée de tout cela est de permettre l'addition de telles lignes
automatiquement par libvirt ou un système équivalent, sans perturbation
d'une configuration manuelle existant par ailleurs.
.SH CODES DE SORTIE
.PP
0 - Dnsmasq s'est correctement lancé en tâche de fond, ou alors s'est
@@ -1621,6 +1932,9 @@ et assume de ce fait qu'il s'agit de la valeur par défaut du système.
.IR /etc/dnsmasq.conf
.IR /usr/local/etc/dnsmasq.conf
.IR /var/run/dnsmasq/resolv.conf
.IR /etc/ppp/resolv.conf
.IR /etc/dhcpc/resolv.conf
.IR /etc/resolv.conf

1152
po/de.po

File diff suppressed because it is too large Load Diff

1271
po/es.po

File diff suppressed because it is too large Load Diff

1329
po/fi.po

File diff suppressed because it is too large Load Diff

1221
po/fr.po

File diff suppressed because it is too large Load Diff

1267
po/id.po

File diff suppressed because it is too large Load Diff

1329
po/it.po

File diff suppressed because it is too large Load Diff

1244
po/no.po

File diff suppressed because it is too large Load Diff

1753
po/pl.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1244
po/ro.po

File diff suppressed because it is too large Load Diff

View File

@@ -221,7 +221,7 @@ triggering dial-on-demand internet links.
Sending SIGHUP to the dnsmasq process will cause it to empty its cache and
then re-load <TT>/etc/hosts</TT> and <TT>/etc/resolv.conf</TT>.
<P> Sending SIGUSR1 (killall -10 dnsmasq) to the dnsmasq process will
cause to to write cache usage statisticss to the log, typically
cause to write cache usage statisticss to the log, typically
<TT>/var/log/syslog</TT> or <TT>/var/log/messages</TT>.
<P> The <TT>log-queries</TT> option tells dnsmasq to verbosely log the queries
it is handling and causes SIGUSR1 to trigger a complete dump of the

229
src/bpf.c
View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -17,112 +17,158 @@
#include "dnsmasq.h"
#if defined(HAVE_BSD_NETWORK) || defined(HAVE_SOLARIS_NETWORK)
#include <ifaddrs.h>
static struct iovec ifconf = {
.iov_base = NULL,
.iov_len = 0
};
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
#include <sys/param.h>
#include <sys/sysctl.h>
#include <net/route.h>
#include <net/if_dl.h>
#include <netinet/if_ether.h>
static struct iovec ifreq = {
.iov_base = NULL,
.iov_len = 0
};
#ifndef SA_SIZE
#define SA_SIZE(sa) \
( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
sizeof(long) : \
1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(long) - 1) ) )
#endif
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
int arp_enumerate(void *parm, int (*callback)())
{
char *ptr;
struct ifreq *ifr;
struct ifconf ifc;
int fd, errsav, ret = 0;
int lastlen = 0;
size_t len = 0;
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
int mib[6];
size_t needed;
char *next;
struct rt_msghdr *rtm;
struct sockaddr_inarp *sin2;
struct sockaddr_dl *sdl;
struct iovec buff;
int rc;
buff.iov_base = NULL;
buff.iov_len = 0;
mib[0] = CTL_NET;
mib[1] = PF_ROUTE;
mib[2] = 0;
mib[3] = AF_INET;
mib[4] = NET_RT_FLAGS;
#ifdef RTF_LLINFO
mib[5] = RTF_LLINFO;
#else
mib[5] = 0;
#endif
if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1 || needed == 0)
return 0;
while (1)
{
if (!expand_buf(&buff, needed))
return 0;
if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
errno != ENOMEM)
break;
needed += needed / 8;
}
if (rc == -1)
return 0;
while(1)
for (next = buff.iov_base ; next < (char *)buff.iov_base + needed; next += rtm->rtm_msglen)
{
len += 10*sizeof(struct ifreq);
if (!expand_buf(&ifconf, len))
goto err;
ifc.ifc_len = len;
ifc.ifc_buf = ifconf.iov_base;
if (ioctl(fd, SIOCGIFCONF, &ifc) == -1)
{
if (errno != EINVAL || lastlen != 0)
goto err;
}
else
{
if (ifc.ifc_len == lastlen)
break; /* got a big enough buffer now */
lastlen = ifc.ifc_len;
}
rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
if (!(*callback)(AF_INET, &sin2->sin_addr, LLADDR(sdl), sdl->sdl_alen, parm))
return 0;
}
for (ptr = ifc.ifc_buf; ptr < (char *)(ifc.ifc_buf + ifc.ifc_len); ptr += len)
return 1;
}
#endif
int iface_enumerate(int family, void *parm, int (*callback)())
{
struct ifaddrs *head, *addrs;
int errsav, ret = 0;
if (family == AF_UNSPEC)
#if defined(HAVE_BSD_NETWORK) && !defined(__APPLE__)
return arp_enumerate(parm, callback);
#else
return 0; /* need code for Solaris and MacOS*/
#endif
/* AF_LINK doesn't exist in Linux, so we can't use it in our API */
if (family == AF_LOCAL)
family = AF_LINK;
if (getifaddrs(&head) == -1)
return 0;
for (addrs = head; addrs; addrs = addrs->ifa_next)
{
/* subsequent entries may not be aligned, so copy into
an aligned buffer to avoid nasty complaints about
unaligned accesses. */
len = sizeof(struct ifreq);
#ifdef HAVE_SOCKADDR_SA_LEN
ifr = (struct ifreq *)ptr;
if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_ifru))
len = ifr->ifr_addr.sa_len + offsetof(struct ifreq, ifr_ifru);
#endif
if (!expand_buf(&ifreq, len))
goto err;
ifr = (struct ifreq *)ifreq.iov_base;
memcpy(ifr, ptr, len);
if (ifr->ifr_addr.sa_family == AF_INET && ipv4_callback)
if (addrs->ifa_addr->sa_family == family)
{
struct in_addr addr, netmask, broadcast;
broadcast.s_addr = 0;
addr = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
int iface_index = if_nametoindex(addrs->ifa_name);
if (iface_index == 0)
continue;
netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (ioctl(fd, SIOCGIFBRDADDR, ifr) != -1)
broadcast = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
if (!((*ipv4_callback)(addr,
(int)if_nametoindex(ifr->ifr_name),
netmask, broadcast,
parm)))
goto err;
}
#ifdef HAVE_IPV6
else if (ifr->ifr_addr.sa_family == AF_INET6 && ipv6_callback)
{
struct in6_addr *addr = &((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_addr;
/* voodoo to clear interface field in address */
if (!(daemon->options & OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
if (family == AF_INET)
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
struct in_addr addr, netmask, broadcast;
addr = ((struct sockaddr_in *) addrs->ifa_addr)->sin_addr;
netmask = ((struct sockaddr_in *) addrs->ifa_netmask)->sin_addr;
broadcast = ((struct sockaddr_in *) addrs->ifa_broadaddr)->sin_addr;
if (!((*callback)(addr, iface_index, netmask, broadcast, parm)))
goto err;
}
if (!((*ipv6_callback)(addr,
(int)((struct sockaddr_in6 *)&ifr->ifr_addr)->sin6_scope_id,
(int)if_nametoindex(ifr->ifr_name),
parm)))
goto err;
#ifdef HAVE_IPV6
else if (family == AF_INET6)
{
struct in6_addr *addr = &((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_addr;
unsigned char *netmask = (unsigned char *) &((struct sockaddr_in6 *) addrs->ifa_netmask)->sin6_addr;
int scope_id = ((struct sockaddr_in6 *) addrs->ifa_addr)->sin6_scope_id;
int i, j, prefix = 0;
for (i = 0; i < IN6ADDRSZ; i++, prefix += 8)
if (netmask[i] != 0xff)
break;
if (i != IN6ADDRSZ && netmask[i])
for (j = 7; j > 0; j--, prefix++)
if ((netmask[i] & (1 << j)) == 0)
break;
/* voodoo to clear interface field in address */
if (!option_bool(OPT_NOWILD) && IN6_IS_ADDR_LINKLOCAL(addr))
{
addr->s6_addr[2] = 0;
addr->s6_addr[3] = 0;
}
if (!((*callback)(addr, prefix, scope_id, iface_index, 0, parm)))
goto err;
}
#endif
#ifdef HAVE_DHCP6
else if (family == AF_LINK)
{
/* Assume ethernet again here */
struct sockaddr_dl *sdl = (struct sockaddr_dl *) addrs->ifa_addr;
if (sdl->sdl_alen != 0 &&
!((*callback)(iface_index, ARPHRD_ETHER, LLADDR(sdl), sdl->sdl_alen, parm)))
goto err;
}
#endif
}
}
ret = 1;
err:
errsav = errno;
close(fd);
freeifaddrs(head);
errno = errsav;
return ret;
@@ -139,13 +185,10 @@ void init_bpf(void)
while (1)
{
/* useful size which happens to be sufficient */
if (expand_buf(&ifreq, sizeof(struct ifreq)))
{
sprintf(ifreq.iov_base, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(ifreq.iov_base, O_RDWR, 0)) != -1)
return;
}
sprintf(daemon->dhcp_buff, "/dev/bpf%d", i++);
if ((daemon->dhcp_raw_fd = open(daemon->dhcp_buff, O_RDWR, 0)) != -1)
return;
if (errno != EBUSY)
die(_("cannot create DHCP BPF socket: %s"), NULL, EC_BADNET);
}

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,7 +25,9 @@ static int cache_inserted = 0, cache_live_freed = 0, insert_error;
static union bigname *big_free = NULL;
static int bignames_left, hash_size;
static int uid = 0;
static char *addrbuff = NULL;
#ifdef HAVE_DNSSEC
static struct keydata *keyblock_free = NULL;
#endif
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
static const struct {
@@ -75,9 +77,6 @@ void cache_init(void)
struct crec *crecp;
int i;
if (daemon->options & OPT_LOG)
addrbuff = safe_malloc(ADDRSTRLEN);
bignames_left = daemon->cachesize/10;
if (daemon->cachesize > 0)
@@ -194,6 +193,10 @@ static void cache_free(struct crec *crecp)
big_free = crecp->name.bname;
crecp->flags &= ~F_BIGNAME;
}
#ifdef HAVE_DNSSEC
else if (crecp->flags & (F_DNSKEY | F_DS))
keydata_free(crecp->addr.key.keydata);
#endif
}
/* insert a new cache entry at the head of the list (youngest entry) */
@@ -226,7 +229,7 @@ char *cache_get_name(struct crec *crecp)
{
if (crecp->flags & F_BIGNAME)
return crecp->name.bname->name;
else if (crecp->flags & (F_DHCP | F_CONFIG))
else if (crecp->flags & F_NAMEP)
return crecp->name.namep;
return crecp->name.sname;
@@ -237,7 +240,11 @@ static int is_outdated_cname_pointer(struct crec *crecp)
if (!(crecp->flags & F_CNAME))
return 0;
if (crecp->addr.cname.cache && crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
/* NB. record may be reused as DS or DNSKEY, where uid is
overloaded for something completely different */
if (crecp->addr.cname.cache &&
(crecp->addr.cname.cache->flags & (F_IPV4 | F_IPV6 | F_CNAME)) &&
crecp->addr.cname.uid == crecp->addr.cname.cache->uid)
return 0;
return 1;
@@ -284,7 +291,7 @@ static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsign
}
}
else if ((crecp->flags & F_FORWARD) &&
((flags & crecp->flags & (F_IPV4 | F_IPV6)) || ((crecp->flags | flags) & F_CNAME)) &&
((flags & crecp->flags & F_TYPE) || ((crecp->flags | flags) & F_CNAME)) &&
hostname_isequal(cache_get_name(crecp), name))
{
if (crecp->flags & (F_HOSTS | F_DHCP))
@@ -364,10 +371,9 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE;
int free_avail = 0;
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* CONFIG bit means something else when stored in cache entries */
flags &= ~F_CONFIG;
/* Don't log keys */
if (flags & (F_IPV4 | F_IPV6))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */
if (insert_error)
@@ -459,9 +465,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
if (addr)
new->addr.addr = *addr;
else
new->addr.cname.cache = NULL;
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
@@ -503,7 +507,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
/* first search, look for relevant entries and push to top of list
also free anything which has expired */
struct crec *next, **up, **insert = NULL, **chainp = &ans;
int ins_flags = 0;
unsigned short ins_flags = 0;
for (up = hash_bucket(name), crecp = *up; crecp; crecp = next)
{
@@ -641,12 +645,30 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
return NULL;
}
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
unsigned short flags, int index, int addr_dup)
static void add_hosts_cname(struct crec *target)
{
struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0;
struct crec *crec;
struct cname *a;
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache_get_name(target), a->target) &&
(crec = whine_malloc(sizeof(struct crec))))
{
crec->flags = F_FORWARD | F_IMMORTAL | F_NAMEP | F_HOSTS | F_CNAME;
crec->name.namep = a->alias;
crec->addr.cname.cache = target;
crec->addr.cname.uid = target->uid;
cache_hash(crec);
add_hosts_cname(crec); /* handle chains */
}
}
static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen,
int index, struct crec **rhash, int hashsz)
{
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i, nameexists = 0;
unsigned int j;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS))
@@ -660,47 +682,43 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, first we see if the address is the same as
the last one we saw, which eliminates most in the case of an ad-block
file with thousands of entries for the same address.
Then we search and bail at the first matching address that came from
a HOSTS file. Since the first host entry gets reverse, we know
then that it must exist without searching exhaustively for it. */
We do this by steam here, The entries are kept in hash chains, linked
by ->next (which is unused at this point) held in hash buckets in
the array rhash, hashed on address. Note that rhash and the values
in ->next are only valid whilst reading hosts files: the buckets are
then freed, and the ->next pointer used for other things.
Only insert each unique address once into this hashing structure.
This complexity avoids O(n^2) divergent CPU use whilst reading
large (10000 entry) hosts files. */
if (addr_dup)
flags &= ~F_REVERSE;
else
for (i=0; i<hash_size; i++)
/* hash address */
for (j = 0, i = 0; i < addrlen; i++)
j = (j*2 +((unsigned char *)addr)[i]) % hashsz;
for (lookup = rhash[j]; lookup; lookup = lookup->next)
if ((lookup->flags & cache->flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
for (lookup = hash_table[i]; lookup; lookup = lookup->hash_next)
if ((lookup->flags & F_HOSTS) &&
(lookup->flags & flags & (F_IPV4 | F_IPV6)) &&
memcmp(&lookup->addr.addr, addr, addrlen) == 0)
{
flags &= ~F_REVERSE;
break;
}
if (lookup)
break;
cache->flags &= ~F_REVERSE;
break;
}
cache->flags = flags;
/* maintain address hash chain, insert new unique address */
if (!lookup)
{
cache->next = rhash[j];
rhash[j] = cache;
}
cache->uid = index;
memcpy(&cache->addr.addr, addr, addrlen);
memcpy(&cache->addr.addr, addr, addrlen);
cache_hash(cache);
/* don't need to do alias stuff for second and subsequent addresses. */
if (!nameexists)
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache->name.sname, a->target) &&
(lookup = whine_malloc(sizeof(struct crec))))
{
lookup->flags = F_FORWARD | F_IMMORTAL | F_CONFIG | F_HOSTS | F_CNAME;
lookup->name.namep = a->alias;
lookup->addr.cname.cache = cache;
lookup->addr.cname.uid = index;
cache_hash(lookup);
}
add_hosts_cname(cache);
}
static int eatspace(FILE *f)
@@ -750,14 +768,14 @@ static int gettok(FILE *f, char *token)
}
}
static int read_hostsfile(char *filename, int index, int cache_size)
static int read_hostsfile(char *filename, int index, int cache_size, struct crec **rhash, int hashsz)
{
FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 0;
unsigned short flags = 0, saved_flags = 0;
struct all_addr addr, saved_addr;
int atnl, addrlen = 0, addr_dup;
unsigned short flags = 0;
struct all_addr addr;
int atnl, addrlen = 0;
if (!f)
{
@@ -769,28 +787,20 @@ static int read_hostsfile(char *filename, int index, int cache_size)
while ((atnl = gettok(f, token)) != EOF)
{
addr_dup = 0;
lineno++;
#ifdef HAVE_IPV6
if (inet_pton(AF_INET, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr) > 0)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6;
addrlen = IN6ADDRSZ;
domain_suffix = daemon->domain_suffix;
}
#else
if ((addr.addr.addr4.s_addr = inet_addr(token)) != (in_addr_t) -1)
{
flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4;
addrlen = INADDRSZ;
domain_suffix = get_domain(addr.addr.addr4);
domain_suffix = get_domain6(&addr.addr.addr6);
}
#endif
else
@@ -801,14 +811,6 @@ static int read_hostsfile(char *filename, int index, int cache_size)
continue;
}
if (saved_flags == flags && memcmp(&addr, &saved_addr, addrlen) == 0)
addr_dup = 1;
else
{
saved_flags = flags;
saved_addr = addr;
}
addr_count++;
/* rehash every 1000 names. */
@@ -832,21 +834,22 @@ static int read_hostsfile(char *filename, int index, int cache_size)
if ((canon = canonicalise(token, &nomem)))
{
/* If set, add a version of the name with a default domain appended */
if ((daemon->options & OPT_EXPAND) && domain_suffix && !fqdn &&
if (option_bool(OPT_EXPAND) && domain_suffix && !fqdn &&
(cache = whine_malloc(sizeof(struct crec) +
strlen(canon)+2+strlen(domain_suffix)-SMALLDNAME)))
{
strcpy(cache->name.sname, canon);
strcat(cache->name.sname, ".");
strcat(cache->name.sname, domain_suffix);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
addr_dup = 1;
cache->flags = flags;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
if ((cache = whine_malloc(sizeof(struct crec) + strlen(canon)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, canon);
add_hosts_entry(cache, &addr, addrlen, flags, index, addr_dup);
cache->flags = flags;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
}
free(canon);
@@ -868,8 +871,10 @@ static int read_hostsfile(char *filename, int index, int cache_size)
void cache_reload(void)
{
struct crec *cache, **up, *tmp;
int i, total_size = daemon->cachesize;
int revhashsz, i, total_size = daemon->cachesize;
struct hostsfile *ah;
struct host_record *hr;
struct name_list *nl;
cache_inserted = cache_live_freed = 0;
@@ -896,110 +901,48 @@ void cache_reload(void)
up = &cache->hash_next;
}
if ((daemon->options & OPT_NO_HOSTS) && !daemon->addn_hosts)
/* borrow the packet buffer for a temporary by-address hash */
memset(daemon->packet, 0, daemon->packet_buff_sz);
revhashsz = daemon->packet_buff_sz / sizeof(struct crec *);
/* we overwrote the buffer... */
daemon->srv_save = NULL;
/* Do host_records in config. */
for (hr = daemon->host_records; hr; hr = hr->next)
for (nl = hr->names; nl; nl = nl->next)
{
if (hr->addr.s_addr != 0 &&
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV4 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr, INADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
}
#ifdef HAVE_IPV6
if (!IN6_IS_ADDR_UNSPECIFIED(&hr->addr6) &&
(cache = whine_malloc(sizeof(struct crec))))
{
cache->name.namep = nl->name;
cache->flags = F_HOSTS | F_IMMORTAL | F_FORWARD | F_REVERSE | F_IPV6 | F_NAMEP | F_CONFIG;
add_hosts_entry(cache, (struct all_addr *)&hr->addr6, IN6ADDRSZ, 0, (struct crec **)daemon->packet, revhashsz);
}
#endif
}
if (option_bool(OPT_NO_HOSTS) && !daemon->addn_hosts)
{
if (daemon->cachesize > 0)
my_syslog(LOG_INFO, _("cleared cache"));
return;
}
if (!(daemon->options & OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size);
for (i = 0, ah = daemon->addn_hosts; ah; ah = ah->next)
{
if (i <= ah->index)
i = ah->index + 1;
if (ah->flags & AH_DIR)
ah->flags |= AH_INACTIVE;
else
ah->flags &= ~AH_INACTIVE;
}
if (!option_bool(OPT_NO_HOSTS))
total_size = read_hostsfile(HOSTSFILE, 0, total_size, (struct crec **)daemon->packet, revhashsz);
daemon->addn_hosts = expand_filelist(daemon->addn_hosts);
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
{
struct stat buf;
if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
{
DIR *dir_stream;
struct dirent *ent;
/* don't read this as a file */
ah->flags |= AH_INACTIVE;
if (!(dir_stream = opendir(ah->fname)))
my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
ah->fname, strerror(errno));
else
{
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lenfile = strlen(ent->d_name);
struct hostsfile *ah1;
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
/* see if we have an existing record.
dir is ah->fname
file is ent->d_name
path to match is ah1->fname */
for (ah1 = daemon->addn_hosts; ah1; ah1 = ah1->next)
{
if (lendir < strlen(ah1->fname) &&
strstr(ah1->fname, ah->fname) == ah1->fname &&
ah1->fname[lendir] == '/' &&
strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
{
ah1->flags &= ~AH_INACTIVE;
break;
}
}
/* make new record */
if (!ah1)
{
if (!(ah1 = whine_malloc(sizeof(struct hostsfile))))
continue;
if (!(path = whine_malloc(lendir + lenfile + 2)))
{
free(ah1);
continue;
}
strcpy(path, ah->fname);
strcat(path, "/");
strcat(path, ent->d_name);
ah1->fname = path;
ah1->index = i++;
ah1->flags = AH_DIR;
ah1->next = daemon->addn_hosts;
daemon->addn_hosts = ah1;
}
/* inactivate record if not regular file */
if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
ah1->flags |= AH_INACTIVE;
}
closedir(dir_stream);
}
}
}
for (ah = daemon->addn_hosts; ah; ah = ah->next)
if (!(ah->flags & AH_INACTIVE))
total_size = read_hostsfile(ah->fname, ah->index, total_size);
total_size = read_hostsfile(ah->fname, ah->index, total_size, (struct crec **)daemon->packet, revhashsz);
}
char *get_domain(struct in_addr addr)
@@ -1007,14 +950,49 @@ char *get_domain(struct in_addr addr)
struct cond_domain *c;
for (c = daemon->cond_domain; c; c = c->next)
if (ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
if (!c->is6 &&
ntohl(addr.s_addr) >= ntohl(c->start.s_addr) &&
ntohl(addr.s_addr) <= ntohl(c->end.s_addr))
return c->domain;
return daemon->domain_suffix;
}
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr)
{
struct cond_domain *c;
u64 addrpart = addr6part(addr);
for (c = daemon->cond_domain; c; c = c->next)
if (c->is6 &&
is_same_net6(addr, &c->start6, 64) &&
addrpart >= addr6part(&c->start6) &&
addrpart <= addr6part(&c->end6))
return c->domain;
return daemon->domain_suffix;
}
#endif
#ifdef HAVE_DHCP
struct in_addr a_record_from_hosts(char *name, time_t now)
{
struct crec *crecp = NULL;
struct in_addr ret;
while ((crecp = cache_find_by_name(crecp, name, now, F_IPV4)))
if (crecp->flags & F_HOSTS)
return *(struct in_addr *)&crecp->addr;
my_syslog(MS_DHCP | LOG_WARNING, _("No IPv4 address found for %s"), name);
ret.s_addr = 0;
return ret;
}
void cache_unhash_dhcp(void)
{
struct crec *cache, **up;
@@ -1032,94 +1010,120 @@ void cache_unhash_dhcp(void)
up = &cache->hash_next;
}
void cache_add_dhcp_entry(char *host_name,
struct in_addr *host_address, time_t ttd)
static void add_dhcp_cname(struct crec *target, time_t ttd)
{
struct crec *crec = NULL, *aliasc;
unsigned short flags = F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
int in_hosts = 0;
struct crec *aliasc;
struct cname *a;
while ((crec = cache_find_by_name(crec, host_name, 0, F_IPV4 | F_CNAME)))
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(cache_get_name(target), a->target))
{
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(sizeof(struct crec));
if (aliasc)
{
aliasc->flags = F_FORWARD | F_NAMEP | F_DHCP | F_CNAME;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.cache = target;
aliasc->addr.cname.uid = target->uid;
cache_hash(aliasc);
add_dhcp_cname(aliasc, ttd);
}
}
}
void cache_add_dhcp_entry(char *host_name, int prot,
struct all_addr *host_address, time_t ttd)
{
struct crec *crec = NULL, *fail_crec = NULL;
unsigned short flags = F_IPV4;
int in_hosts = 0;
size_t addrlen = sizeof(struct in_addr);
#ifdef HAVE_IPV6
if (prot == AF_INET6)
{
flags = F_IPV6;
addrlen = sizeof(struct in6_addr);
}
#endif
inet_ntop(prot, host_address, daemon->addrbuff, ADDRSTRLEN);
while ((crec = cache_find_by_name(crec, host_name, 0, flags | F_CNAME)))
{
/* check all addresses associated with name */
if (crec->flags & F_HOSTS)
{
/* if in hosts, don't need DHCP record */
in_hosts = 1;
if (crec->flags & F_CNAME)
my_syslog(LOG_WARNING,
my_syslog(MS_DHCP | LOG_WARNING,
_("%s is a CNAME, not giving it to the DHCP lease of %s"),
host_name, inet_ntoa(*host_address));
else if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
{
strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
my_syslog(LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
host_name, inet_ntoa(*host_address),
record_source(crec->uid), daemon->namebuff);
}
host_name, daemon->addrbuff);
else if (memcmp(&crec->addr.addr, host_address, addrlen) == 0)
in_hosts = 1;
else
fail_crec = crec;
}
else if (!(crec->flags & F_DHCP))
{
cache_scan_free(host_name, NULL, 0, crec->flags & (F_IPV4 | F_CNAME | F_FORWARD));
cache_scan_free(host_name, NULL, 0, crec->flags & (flags | F_CNAME | F_FORWARD));
/* scan_free deletes all addresses associated with name */
break;
}
}
if (in_hosts)
/* if in hosts, don't need DHCP record */
if (in_hosts)
return;
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
{
if (crec->flags & F_NEG)
cache_scan_free(NULL, (struct all_addr *)host_address, 0, F_IPV4 | F_REVERSE);
else
/* avoid multiple reverse mappings */
flags &= ~F_REVERSE;
}
if ((crec = dhcp_spare))
/* Name in hosts, address doesn't match */
if (fail_crec)
{
inet_ntop(prot, &fail_crec->addr.addr, daemon->namebuff, MAXDNAME);
my_syslog(MS_DHCP | LOG_WARNING,
_("not giving name %s to the DHCP lease of %s because "
"the name exists in %s with address %s"),
host_name, daemon->addrbuff,
record_source(fail_crec->uid), daemon->namebuff);
return;
}
if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, flags)))
{
if (crec->flags & F_NEG)
{
flags |= F_REVERSE;
cache_scan_free(NULL, (struct all_addr *)host_address, 0, flags);
}
}
else
flags |= F_REVERSE;
if ((crec = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
crec = whine_malloc(sizeof(struct crec));
if (crec) /* malloc may fail */
{
crec->flags = flags;
crec->flags = flags | F_NAMEP | F_DHCP | F_FORWARD;
if (ttd == 0)
crec->flags |= F_IMMORTAL;
else
crec->ttd = ttd;
crec->addr.addr.addr.addr4 = *host_address;
crec->addr.addr = *host_address;
crec->name.namep = host_name;
crec->uid = uid++;
cache_hash(crec);
for (a = daemon->cnames; a; a = a->next)
if (hostname_isequal(host_name, a->target))
{
if ((aliasc = dhcp_spare))
dhcp_spare = dhcp_spare->next;
else /* need new one */
aliasc = whine_malloc(sizeof(struct crec));
if (aliasc)
{
aliasc->flags = F_FORWARD | F_CONFIG | F_DHCP | F_CNAME;
if (ttd == 0)
aliasc->flags |= F_IMMORTAL;
else
aliasc->ttd = ttd;
aliasc->name.namep = a->alias;
aliasc->addr.cname.cache = crec;
aliasc->addr.cname.uid = crec->uid;
cache_hash(aliasc);
}
}
add_dhcp_cname(crec, ttd);
}
}
#endif
@@ -1135,34 +1139,34 @@ void dump_cache(time_t now)
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
daemon->queries_forwarded, daemon->local_answer);
if (!addrbuff && !(addrbuff = whine_malloc(ADDRSTRLEN)))
return;
/* sum counts from different records for same server */
for (serv = daemon->servers; serv; serv = serv->next)
serv->flags &= ~SERV_COUNTED;
for (serv = daemon->servers; serv; serv = serv->next)
if (!(serv->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)))
if (!(serv->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)))
{
int port;
unsigned int queries = 0, failed_queries = 0;
for (serv1 = serv; serv1; serv1 = serv1->next)
if (!(serv1->flags & (SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED)) && sockaddr_isequal(&serv->addr, &serv1->addr))
if (!(serv1->flags &
(SERV_NO_ADDR | SERV_LITERAL_ADDRESS | SERV_COUNTED | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
sockaddr_isequal(&serv->addr, &serv1->addr))
{
serv1->flags |= SERV_COUNTED;
queries += serv1->queries;
failed_queries += serv1->failed_queries;
}
port = prettyprint_addr(&serv->addr, addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), addrbuff, port, queries, failed_queries);
port = prettyprint_addr(&serv->addr, daemon->addrbuff);
my_syslog(LOG_INFO, _("server %s#%d: queries sent %u, retried or failed %u"), daemon->addrbuff, port, queries, failed_queries);
}
if ((daemon->options & (OPT_DEBUG | OPT_LOG)))
if (option_bool(OPT_DEBUG) || option_bool(OPT_LOG))
{
struct crec *cache ;
int i;
my_syslog(LOG_DEBUG, "Host Address Flags Expires");
my_syslog(LOG_INFO, "Host Address Flags Expires");
for (i=0; i<hash_size; i++)
for (cache = hash_table[i]; cache; cache = cache->hash_next)
@@ -1177,22 +1181,35 @@ void dump_cache(time_t now)
if (!is_outdated_cname_pointer(cache))
a = cache_get_name(cache->addr.cname.cache);
}
#ifdef HAVE_IPV6
#ifdef HAVE_DNSSEC
else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%3u %u", cache->addr.key.algo, cache->uid);
}
else if (cache->flags & F_DS)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u %u", cache->addr.key.flags_or_keyid,
cache->addr.key.algo, cache->addr.key.digest, cache->uid);
}
#endif
else
{
a = addrbuff;
a = daemon->addrbuff;
if (cache->flags & F_IPV4)
inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
inet_ntop(AF_INET, &cache->addr.addr, a, ADDRSTRLEN);
#ifdef HAVE_IPV6
else if (cache->flags & F_IPV6)
inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
}
#else
else
a = inet_ntoa(cache->addr.addr.addr.addr4);
inet_ntop(AF_INET6, &cache->addr.addr, a, ADDRSTRLEN);
#endif
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s ", a,
}
p += sprintf(p, "%-30.30s %s%s%s%s%s%s%s%s%s%s%s%s%s ", a,
cache->flags & F_IPV4 ? "4" : "",
cache->flags & F_IPV6 ? "6" : "",
cache->flags & F_DNSKEY ? "K" : "",
cache->flags & F_DS ? "S" : "",
cache->flags & F_CNAME ? "C" : "",
cache->flags & F_FORWARD ? "F" : " ",
cache->flags & F_REVERSE ? "R" : " ",
@@ -1200,7 +1217,8 @@ void dump_cache(time_t now)
cache->flags & F_DHCP ? "D" : " ",
cache->flags & F_NEG ? "N" : " ",
cache->flags & F_NXDOMAIN ? "X" : " ",
cache->flags & F_HOSTS ? "H" : " ");
cache->flags & F_HOSTS ? "H" : " ",
cache->flags & F_DNSSECOK ? "V" : " ");
#ifdef HAVE_BROKEN_RTC
p += sprintf(p, "%lu", cache->flags & F_IMMORTAL ? 0: (unsigned long)(cache->ttd - now));
#else
@@ -1208,7 +1226,7 @@ void dump_cache(time_t now)
/* ctime includes trailing \n - eat it */
*(p-1) = 0;
#endif
my_syslog(LOG_DEBUG, daemon->namebuff);
my_syslog(LOG_INFO, daemon->namebuff);
}
}
}
@@ -1237,28 +1255,28 @@ void querystr(char *str, unsigned short type)
sprintf(str,"query[%s]", typestr[i].name);
}
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg)
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg)
{
char *source, *dest = addrbuff;
char *source, *dest = daemon->addrbuff;
char *verb = "is";
if (!(daemon->options & OPT_LOG))
if (!option_bool(OPT_LOG))
return;
if (addr)
{
#ifdef HAVE_IPV6
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
addr, addrbuff, ADDRSTRLEN);
addr, daemon->addrbuff, ADDRSTRLEN);
#else
strncpy(addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
strncpy(daemon->addrbuff, inet_ntoa(addr->addr.addr4), ADDRSTRLEN);
#endif
}
if (flags & F_REVERSE)
{
dest = name;
name = addrbuff;
name = daemon->addrbuff;
}
if (flags & F_NEG)
@@ -1283,13 +1301,9 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, char *ar
}
}
else if (flags & F_CNAME)
{
/* nasty abuse of NXDOMAIN and CNAME flags */
if (flags & F_NXDOMAIN)
dest = arg;
else
dest = "<CNAME>";
}
dest = "<CNAME>";
else if (flags & F_RRNAME)
dest = arg;
if (flags & F_CONFIG)
source = "config";
@@ -1315,6 +1329,53 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, char *ar
if (strlen(name) == 0)
name = ".";
my_syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, dest);
my_syslog(LOG_INFO, "%s %s %s %s", source, name, verb, dest);
}
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len)
{
struct keydata *block, *ret = NULL;
struct keydata **prev = &ret;
while (len > 0)
{
if (keyblock_free)
{
block = keyblock_free;
keyblock_free = block->next;
}
else
block = whine_malloc(sizeof(struct keydata));
if (!block)
{
/* failed to alloc, free partial chain */
keydata_free(ret);
return NULL;
}
memcpy(block->key, data, len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len);
data += KEYBLOCK_LEN;
len -= KEYBLOCK_LEN;
*prev = block;
prev = &block->next;
block->next = NULL;
}
return ret;
}
void keydata_free(struct keydata *blocks)
{
struct keydata *tmp;
if (blocks)
{
for (tmp = blocks; tmp->next; tmp = tmp->next);
tmp->next = keyblock_free;
keyblock_free = blocks;
}
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,15 +14,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define VERSION "2.55"
#define FTABSIZ 150 /* max number of outstanding requests (default) */
#define MAX_PROCS 20 /* max no children for TCP requests */
#define CHILD_LIFETIME 150 /* secs 'till terminated (RFC1035 suggests > 120s) */
#define EDNS_PKTSZ 4096 /* default max EDNS.0 UDP packet from RFC5625 */
#define KEYBLOCK_LEN 140 /* choose to mininise fragmentation when storing DNSSEC keys */
#define TIMEOUT 10 /* drop UDP queries after TIMEOUT seconds */
#define FORWARD_TEST 50 /* try all servers every 50 queries */
#define FORWARD_TIME 20 /* or 10 seconds */
#define FORWARD_TIME 20 /* or 20 seconds */
#define RANDOM_SOCKS 64 /* max simultaneous random ports */
#define LEASE_RETRY 60 /* on error, retry writing leasefile after LEASE_RETRY seconds */
#define CACHESIZ 150 /* default cache size */
@@ -34,80 +33,18 @@
#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"
#ifndef LEASEFILE
# if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
# define LEASEFILE "/var/db/dnsmasq.leases"
# elif defined(__sun__) || defined (__sun)
# define LEASEFILE "/var/cache/dnsmasq.leases"
# else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
# endif
#endif
#ifndef CONFFILE
# if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
# else
# define CONFFILE "/etc/dnsmasq.conf"
# endif
#endif
#define DEFLEASE 3600 /* default lease time, 1 hour */
#define CHUSER "nobody"
#define CHGRP "dip"
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#define TFTP_PORT 69
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
#define LOG_MAX 5 /* log-queue length */
#define RANDFILE "/dev/urandom"
#define DAD_WAIT 20 /* retry binding IPv6 sockets for this long */
/* DBUS interface specifics */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq"
#define EDNS0_OPTION_MAC 5 /* dyndns.org temporary assignment */
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
#define DNSMASQ_PATH "/uk/org/thekelleys/dnsmasq"
/* A small collection of RR-types which are missing on some platforms */
#ifndef T_SIG
# define T_SIG 24
#endif
#ifndef T_SRV
# define T_SRV 33
#endif
#ifndef T_OPT
# define T_OPT 41
#endif
#ifndef T_TKEY
# define T_TKEY 249
#endif
#ifndef T_TSIG
# define T_TSIG 250
#endif
/* Follows system specific switches. If you run on a
new system, you may want to edit these.
May replace this with Autoconf one day.
HAVE_LINUX_NETWORK
HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
/* compile-time options: uncomment below to enable or do eg.
make COPTS=-DHAVE_BROKEN_RTC
HAVE_BROKEN_RTC
define this on embedded systems which don't have an RTC
@@ -127,69 +64,124 @@ HAVE_TFTP
define this to get dnsmasq's built-in TFTP server.
HAVE_DHCP
define this to get dnsmasq's DHCP server.
define this to get dnsmasq's DHCPv4 server.
HAVE_DHCP6
define this to get dnsmasq's DHCPv6 server. (implies HAVE_DHCP).
HAVE_SCRIPT
define this to get the ability to call scripts on lease-change
define this to get the ability to call scripts on lease-change.
HAVE_GETOPT_LONG
define this if you have GNU libc or GNU getopt.
HAVE_ARC4RANDOM
define this if you have arc4random() to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_SOCKADDR_SA_LEN
define this if struct sockaddr has sa_len field (*BSD)
HAVE_LUASCRIPT
define this to get the ability to call Lua script on lease-change. (implies HAVE_SCRIPT)
HAVE_DBUS
Define this if you want to link against libdbus, and have dnsmasq
define some methods to allow (re)configuration of the upstream DNS
define this if you want to link against libdbus, and have dnsmasq
support some methods to allow (re)configuration of the upstream DNS
servers via DBus.
NOTES:
For Linux you should define
HAVE_LINUX_NETWORK
HAVE_GETOPT_LONG
you should NOT define
HAVE_ARC4RANDOM
HAVE_SOCKADDR_SA_LEN
HAVE_IDN
define this if you want international domain name support.
NOTE: for backwards compatibility, IDN support is automatically
included when internationalisation support is built, using the
*-i18n makefile targets, even if HAVE_IDN is not explicitly set.
For *BSD systems you should define
HAVE_BSD_NETWORK
HAVE_SOCKADDR_SA_LEN
and you MAY define
HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
HAVE_GETOPT_LONG - NetBSD, later FreeBSD
(FreeBSD and OpenBSD only if you link GNU getopt)
HAVE_CONNTRACK
define this to include code which propogates conntrack marks from
incoming DNS queries to the corresponding upstream queries. This adds
a build-dependency on libnetfilter_conntrack, but the resulting binary will
still run happily on a kernel without conntrack support.
NO_IPV6
NO_TFTP
NO_DHCP
NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
with something like "make COPTS=-DNO_SCRIPT" will do the trick.
LEASEFILE
CONFFILE
RESOLVFILE
the default locations of these files are determined below, but may be overridden
in a build command line using COPTS.
*/
/* platform independent options- uncomment to enable */
/* The default set of options to build. Built with these options, dnsmasq
has no library dependencies other than libc */
#define HAVE_DHCP
#define HAVE_DHCP6
#define HAVE_TFTP
#define HAVE_SCRIPT
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
/* #define HAVE_IDN */
/* #define HAVE_CONNTRACK */
/* Allow TFTP to be disabled with COPTS=-DNO_TFTP */
#ifdef NO_TFTP
#undef HAVE_TFTP
/* Default locations for important system files. */
#ifndef LEASEFILE
# if defined(__FreeBSD__) || defined (__OpenBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
# define LEASEFILE "/var/db/dnsmasq.leases"
# elif defined(__sun__) || defined (__sun)
# define LEASEFILE "/var/cache/dnsmasq.leases"
# elif defined(__ANDROID__)
# define LEASEFILE "/data/misc/dhcp/dnsmasq.leases"
# else
# define LEASEFILE "/var/lib/misc/dnsmasq.leases"
# endif
#endif
/* Allow DHCP to be disabled with COPTS=-DNO_DHCP */
#ifdef NO_DHCP
#undef HAVE_DHCP
#ifndef CONFFILE
# if defined(__FreeBSD__)
# define CONFFILE "/usr/local/etc/dnsmasq.conf"
# else
# define CONFFILE "/etc/dnsmasq.conf"
# endif
#endif
/* Allow scripts to be disabled with COPTS=-DNO_SCRIPT */
#ifdef NO_SCRIPT
#undef HAVE_SCRIPT
#ifndef RESOLVFILE
# if defined(__uClinux__)
# define RESOLVFILE "/etc/config/resolv.conf"
# else
# define RESOLVFILE "/etc/resolv.conf"
# endif
#endif
#ifndef RUNFILE
# if defined(__ANDROID__)
# define RUNFILE "/data/dnsmasq.pid"
# else
# define RUNFILE "/var/run/dnsmasq.pid"
# endif
#endif
/* platform dependent options: these are determined automatically below
/* platform dependent options. */
HAVE_LINUX_NETWORK
HAVE_BSD_NETWORK
HAVE_SOLARIS_NETWORK
define exactly one of these to alter interaction with kernel networking.
HAVE_GETOPT_LONG
defined when GNU-style getopt_long available.
HAVE_ARC4RANDOM
defined if arc4random() available to get better security from DNS spoofs
by using really random ids (OpenBSD)
HAVE_SOCKADDR_SA_LEN
defined if struct sockaddr has sa_len field (*BSD)
*/
/* Must preceed __linux__ since uClinux defines __linux__ too. */
#if defined(__uClinux__)
@@ -265,18 +257,12 @@ NOTES:
#endif
/* Decide if we're going to support IPv6 */
/* IPv6 can be forced off with "make COPTS=-DNO_IPV6" */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY) && !defined(NO_IPV6)
#if defined(INET6_ADDRSTRLEN) && defined(IPV6_V6ONLY)
# 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
@@ -285,8 +271,103 @@ NOTES:
# define ADDRSTRLEN 16 /* 4*3 + 3 dots + NULL */
#endif
/* Can't do scripts without fork */
#ifdef NOFORK
# undef HAVE_SCRIPT
/* rules to implement compile-time option dependencies and
the NO_XXX flags */
#ifdef NO_IPV6
#undef HAVE_IPV6
#endif
#ifdef NO_TFTP
#undef HAVE_TFTP
#endif
#ifdef NO_DHCP
#undef HAVE_DHCP
#undef HAVE_DHCP6
#endif
#if defined(NO_DHCP6) || !defined(HAVE_IPV6)
#undef HAVE_DHCP6
#endif
/* DHCP6 needs DHCP too */
#ifdef HAVE_DHCP6
#define HAVE_DHCP
#endif
#if defined(NO_SCRIPT) || !defined(HAVE_DHCP) || defined(NO_FORK)
#undef HAVE_SCRIPT
#undef HAVE_LUASCRIPT
#endif
/* Must HAVE_SCRIPT to HAVE_LUASCRIPT */
#ifdef HAVE_LUASCRIPT
#define HAVE_SCRIPT
#endif
/* Define a string indicating which options are in use.
DNSMASQP_COMPILE_OPTS is only defined in dnsmasq.c */
#ifdef DNSMASQ_COMPILE_OPTS
static char *compile_opts =
#ifndef HAVE_IPV6
"no-"
#endif
"IPv6 "
#ifndef HAVE_GETOPT_LONG
"no-"
#endif
"GNU-getopt "
#ifdef HAVE_BROKEN_RTC
"no-RTC "
#endif
#ifdef NO_FORK
"no-MMU "
#endif
#ifndef HAVE_DBUS
"no-"
#endif
"DBus "
#ifndef LOCALEDIR
"no-"
#endif
"i18n "
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
"no-"
#endif
"IDN "
#ifndef HAVE_DHCP
"no-"
#endif
"DHCP "
#if defined(HAVE_DHCP)
# if !defined (HAVE_DHCP6)
"no-"
# endif
"DHCPv6 "
# if !defined(HAVE_SCRIPT)
"no-scripts "
# else
# if !defined(HAVE_LUASCRIPT)
"no-"
# endif
"Lua "
# endif
#endif
#ifndef HAVE_TFTP
"no-"
#endif
"TFTP "
#ifndef HAVE_CONNTRACK
"no-"
#endif
"conntrack";
#endif

90
src/conntrack.c Normal file
View File

@@ -0,0 +1,90 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_CONNTRACK
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
static int gotit = 0; /* yuck */
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data);
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr, int istcp, unsigned int *markp)
{
struct nf_conntrack *ct;
struct nfct_handle *h;
gotit = 0;
if ((ct = nfct_new()))
{
nfct_set_attr_u8(ct, ATTR_L4PROTO, istcp ? IPPROTO_TCP : IPPROTO_UDP);
nfct_set_attr_u16(ct, ATTR_PORT_DST, htons(daemon->port));
#ifdef HAVE_IPV6
if (peer_addr->sa.sa_family == AF_INET6)
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET6);
nfct_set_attr(ct, ATTR_IPV6_SRC, peer_addr->in6.sin6_addr.s6_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in6.sin6_port);
nfct_set_attr(ct, ATTR_IPV6_DST, local_addr->addr.addr6.s6_addr);
}
else
#endif
{
nfct_set_attr_u8(ct, ATTR_L3PROTO, AF_INET);
nfct_set_attr_u32(ct, ATTR_IPV4_SRC, peer_addr->in.sin_addr.s_addr);
nfct_set_attr_u16(ct, ATTR_PORT_SRC, peer_addr->in.sin_port);
nfct_set_attr_u32(ct, ATTR_IPV4_DST, local_addr->addr.addr4.s_addr);
}
if ((h = nfct_open(CONNTRACK, 0)))
{
nfct_callback_register(h, NFCT_T_ALL, callback, (void *)markp);
if (nfct_query(h, NFCT_Q_GET, ct) == -1)
{
static int warned = 0;
if (!warned)
{
my_syslog(LOG_ERR, _("Conntrack connection mark retrieval failed: %s"), strerror(errno));
warned = 1;
}
}
nfct_close(h);
}
nfct_destroy(ct);
}
return gotit;
}
static int callback(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data)
{
unsigned int *ret = (unsigned int *)data;
*ret = nfct_get_attr_u32(ct, ATTR_MARK);
(void)type; /* eliminate warning */
gotit = 1;
return NFCT_CB_CONTINUE;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,7 +20,7 @@
#include <dbus/dbus.h>
const char* introspection_xml =
const char* introspection_xml_template =
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
"<node name=\"" DNSMASQ_PATH "\">\n"
@@ -29,7 +29,7 @@ const char* introspection_xml =
" <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
" </method>\n"
" </interface>\n"
" <interface name=\"" DNSMASQ_SERVICE "\">\n"
" <interface name=\"%s\">\n"
" <method name=\"ClearCache\">\n"
" </method>\n"
" <method name=\"GetVersion\">\n"
@@ -56,6 +56,8 @@ const char* introspection_xml =
" </interface>\n"
"</node>\n";
static char *introspection_xml = NULL;
struct watch {
DBusWatch *watch;
struct watch *next;
@@ -266,11 +268,21 @@ DBusHandlerResult message_handler(DBusConnection *connection,
if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
{
DBusMessage *reply = dbus_message_new_method_return(message);
DBusMessage *reply;
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
/* string length: "%s" provides space for termination zero */
if (!introspection_xml &&
(introspection_xml = whine_malloc(strlen(introspection_xml_template) + strlen(daemon->dbus_name))))
sprintf(introspection_xml, introspection_xml_template, daemon->dbus_name);
if (introspection_xml)
{
reply = dbus_message_new_method_return(message);
dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection_xml, DBUS_TYPE_INVALID);
dbus_connection_send (connection, reply, NULL);
dbus_message_unref (reply);
}
}
else if (strcmp(method, "GetVersion") == 0)
{
@@ -315,7 +327,7 @@ char *dbus_init(void)
dbus_connection_set_watch_functions(connection, add_watch, remove_watch,
NULL, NULL, NULL);
dbus_error_init (&dbus_error);
dbus_bus_request_name (connection, DNSMASQ_SERVICE, 0, &dbus_error);
dbus_bus_request_name (connection, daemon->dbus_name, 0, &dbus_error);
if (dbus_error_is_set (&dbus_error))
return (char *)dbus_error.message;
@@ -325,7 +337,7 @@ char *dbus_init(void)
daemon->dbus = connection;
if ((message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, "Up")))
if ((message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, "Up")))
{
dbus_connection_send(connection, message, NULL);
dbus_message_unref(message);
@@ -396,7 +408,7 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
DBusConnection *connection = (DBusConnection *)daemon->dbus;
DBusMessage* message = NULL;
DBusMessageIter args;
char *action_str, *addr, *mac = daemon->namebuff;
char *action_str, *mac = daemon->namebuff;
unsigned char *p;
int i;
@@ -406,10 +418,21 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
if (!hostname)
hostname = "";
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
{
print_mac(mac, lease->clid, lease->clid_len);
inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN);
}
else
#endif
{
p = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len,
lease->hwaddr, lease->clid_len, lease->clid, &i);
print_mac(mac, p, i);
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
}
if (action == ACTION_DEL)
action_str = "DhcpLeaseDeleted";
else if (action == ACTION_ADD)
@@ -419,14 +442,12 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname)
else
return;
addr = inet_ntoa(lease->addr);
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, DNSMASQ_SERVICE, action_str)))
if (!(message = dbus_message_new_signal(DNSMASQ_PATH, daemon->dbus_name, action_str)))
return;
dbus_message_iter_init_append(message, &args);
if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &addr) &&
if (dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &daemon->addrbuff) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &mac) &&
dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &hostname))
dbus_connection_send(connection, message, NULL);

751
src/dhcp-common.c Normal file
View File

@@ -0,0 +1,751 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP
void dhcp_common_init(void)
{
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
/* dhcp_packet is used by v4 and v6, outpacket only by v6
sizeof(struct dhcp_packet) is as good an initial size as any,
even for v6 */
expand_buf(&daemon->dhcp_packet, sizeof(struct dhcp_packet));
#ifdef HAVE_DHCP6
if (daemon->dhcp6)
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
#endif
}
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
{
ssize_t sz;
while (1)
{
msg->msg_flags = 0;
while ((sz = recvmsg(fd, msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
if (sz == -1)
return -1;
if (!(msg->msg_flags & MSG_TRUNC))
break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t)sz == msg->msg_iov->iov_len)
{
if (!expand_buf(msg->msg_iov, sz + 100))
return -1;
}
else
{
expand_buf(msg->msg_iov, sz);
break;
}
}
while ((sz = recvmsg(fd, msg, 0)) == -1 && errno == EINTR);
return (msg->msg_flags & MSG_TRUNC) ? -1 : sz;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags, struct dhcp_opt *opts)
{
struct dhcp_netid *tagif = run_tag_if(tags);
struct dhcp_opt *opt;
/* flag options which are valid with the current tag set (sans context tags) */
for (opt = opts; opt; opt = opt->next)
{
opt->flags &= ~DHOPT_TAGOK;
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
match_netid(opt->netid, tagif, 0))
opt->flags |= DHOPT_TAGOK;
}
/* now flag options which are valid, including the context tags,
otherwise valid options are inhibited if we found a higher priority one above */
if (context_tags)
{
struct dhcp_netid *last_tag;
for (last_tag = context_tags; last_tag->next; last_tag = last_tag->next);
last_tag->next = tags;
tagif = run_tag_if(context_tags);
/* reset stuff with tag:!<tag> which now matches. */
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925)) &&
(opt->flags & DHOPT_TAGOK) &&
!match_netid(opt->netid, tagif, 0))
opt->flags &= ~DHOPT_TAGOK;
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) &&
match_netid(opt->netid, tagif, 0))
{
struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt->opt && opt->netid && (tmp->flags & DHOPT_TAGOK))
break;
if (!tmp)
opt->flags |= DHOPT_TAGOK;
}
}
/* now flag untagged options which are not overridden by tagged ones */
for (opt = opts; opt; opt = opt->next)
if (!(opt->flags & (DHOPT_ENCAPSULATE | DHOPT_VENDOR | DHOPT_RFC3925 | DHOPT_TAGOK)) && !opt->netid)
{
struct dhcp_opt *tmp;
for (tmp = opts; tmp; tmp = tmp->next)
if (tmp->opt == opt->opt && (tmp->flags & DHOPT_TAGOK))
break;
if (!tmp)
opt->flags |= DHOPT_TAGOK;
else if (!tmp->netid)
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring duplicate dhcp-option %d"), tmp->opt);
}
return tagif;
}
/* Is every member of check matched by a member of pool?
If tagnotneeded, untagged is OK */
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
{
struct dhcp_netid *tmp1;
if (!check && !tagnotneeded)
return 0;
for (; check; check = check->next)
{
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
/* return domain or NULL if none. */
char *strip_hostname(char *hostname)
{
char *dot = strchr(hostname, '.');
if (!dot)
return NULL;
*dot = 0; /* truncate */
if (strlen(dot+1) != 0)
return dot+1;
return NULL;
}
void log_tags(struct dhcp_netid *netid, u32 xid)
{
if (netid && option_bool(OPT_LOG_OPTS))
{
char *s = daemon->namebuff;
for (*s = 0; netid; netid = netid->next)
{
/* kill dupes. */
struct dhcp_netid *n;
for (n = netid->next; n; n = n->next)
if (strcmp(netid->net, n->net) == 0)
break;
if (!n)
{
strncat (s, netid->net, (MAXDNAME-1) - strlen(s));
if (netid->next)
strncat (s, ", ", (MAXDNAME-1) - strlen(s));
}
}
my_syslog(MS_DHCP | LOG_INFO, _("%u tags: %s"), xid, s);
}
}
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len)
{
int i;
if (o->len > len)
return 0;
if (o->len == 0)
return 1;
if (o->flags & DHOPT_HEX)
{
if (memcmp_masked(o->val, p, o->len, o->u.wildcard_mask))
return 1;
}
else
for (i = 0; i <= (len - o->len); )
{
if (memcmp(o->val, p + i, o->len) == 0)
return 1;
if (o->flags & DHOPT_STRING)
i++;
else
i += o->len;
}
return 0;
}
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.
We take care to maintain the invariant that any IP address can appear
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
restore the status-quo ante first. */
struct dhcp_config *config;
struct crec *crec;
int prot = AF_INET;
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_ADDR_HOSTS)
config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR6 | CONFIG_ADDR_HOSTS);
#ifdef HAVE_DHCP6
again:
#endif
if (daemon->port != 0)
for (config = configs; config; config = config->next)
{
int conflags = CONFIG_ADDR;
int cacheflags = F_IPV4;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
{
conflags = CONFIG_ADDR6;
cacheflags = F_IPV6;
}
#endif
if (!(config->flags & conflags) &&
(config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, cacheflags)) &&
(crec->flags & F_HOSTS))
{
if (cache_find_by_name(crec, config->hostname, 0, cacheflags))
{
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, cacheflags);
if (!crec)
continue; /* should be never */
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, daemon->addrbuff);
}
if (prot == AF_INET && !config_find_by_address(configs, crec->addr.addr.addr.addr4))
{
config->addr = crec->addr.addr.addr.addr4;
config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
continue;
}
#ifdef HAVE_DHCP6
if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0))
{
memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
continue;
}
#endif
inet_ntop(prot, &crec->addr.addr, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
daemon->addrbuff, config->hostname);
}
}
#ifdef HAVE_DHCP6
if (prot == AF_INET)
{
prot = AF_INET6;
goto again;
}
#endif
}
#ifdef HAVE_DHCP6
static int join_multicast_worker(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
char ifrn_name[IFNAMSIZ];
struct ipv6_mreq mreq;
int fd, i, max = *((int *)vparam);
struct iname *tmp;
(void)prefix;
(void)scope;
(void)dad;
/* record which interfaces we join on, so that we do it at most one per
interface, even when they have multiple addresses. Use outpacket
as an array of int, since it's always allocated here and easy
to expand for theoretical vast numbers of interfaces. */
for (i = 0; i < max; i++)
if (if_index == ((int *)daemon->outpacket.iov_base)[i])
return 1;
if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
return 0;
if (!indextoname(fd, if_index, ifrn_name))
{
close(fd);
return 0;
}
close(fd);
/* Are we doing DHCP on this interface? */
if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
return 1;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
return 1;
mreq.ipv6mr_interface = if_index;
inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
if (daemon->dhcp6 &&
setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
if (daemon->ra_contexts &&
setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
return 0;
expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
((int *)daemon->outpacket.iov_base)[max++] = if_index;
*((int *)vparam) = max;
return 1;
}
void join_multicast(void)
{
int count = 0;
if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
}
#endif
#ifdef HAVE_LINUX_NETWORK
void bindtodevice(int fd)
{
/* If we are doing DHCP on exactly one interface, and running linux, do SO_BINDTODEVICE
to that device. This is for the use case of (eg) OpenStack, which runs a new
dnsmasq instance for each VLAN interface it creates. Without the BINDTODEVICE,
individual processes don't always see the packets they should.
SO_BINDTODEVICE is only available Linux. */
struct irec *iface, *found;
for (found = NULL, iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dhcp_ok)
{
if (!found)
found = iface;
else if (strcmp(found->name, iface->name) != 0)
{
/* more than one. */
found = NULL;
break;
}
}
if (found)
{
struct ifreq ifr;
strcpy(ifr.ifr_name, found->name);
/* only allowed by root. */
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) == -1 &&
errno != EPERM)
die(_("failed to set SO_BINDTODEVICE on DHCP socket: %s"), NULL, EC_BADNET);
}
}
#endif
static const struct opttab_t {
char *name;
u16 val, size;
} opttab[] = {
{ "netmask", 1, OT_ADDR_LIST },
{ "time-offset", 2, 4 },
{ "router", 3, OT_ADDR_LIST },
{ "dns-server", 6, OT_ADDR_LIST },
{ "log-server", 7, OT_ADDR_LIST },
{ "lpr-server", 9, OT_ADDR_LIST },
{ "hostname", 12, OT_INTERNAL | OT_NAME },
{ "boot-file-size", 13, 2 | OT_DEC },
{ "domain-name", 15, OT_NAME },
{ "swap-server", 16, OT_ADDR_LIST },
{ "root-path", 17, OT_NAME },
{ "extension-path", 18, OT_NAME },
{ "ip-forward-enable", 19, 1 },
{ "non-local-source-routing", 20, 1 },
{ "policy-filter", 21, OT_ADDR_LIST },
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
{ "default-ttl", 23, 1 | OT_DEC },
{ "mtu", 26, 2 | OT_DEC },
{ "all-subnets-local", 27, 1 },
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
{ "router-discovery", 31, 1 },
{ "router-solicitation", 32, OT_ADDR_LIST },
{ "static-route", 33, OT_ADDR_LIST },
{ "trailer-encapsulation", 34, 1 },
{ "arp-timeout", 35, 4 | OT_DEC },
{ "ethernet-encap", 36, 1 },
{ "tcp-ttl", 37, 1 },
{ "tcp-keepalive", 38, 4 | OT_DEC },
{ "nis-domain", 40, OT_NAME },
{ "nis-server", 41, OT_ADDR_LIST },
{ "ntp-server", 42, OT_ADDR_LIST },
{ "vendor-encap", 43, OT_INTERNAL },
{ "netbios-ns", 44, OT_ADDR_LIST },
{ "netbios-dd", 45, OT_ADDR_LIST },
{ "netbios-nodetype", 46, 1 },
{ "netbios-scope", 47, 0 },
{ "x-windows-fs", 48, OT_ADDR_LIST },
{ "x-windows-dm", 49, OT_ADDR_LIST },
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
{ "option-overload", 52, OT_INTERNAL },
{ "message-type", 53, OT_INTERNAL | OT_DEC },
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
{ "parameter-request", 55, OT_INTERNAL },
{ "message", 56, OT_INTERNAL },
{ "max-message-size", 57, OT_INTERNAL },
{ "T1", 58, OT_INTERNAL | OT_TIME},
{ "T2", 59, OT_INTERNAL | OT_TIME},
{ "vendor-class", 60, 0 },
{ "client-id", 61, OT_INTERNAL },
{ "nis+-domain", 64, OT_NAME },
{ "nis+-server", 65, OT_ADDR_LIST },
{ "tftp-server", 66, OT_NAME },
{ "bootfile-name", 67, OT_NAME },
{ "mobile-ip-home", 68, OT_ADDR_LIST },
{ "smtp-server", 69, OT_ADDR_LIST },
{ "pop3-server", 70, OT_ADDR_LIST },
{ "nntp-server", 71, OT_ADDR_LIST },
{ "irc-server", 74, OT_ADDR_LIST },
{ "user-class", 77, 0 },
{ "FQDN", 81, OT_INTERNAL },
{ "agent-id", 82, OT_INTERNAL },
{ "client-arch", 93, 2 | OT_DEC },
{ "client-interface-id", 94, 0 },
{ "client-machine-id", 97, 0 },
{ "subnet-select", 118, OT_INTERNAL },
{ "domain-search", 119, OT_RFC1035_NAME },
{ "sip-server", 120, 0 },
{ "classless-static-route", 121, 0 },
{ "vendor-id-encap", 125, 0 },
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
{ NULL, 0, 0 }
};
#ifdef HAVE_DHCP6
static const struct opttab_t opttab6[] = {
{ "client-id", 1, OT_INTERNAL },
{ "server-id", 2, OT_INTERNAL },
{ "ia-na", 3, OT_INTERNAL },
{ "ia-ta", 4, OT_INTERNAL },
{ "iaaddr", 5, OT_INTERNAL },
{ "oro", 6, OT_INTERNAL },
{ "preference", 7, OT_INTERNAL | OT_DEC },
{ "unicast", 12, OT_INTERNAL },
{ "status", 13, OT_INTERNAL },
{ "rapid-commit", 14, OT_INTERNAL },
{ "user-class", 15, OT_INTERNAL | OT_CSTRING },
{ "vendor-class", 16, OT_INTERNAL | OT_CSTRING },
{ "vendor-opts", 17, OT_INTERNAL },
{ "sip-server-domain", 21, OT_RFC1035_NAME },
{ "sip-server", 22, OT_ADDR_LIST },
{ "dns-server", 23, OT_ADDR_LIST },
{ "domain-search", 24, OT_RFC1035_NAME },
{ "nis-server", 27, OT_ADDR_LIST },
{ "nis+-server", 28, OT_ADDR_LIST },
{ "nis-domain", 29, OT_RFC1035_NAME },
{ "nis+-domain", 30, OT_RFC1035_NAME },
{ "sntp-server", 31, OT_ADDR_LIST },
{ "information-refresh-time", 32, OT_TIME },
{ "FQDN", 39, OT_INTERNAL | OT_RFC1035_NAME },
{ "ntp-server", 56, OT_ADDR_LIST },
{ "bootfile-url", 59, OT_NAME },
{ "bootfile-param", 60, OT_CSTRING },
{ NULL, 0, 0 }
};
#endif
void display_opts(void)
{
int i;
printf(_("Known DHCP options:\n"));
for (i = 0; opttab[i].name; i++)
if (!(opttab[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab[i].val, opttab[i].name);
}
#ifdef HAVE_DHCP6
void display_opts6(void)
{
int i;
printf(_("Known DHCPv6 options:\n"));
for (i = 0; opttab6[i].name; i++)
if (!(opttab6[i].size & OT_INTERNAL))
printf("%3d %s\n", opttab6[i].val, opttab6[i].name);
}
#endif
u16 lookup_dhcp_opt(int prot, char *name)
{
const struct opttab_t *t;
int i;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
t = opttab6;
else
#endif
t = opttab;
for (i = 0; t[i].name; i++)
if (!(t[i].size & OT_INTERNAL) &&
strcasecmp(t[i].name, name) == 0)
return t[i].val;
return 0;
}
u16 lookup_dhcp_len(int prot, u16 val)
{
const struct opttab_t *t;
int i;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
t = opttab6;
else
#endif
t = opttab;
for (i = 0; t[i].name; i++)
if (val == t[i].val)
{
if (t[i].size & OT_INTERNAL)
return 0;
return t[i].size & ~OT_DEC;
}
return 0;
}
char *option_string(int prot, unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
{
int o, i, j, nodecode = 0;
const struct opttab_t *ot = opttab;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
ot = opttab6;
#endif
for (o = 0; ot[o].name; o++)
if (ot[o].val == opt)
{
if (buf)
{
memset(buf, 0, buf_len);
if (ot[o].size & OT_ADDR_LIST)
{
struct all_addr addr;
int addr_len = INADDRSZ;
#ifdef HAVE_DHCP6
if (prot == AF_INET6)
addr_len = IN6ADDRSZ;
#endif
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
{
if (i != 0)
strncat(buf, ", ", buf_len - strlen(buf));
/* align */
memcpy(&addr, &val[i], addr_len);
inet_ntop(prot, &val[i], daemon->addrbuff, ADDRSTRLEN);
strncat(buf, daemon->addrbuff, buf_len - strlen(buf));
}
}
else if (ot[o].size & OT_NAME)
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
{
char c = val[i];
if (isprint((int)c))
buf[j++] = c;
}
#ifdef HAVE_DHCP6
/* We don't handle compressed rfc1035 names, so no good in IPv4 land */
else if ((ot[o].size & OT_RFC1035_NAME) && prot == AF_INET6)
{
i = 0, j = 0;
while (i < opt_len && val[i] != 0)
{
int k, l = i + val[i] + 1;
for (k = i + 1; k < opt_len && k < l && j < buf_len ; k++)
{
char c = val[k];
if (isprint((int)c))
buf[j++] = c;
}
i = l;
if (val[i] != 0 && j < buf_len)
buf[j++] = '.';
}
}
else if ((ot[o].size & OT_CSTRING))
{
int k, len;
unsigned char *p;
i = 0, j = 0;
while (1)
{
p = &val[i];
GETSHORT(len, p);
for (k = 0; k < len && j < buf_len; k++)
{
char c = *p++;
if (isprint((int)c))
buf[j++] = c;
}
i += len +2;
if (i >= opt_len)
break;
if (j < buf_len)
buf[j++] = ',';
}
}
#endif
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
{
unsigned int dec = 0;
for (i = 0; i < opt_len; i++)
dec = (dec << 8) | val[i];
if (ot[o].size & OT_TIME)
prettyprint_time(buf, dec);
else
sprintf(buf, "%u", dec);
}
else
nodecode = 1;
}
break;
}
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
{
int trunc = 0;
if (opt_len > 14)
{
trunc = 1;
opt_len = 14;
}
print_mac(buf, val, opt_len);
if (trunc)
strncat(buf, "...", buf_len - strlen(buf));
}
return ot[o].name ? ot[o].name : "";
}
#endif

96
src/dhcp-protocol.h Normal file
View File

@@ -0,0 +1,96 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DHCP_SERVER_PORT 67
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_ALTPORT 1067
#define DHCP_CLIENT_ALTPORT 1068
#define PXE_PORT 4011
#define BOOTREQUEST 1
#define BOOTREPLY 2
#define DHCP_COOKIE 0x63825363
/* The Linux in-kernel DHCP client silently ignores any packet
smaller than this. Sigh........... */
#define MIN_PACKETSZ 300
#define OPTION_PAD 0
#define OPTION_NETMASK 1
#define OPTION_ROUTER 3
#define OPTION_DNSSERVER 6
#define OPTION_HOSTNAME 12
#define OPTION_DOMAINNAME 15
#define OPTION_BROADCAST 28
#define OPTION_VENDOR_CLASS_OPT 43
#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_VENDOR_ID 60
#define OPTION_CLIENT_ID 61
#define OPTION_SNAME 66
#define OPTION_FILENAME 67
#define OPTION_USER_CLASS 77
#define OPTION_CLIENT_FQDN 81
#define OPTION_AGENT_ID 82
#define OPTION_ARCH 93
#define OPTION_PXE_UUID 97
#define OPTION_SUBNET_SELECT 118
#define OPTION_DOMAIN_SEARCH 119
#define OPTION_SIP_SERVER 120
#define OPTION_VENDOR_IDENT 124
#define OPTION_VENDOR_IDENT_OPT 125
#define OPTION_END 255
#define SUBOPT_CIRCUIT_ID 1
#define SUBOPT_REMOTE_ID 2
#define SUBOPT_SUBNET_SELECT 5 /* RFC 3527 */
#define SUBOPT_SUBSCR_ID 6 /* RFC 3393 */
#define SUBOPT_SERVER_OR 11 /* RFC 5107 */
#define SUBOPT_PXE_BOOT_ITEM 71 /* PXE standard */
#define SUBOPT_PXE_DISCOVERY 6
#define SUBOPT_PXE_SERVERS 8
#define SUBOPT_PXE_MENU 9
#define SUBOPT_PXE_MENU_PROMPT 10
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
#define DHCP_CHADDR_MAX 16
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
};

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,13 +19,19 @@
#ifdef HAVE_DHCP
struct iface_param {
struct in_addr relay, primary;
struct dhcp_context *current;
int ind;
};
struct match_param {
int ind, matched;
struct in_addr netmask, broadcast, addr;
};
static int complete_context(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int check_listen_addrs(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam);
static int make_fd(int port)
{
@@ -35,16 +41,22 @@ static int make_fd(int port)
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
int mtu = IP_PMTUDISC_DONT;
#endif
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
int tos = IPTOS_CLASS_CS6;
#endif
if (fd == -1)
die (_("cannot create DHCP socket: %s"), NULL, EC_BADNET);
if (!fix_fd(fd) ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(fd, SOL_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &mtu, sizeof(mtu)) == -1 ||
#endif
#if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) == -1 ||
#endif
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
#else
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &oneopt, sizeof(oneopt)) == -1 ||
#endif
@@ -54,7 +66,7 @@ static int make_fd(int port)
/* When bind-interfaces is set, there might be more than one dnmsasq
instance binding port 67. That's OK if they serve different networks.
Need to set REUSEADDR to make this posible, or REUSEPORT on *BSD. */
if (daemon->options & OPT_NOWILD)
if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
{
#ifdef SO_REUSEPORT
int rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt));
@@ -96,7 +108,7 @@ void dhcp_init(void)
we drop root. Also, set buffer size small, to avoid wasting
kernel buffers */
if (daemon->options & OPT_NO_PING)
if (option_bool(OPT_NO_PING))
daemon->dhcp_icmp_fd = -1;
else if ((daemon->dhcp_icmp_fd = make_icmp_sock()) == -1 ||
setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1 )
@@ -104,14 +116,9 @@ void dhcp_init(void)
/* Make BPF raw send socket */
init_bpf();
#endif
check_dhcp_hosts(1);
daemon->dhcp_packet.iov_len = sizeof(struct dhcp_packet);
daemon->dhcp_packet.iov_base = safe_malloc(daemon->dhcp_packet.iov_len);
#endif
}
void dhcp_packet(time_t now, int pxe_fd)
{
int fd = pxe_fd ? daemon->pxefd : daemon->dhcpfd;
@@ -125,7 +132,7 @@ void dhcp_packet(time_t now, int pxe_fd)
struct iovec iov;
ssize_t sz;
int iface_index = 0, unicast_dest = 0, is_inform = 0;
struct in_addr iface_addr, *addrp = NULL;
struct in_addr iface_addr;
struct iface_param parm;
#ifdef HAVE_LINUX_NETWORK
struct arpreq arp_req;
@@ -141,56 +148,23 @@ void dhcp_packet(time_t now, int pxe_fd)
char control[CMSG_SPACE(sizeof(struct sockaddr_dl))];
#endif
} control_u;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_name = NULL;
msg.msg_namelen = 0;
struct dhcp_bridge *bridge, *alias;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
while (1)
{
msg.msg_flags = 0;
while ((sz = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC)) == -1 && errno == EINTR);
if (sz == -1)
return;
if (!(msg.msg_flags & MSG_TRUNC))
break;
/* Very new Linux kernels return the actual size needed,
older ones always return truncated size */
if ((size_t)sz == daemon->dhcp_packet.iov_len)
{
if (!expand_buf(&daemon->dhcp_packet, sz + 100))
return;
}
else
{
expand_buf(&daemon->dhcp_packet, sz);
break;
}
}
/* expand_buf may have moved buffer */
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
msg.msg_controllen = sizeof(control_u);
msg.msg_control = control_u.control;
msg.msg_flags = 0;
msg.msg_name = &dest;
msg.msg_namelen = sizeof(dest);
while ((sz = recvmsg(fd, &msg, 0)) == -1 && errno == EINTR);
if ((msg.msg_flags & MSG_TRUNC) || sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options)))
if ((sz = recv_dhcp_packet(fd, &msg)) == -1 ||
(sz < (ssize_t)(sizeof(*mess) - sizeof(mess->options))))
return;
#if defined (HAVE_LINUX_NETWORK)
#if defined (HAVE_LINUX_NETWORK)
if (msg.msg_controllen >= sizeof(struct cmsghdr))
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
@@ -237,71 +211,85 @@ void dhcp_packet(time_t now, int pxe_fd)
strncpy(arp_req.arp_dev, ifr.ifr_name, 16);
#endif
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(ifr.ifr_name, alias->iface, IF_NAMESIZE) == 0)
{
if (!(iface_index = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), ifr.ifr_name);
return;
}
else
{
strncpy(ifr.ifr_name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
#ifdef MSG_BCAST
/* OpenBSD tells us when a packet was broadcast */
if (!(msg.msg_flags & MSG_BCAST))
unicast_dest = 1;
#endif
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) != -1 )
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
else
{
addrp = &iface_addr;
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
if (!iface_check(AF_INET, (struct all_addr *)addrp, ifr.ifr_name, &iface_index))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* weird libvirt-inspired access control */
for (context = daemon->dhcp; context; context = context->next)
if (!context->interface || strcmp(context->interface, ifr.ifr_name) == 0)
break;
if (!context)
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp; context; context = context->next)
context->current = context;
parm.relay = mess->giaddr;
parm.primary = iface_addr;
parm.current = NULL;
parm.ind = iface_index;
/* interface may have been changed by alias in iface_check, make sure it gets priority in case
there is more than one address on the interface in the same subnet */
if (ioctl(daemon->dhcpfd, SIOCGIFADDR, &ifr) == -1)
if (!iface_check(AF_INET, (struct all_addr *)&iface_addr, ifr.ifr_name))
{
my_syslog(MS_DHCP | LOG_WARNING, _("DHCP packet received on %s which has no address"), ifr.ifr_name);
return;
}
else
{
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
{
struct in_addr netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
{
struct in_addr broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
complete_context(iface_addr, iface_index, netmask, broadcast, &parm);
}
}
}
/* If we failed to match the primary address of the interface, see if we've got a --listen-address
for a secondary */
struct match_param match;
match.matched = 0;
match.ind = iface_index;
if (!daemon->if_addrs ||
!iface_enumerate(AF_INET, &match, check_listen_addrs) ||
!match.matched)
return;
if (!iface_enumerate(&parm, complete_context, NULL))
iface_addr = match.addr;
/* make sure secondary address gets priority in case
there is more than one address on the interface in the same subnet */
complete_context(match.addr, iface_index, match.netmask, match.broadcast, &parm);
}
if (!iface_enumerate(AF_INET, &parm, complete_context))
return;
lease_prune(NULL, now); /* lose any expired leases */
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
now, unicast_dest, &is_inform, pxe_fd);
now, unicast_dest, &is_inform, pxe_fd, iface_addr);
lease_update_file(now);
lease_update_dns();
lease_update_dns(0);
if (iov.iov_len == 0)
return;
@@ -319,7 +307,7 @@ void dhcp_packet(time_t now, int pxe_fd)
#ifdef HAVE_SOCKADDR_SA_LEN
dest.sin_len = sizeof(struct sockaddr_in);
#endif
if (pxe_fd)
{
if (mess->ciaddr.s_addr != 0)
@@ -357,7 +345,7 @@ void dhcp_packet(time_t now, int pxe_fd)
pkt->ipi_ifindex = iface_index;
pkt->ipi_spec_dst.s_addr = 0;
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO;
dest.sin_addr.s_addr = INADDR_BROADCAST;
dest.sin_port = htons(daemon->dhcp_client_port);
@@ -414,6 +402,30 @@ void dhcp_packet(time_t now, int pxe_fd)
while(sendmsg(fd, &msg, 0) == -1 && retry_send());
}
/* check against secondary interface addresses */
static int check_listen_addrs(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct match_param *param = vparam;
struct iname *tmp;
if (if_index == param->ind)
{
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if ( tmp->addr.sa.sa_family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == local.s_addr)
{
param->matched = 1;
param->addr = local;
param->netmask = netmask;
param->broadcast = broadcast;
break;
}
}
return 1;
}
/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple
of each interface (and any relay address) and does the following things:
@@ -448,38 +460,27 @@ static int complete_context(struct in_addr local, int if_index,
context->netmask = netmask;
}
if (context->netmask.s_addr)
if (context->netmask.s_addr != 0 &&
is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask))
{
if (is_same_net(local, context->start, context->netmask) &&
is_same_net(local, context->end, context->netmask))
/* link it onto the current chain if we've not seen it before */
if (if_index == param->ind && context->current == context)
{
/* link it onto the current chain if we've not seen it before */
if (if_index == param->ind && context->current == context)
{
context->router = local;
context->local = local;
context->current = param->current;
param->current = context;
}
if (!(context->flags & CONTEXT_BRDCAST))
{
if (is_same_net(broadcast, context->start, context->netmask))
context->broadcast = broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
else if (param->relay.s_addr && is_same_net(param->relay, context->start, context->netmask))
context->router = local;
context->local = local;
context->current = param->current;
param->current = context;
}
if (!(context->flags & CONTEXT_BRDCAST))
{
context->router = param->relay;
context->local = param->primary;
/* fill in missing broadcast addresses for relayed ranges */
if (!(context->flags & CONTEXT_BRDCAST))
if (is_same_net(broadcast, context->start, context->netmask))
context->broadcast = broadcast;
else
context->broadcast.s_addr = context->start.s_addr | ~context->netmask.s_addr;
}
}
}
}
return 1;
@@ -505,7 +506,7 @@ struct dhcp_context *address_available(struct dhcp_context *context,
start = ntohl(tmp->start.s_addr);
end = ntohl(tmp->end.s_addr);
if (!(tmp->flags & CONTEXT_STATIC) &&
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_PROXY)) &&
addr >= start &&
addr <= end &&
match_netid(tmp->filter, netids, 1))
@@ -540,7 +541,8 @@ struct dhcp_context *narrow_context(struct dhcp_context *context,
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net(taddr, tmp->start, tmp->netmask))
is_same_net(taddr, tmp->start, tmp->netmask) &&
!(tmp->flags & CONTEXT_PROXY))
break;
}
@@ -562,50 +564,6 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
return NULL;
}
/* Is every member of check matched by a member of pool?
If tagnotneeded, untagged is OK */
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int tagnotneeded)
{
struct dhcp_netid *tmp1;
if (!check && !tagnotneeded)
return 0;
for (; check; check = check->next)
{
/* '#' for not is for backwards compat. */
if (check->net[0] != '!' && check->net[0] != '#')
{
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp(check->net, tmp1->net) == 0)
break;
if (!tmp1)
return 0;
}
else
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
if (strcmp((check->net)+1, tmp1->net) == 0)
return 0;
}
return 1;
}
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
{
struct tag_if *exprs;
struct dhcp_netid_list *list;
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
if (match_netid(exprs->tag, tags, 1))
for (list = exprs->set; list; list = list->next)
{
list->list->next = tags;
tags = list->list;
}
return tags;
}
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now)
@@ -626,16 +584,22 @@ int address_allocate(struct dhcp_context *context,
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & CONTEXT_STATIC)
if (c->flags & (CONTEXT_STATIC | CONTEXT_PROXY))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
else
{
/* pick a seed based on hwaddr then iterate until we find a free address. */
start.s_addr = addr.s_addr =
htonl(ntohl(c->start.s_addr) +
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
if (option_bool(OPT_CONSEC_ADDR))
/* seed is largest extant lease addr in this context */
start = lease_find_max_addr(c);
else
/* pick a seed based on hwaddr */
start.s_addr = htonl(ntohl(c->start.s_addr) +
((j + c->addr_epoch) % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
/* iterate until we find a free address. */
addr = start;
do {
/* eliminate addresses in use by the server. */
@@ -660,9 +624,6 @@ int address_allocate(struct dhcp_context *context,
*addrp = addr;
if (daemon->options & OPT_NO_PING)
return 1;
/* check if we failed to ping addr sometime in the last
PING_CACHE_TIME seconds. If so, assume the same situation still exists.
This avoids problems when a stupid client bangs
@@ -672,33 +633,51 @@ int address_allocate(struct dhcp_context *context,
for (count = 0, r = daemon->ping_results; r; r = r->next)
if (difftime(now, r->time) > (float)PING_CACHE_TIME)
victim = r; /* old record */
else if (++count == max || r->addr.s_addr == addr.s_addr)
return 1;
if (icmp_ping(addr))
/* address in use: perturb address selection so that we are
less likely to try this address again. */
c->addr_epoch++;
else
else
{
count++;
if (r->addr.s_addr == addr.s_addr)
{
/* consec-ip mode: we offered this address for another client
(different hash) recently, don't offer it to this one. */
if (option_bool(OPT_CONSEC_ADDR) && r->hash != j)
break;
return 1;
}
}
if (!r)
{
/* at this point victim may hold an expired record */
if (!victim)
if ((count < max) && !option_bool(OPT_NO_PING) && icmp_ping(addr))
{
if ((victim = whine_malloc(sizeof(struct ping_result))))
/* address in use: perturb address selection so that we are
less likely to try this address again. */
if (!option_bool(OPT_CONSEC_ADDR))
c->addr_epoch++;
}
else
{
/* at this point victim may hold an expired record */
if (!victim)
{
victim->next = daemon->ping_results;
daemon->ping_results = victim;
if ((victim = whine_malloc(sizeof(struct ping_result))))
{
victim->next = daemon->ping_results;
daemon->ping_results = victim;
}
}
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
victim->hash = j;
}
return 1;
}
/* record that this address is OK for 30s
without more ping checks */
if (victim)
{
victim->addr = addr;
victim->time = now;
}
return 1;
}
}
@@ -709,6 +688,7 @@ int address_allocate(struct dhcp_context *context,
} while (addr.s_addr != start.s_addr);
}
return 0;
}
@@ -779,7 +759,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
is_addr_in_context(context, config))
return config;
/* use match with fewest wildcast octets */
/* use match with fewest wildcard octets */
for (candidate = NULL, count = 0, config = configs; config; config = config->next)
if (is_addr_in_context(context, config))
for (conf_addr = config->hwaddr; conf_addr; conf_addr = conf_addr->next)
@@ -956,85 +936,6 @@ void dhcp_read_ethers(void)
my_syslog(MS_DHCP | LOG_INFO, _("read %s - %d addresses"), ETHERSFILE, count);
}
void check_dhcp_hosts(int fatal)
{
/* If the same IP appears in more than one host config, then DISCOVER
for one of the hosts will get the address, but REQUEST will be NAKed,
since the address is reserved by the other one -> protocol loop.
Also check that FQDNs match the domain we are using. */
struct dhcp_config *configs, *cp;
for (configs = daemon->dhcp_conf; configs; configs = configs->next)
{
char *domain;
if ((configs->flags & DHOPT_BANK) || fatal)
{
for (cp = configs->next; cp; cp = cp->next)
if ((configs->flags & cp->flags & CONFIG_ADDR) && configs->addr.s_addr == cp->addr.s_addr)
{
if (fatal)
die(_("duplicate IP address %s in dhcp-config directive."),
inet_ntoa(cp->addr), EC_BADCONF);
else
my_syslog(MS_DHCP | LOG_ERR, _("duplicate IP address %s in %s."),
inet_ntoa(cp->addr), daemon->dhcp_hosts_file);
configs->flags &= ~CONFIG_ADDR;
}
/* split off domain part */
if ((configs->flags & CONFIG_NAME) && (domain = strip_hostname(configs->hostname)))
configs->domain = domain;
}
}
}
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.
We take care to maintain the invariant that any IP address can appear
in at most one dhcp-host. Since /etc/hosts can be re-read by SIGHUP,
restore the status-quo ante first. */
struct dhcp_config *config;
struct crec *crec;
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_ADDR_HOSTS)
config->flags &= ~(CONFIG_ADDR | CONFIG_ADDR_HOSTS);
if (daemon->port != 0)
for (config = configs; config; config = config->next)
if (!(config->flags & CONFIG_ADDR) &&
(config->flags & CONFIG_NAME) &&
(crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
(crec->flags & F_HOSTS))
{
if (cache_find_by_name(crec, config->hostname, 0, F_IPV4))
{
/* use primary (first) address */
while (crec && !(crec->flags & F_REVERSE))
crec = cache_find_by_name(crec, config->hostname, 0, F_IPV4);
if (!crec)
continue; /* should be never */
my_syslog(MS_DHCP | LOG_WARNING, _("%s has more than one address in hostsfile, using %s for DHCP"),
config->hostname, inet_ntoa(crec->addr.addr.addr.addr4));
}
if (config_find_by_address(configs, crec->addr.addr.addr.addr4))
my_syslog(MS_DHCP | LOG_WARNING, _("duplicate IP address %s (%s) in dhcp-config directive"),
inet_ntoa(crec->addr.addr.addr.addr4), config->hostname);
else
{
config->addr = crec->addr.addr.addr.addr4;
config->flags |= CONFIG_ADDR | CONFIG_ADDR_HOSTS;
}
}
}
/* If we've not found a hostname any other way, try and see if there's one in /etc/hosts
for this address. If it has a domain part, that must match the set domain and
@@ -1075,20 +976,5 @@ char *host_from_dns(struct in_addr addr)
return NULL;
}
/* return domain or NULL if none. */
char *strip_hostname(char *hostname)
{
char *dot = strchr(hostname, '.');
if (!dot)
return NULL;
*dot = 0; /* truncate */
if (strlen(dot+1) != 0)
return dot+1;
return NULL;
}
#endif

68
src/dhcp6-protocol.h Normal file
View File

@@ -0,0 +1,68 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define DHCPV6_SERVER_PORT 547
#define DHCPV6_CLIENT_PORT 546
#define ALL_SERVERS "FF05::1:3"
#define ALL_RELAY_AGENTS_AND_SERVERS "FF02::1:2"
#define DHCP6SOLICIT 1
#define DHCP6ADVERTISE 2
#define DHCP6REQUEST 3
#define DHCP6CONFIRM 4
#define DHCP6RENEW 5
#define DHCP6REBIND 6
#define DHCP6REPLY 7
#define DHCP6RELEASE 8
#define DHCP6DECLINE 9
#define DHCP6RECONFIGURE 10
#define DHCP6IREQ 11
#define DHCP6RELAYFORW 12
#define DHCP6RELAYREPL 13
#define OPTION6_CLIENT_ID 1
#define OPTION6_SERVER_ID 2
#define OPTION6_IA_NA 3
#define OPTION6_IA_TA 4
#define OPTION6_IAADDR 5
#define OPTION6_ORO 6
#define OPTION6_PREFERENCE 7
#define OPTION6_ELAPSED_TIME 8
#define OPTION6_RELAY_MSG 9
#define OPTION6_AUTH 11
#define OPTION6_UNICAST 12
#define OPTION6_STATUS_CODE 13
#define OPTION6_RAPID_COMMIT 14
#define OPTION6_USER_CLASS 15
#define OPTION6_VENDOR_CLASS 16
#define OPTION6_VENDOR_OPTS 17
#define OPTION6_INTERFACE_ID 18
#define OPTION6_RECONFIGURE_MSG 19
#define OPTION6_RECONF_ACCEPT 20
#define OPTION6_DNS_SERVER 23
#define OPTION6_DOMAIN_SEARCH 24
#define OPTION6_REMOTE_ID 37
#define OPTION6_SUBSCRIBER_ID 38
#define OPTION6_FQDN 39
#define DHCP6SUCCESS 0
#define DHCP6UNSPEC 1
#define DHCP6NOADDRS 2
#define DHCP6NOBINDING 3
#define DHCP6NOTONLINK 4
#define DHCP6USEMULTI 5

423
src/dhcp6.c Normal file
View File

@@ -0,0 +1,423 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
struct iface_param {
struct dhcp_context *current;
struct in6_addr fallback;
int ind;
};
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);
void dhcp6_init(void)
{
int fd;
struct sockaddr_in6 saddr;
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
int class = IPTOS_CLASS_CS6;
#endif
if ((fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP)) == -1 ||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
#endif
!fix_fd(fd) ||
!set_ipv6pktinfo(fd))
die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
memset(&saddr, 0, sizeof(saddr));
#ifdef HAVE_SOCKADDR_SA_LEN
saddr.sin6_len = sizeof(struct sockaddr_in6);
#endif
saddr.sin6_family = AF_INET6;
saddr.sin6_addr = in6addr_any;
saddr.sin6_port = htons(DHCPV6_SERVER_PORT);
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
daemon->dhcp6fd = fd;
}
void dhcp6_packet(time_t now)
{
struct dhcp_context *context;
struct iface_param parm;
struct cmsghdr *cmptr;
struct msghdr msg;
int if_index = 0;
union {
struct cmsghdr align; /* this ensures alignment */
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control_u;
struct sockaddr_in6 from;
struct all_addr dest;
ssize_t sz;
struct ifreq ifr;
struct iname *tmp;
unsigned short port;
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &daemon->dhcp_packet;
msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
return;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
dest.addr.addr6 = p.p->ipi6_addr;
}
if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
return;
if (!iface_check(AF_INET6, (struct all_addr *)&dest, ifr.ifr_name))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
return;
/* unlinked contexts are marked by context->current == context */
for (context = daemon->dhcp6; context; context = context->next)
{
context->current = context;
memset(&context->local6, 0, IN6ADDRSZ);
}
parm.current = NULL;
parm.ind = if_index;
memset(&parm.fallback, 0, IN6ADDRSZ);
if (!iface_enumerate(AF_INET6, &parm, complete_context6))
return;
lease_prune(NULL, now); /* lose any expired leases */
port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,
sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);
lease_update_file(now);
lease_update_dns(0);
/* The port in the source address of the original request should
be correct, but at least once client sends from the server port,
so we explicitly send to the client port to a client, and the
server port to a relay. */
if (port != 0)
{
from.sin6_port = htons(port);
while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0),
0, (struct sockaddr *)&from, sizeof(from)) == -1 &&
retry_send());
}
}
static int complete_context6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context;
struct iface_param *param = vparam;
(void)scope; /* warning */
(void)dad;
if (if_index == param->ind &&
!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
/* Determine a globally address on the arrival interface, even
if we have no matching dhcp-context, because we're only
allocating on remote subnets via relays. This
is used as a default for the DNS server option. */
param->fallback = *local;
for (context = daemon->dhcp6; context; context = context->next)
{
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
/* link it onto the current chain if we've not seen it before */
if (context->current == context)
{
context->current = param->current;
param->current = context;
context->local6 = *local;
}
}
}
}
return 1;
}
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)
{
struct dhcp_config *config;
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_ADDR6) &&
is_same_net6(&config->addr6, net, prefix) &&
(prefix == 128 || addr6part(&config->addr6) == addr))
return config;
return NULL;
}
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans)
{
/* Find a free address: exclude anything in use and anything allocated to
a particular hwaddr/clientid/hostname in our configuration.
Try to return from contexts which match netids first.
Note that we assume the address prefix lengths are 64 or greater, so we can
get by with 64 bit arithmetic.
*/
u64 start, addr;
struct dhcp_context *c, *d;
int i, pass;
u64 j;
/* hash hwaddr: use the SDBM hashing algorithm. This works
for MAC addresses, let's see how it manages with client-ids! */
for (j = 0, i = 0; i < clid_len; i++)
j += clid[i] + (j << 6) + (j << 16) - j;
for (pass = 0; pass <= 1; pass++)
for (c = context; c; c = c->current)
if (c->flags & (CONTEXT_DEPRECATE | CONTEXT_STATIC | CONTEXT_RA_STATELESS))
continue;
else if (!match_netid(c->filter, netids, pass))
continue;
else
{
if (option_bool(OPT_CONSEC_ADDR))
/* seed is largest extant lease addr in this context */
start = lease_find_max_addr6(c) + serial;
else
start = addr6part(&c->start6) + ((j + c->addr_epoch + serial) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));
/* iterate until we find a free address. */
addr = start;
do {
/* eliminate addresses in use by the server. */
for (d = context; d; d = d->current)
if (addr == addr6part(&d->local6))
break;
if (!d &&
!lease6_find_by_addr(&c->start6, c->prefix, addr) &&
!config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))
{
*ans = c->start6;
setaddr6part (ans, addr);
return 1;
}
addr++;
if (addr == addr6part(&c->end6) + 1)
addr = addr6part(&c->start6);
} while (addr != start);
}
return 0;
}
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
u64 start, end, addr = addr6part(taddr);
struct dhcp_context *tmp;
for (tmp = context; tmp; tmp = tmp->current)
{
start = addr6part(&tmp->start6);
end = addr6part(&tmp->end6);
if (!(tmp->flags & (CONTEXT_STATIC | CONTEXT_RA_STATELESS)) &&
is_same_net6(&tmp->start6, taddr, tmp->prefix) &&
is_same_net6(&tmp->end6, taddr, tmp->prefix) &&
addr >= start &&
addr <= end &&
match_netid(tmp->filter, netids, 1))
return tmp;
}
return NULL;
}
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids)
{
/* We start of with a set of possible contexts, all on the current physical interface.
These are chained on ->current.
Here we have an address, and return the actual context correponding to that
address. Note that none may fit, if the address came a dhcp-host and is outside
any dhcp-range. In that case we return a static range if possible, or failing that,
any context on the correct subnet. (If there's more than one, this is a dodgy
configuration: maybe there should be a warning.) */
struct dhcp_context *tmp;
if (!(tmp = address6_available(context, taddr, netids)))
{
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
(tmp->flags & CONTEXT_STATIC))
break;
if (!tmp)
for (tmp = context; tmp; tmp = tmp->current)
if (match_netid(tmp->filter, netids, 1) &&
is_same_net6(taddr, &tmp->start6, tmp->prefix) &&
!(tmp->flags & CONTEXT_PROXY))
break;
}
/* Only one context allowed now */
if (tmp)
tmp->current = NULL;
return tmp;
}
static int is_addr_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!context) /* called via find_config() from lease_update_from_configs() */
return 1;
if (!(config->flags & CONFIG_ADDR6))
return 1;
for (; context; context = context->current)
if (is_same_net6(&config->addr6, &context->start6, context->prefix))
return 1;
return 0;
}
struct dhcp_config *find_config6(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *duid, int duid_len,
char *hostname)
{
struct dhcp_config *config;
if (duid)
for (config = configs; config; config = config->next)
if (config->flags & CONFIG_CLID)
{
if (config->clid_len == duid_len &&
memcmp(config->clid, duid, duid_len) == 0 &&
is_addr_in_context6(context, config))
return config;
}
if (hostname && context)
for (config = configs; config; config = config->next)
if ((config->flags & CONFIG_NAME) &&
hostname_isequal(config->hostname, hostname) &&
is_addr_in_context6(context, config))
return config;
return NULL;
}
void make_duid(time_t now)
{
if (daemon->duid_config)
{
unsigned char *p;
daemon->duid = p = safe_malloc(daemon->duid_config_len + 6);
daemon->duid_len = daemon->duid_config_len + 6;
PUTSHORT(2, p); /* DUID_EN */
PUTLONG(daemon->duid_enterprise, p);
memcpy(p, daemon->duid_config, daemon->duid_config_len);
}
else
{
/* rebase epoch to 1/1/2000 */
time_t newnow = now - 946684800;
iface_enumerate(AF_LOCAL, &newnow, make_duid1);
if(!daemon->duid)
die("Cannot create DHCPv6 server DUID: %s", NULL, EC_MISC);
}
}
static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm)
{
/* create DUID as specified in RFC3315. We use the MAC of the
first interface we find that isn't loopback or P-to-P and
has address-type < 256. Address types above 256 are things like
tunnels which don't have usable MAC addresses. */
unsigned char *p;
(void)index;
if (type >= 256)
return 1;
#ifdef HAVE_BROKEN_RTC
daemon->duid = p = safe_malloc(maclen + 4);
daemon->duid_len = maclen + 4;
PUTSHORT(3, p); /* DUID_LL */
PUTSHORT(type, p); /* address type */
#else
daemon->duid = p = safe_malloc(maclen + 8);
daemon->duid_len = maclen + 8;
PUTSHORT(1, p); /* DUID_LLT */
PUTSHORT(type, p); /* address type */
PUTLONG(*((time_t *)parm), p); /* time */
#endif
memcpy(p, mac, maclen);
return 0;
}
#endif

114
src/dns-protocol.h Normal file
View File

@@ -0,0 +1,114 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define NAMESERVER_PORT 53
#define TFTP_PORT 69
#define IN6ADDRSZ 16
#define INADDRSZ 4
#define PACKETSZ 512 /* maximum packet size */
#define MAXDNAME 1025 /* maximum presentation domain name */
#define RRFIXEDSZ 10 /* #/bytes of fixed data in r record */
#define MAXLABEL 63 /* maximum length of domain label */
#define NOERROR 0 /* no error */
#define FORMERR 1 /* format error */
#define SERVFAIL 2 /* server failure */
#define NXDOMAIN 3 /* non existent domain */
#define NOTIMP 4 /* not implemented */
#define REFUSED 5 /* query refused */
#define QUERY 0 /* opcode */
#define C_IN 1 /* the arpa internet */
#define C_CHAOS 3 /* for chaos net (MIT) */
#define C_ANY 255 /* wildcard match */
#define T_A 1
#define T_NS 2
#define T_CNAME 5
#define T_SOA 6
#define T_PTR 12
#define T_MX 15
#define T_TXT 16
#define T_SIG 24
#define T_AAAA 28
#define T_SRV 33
#define T_NAPTR 35
#define T_OPT 41
#define T_TKEY 249
#define T_TSIG 250
#define T_MAILB 253
#define T_ANY 255
struct dns_header {
u16 id;
u8 hb3,hb4;
u16 qdcount,ancount,nscount,arcount;
};
#define HB3_QR 0x80
#define HB3_OPCODE 0x78
#define HB3_AA 0x04
#define HB3_TC 0x02
#define HB3_RD 0x01
#define HB4_RA 0x80
#define HB4_AD 0x20
#define HB4_CD 0x10
#define HB4_RCODE 0x0f
#define OPCODE(x) (((x)->hb3 & HB3_OPCODE) >> 3)
#define RCODE(x) ((x)->hb4 & HB4_RCODE)
#define SET_RCODE(x, code) (x)->hb4 = ((x)->hb4 & ~HB4_RCODE) | code
#define GETSHORT(s, cp) { \
unsigned char *t_cp = (unsigned char *)(cp); \
(s) = ((u16)t_cp[0] << 8) \
| ((u16)t_cp[1]) \
; \
(cp) += 2; \
}
#define GETLONG(l, cp) { \
unsigned char *t_cp = (unsigned char *)(cp); \
(l) = ((u32)t_cp[0] << 24) \
| ((u32)t_cp[1] << 16) \
| ((u32)t_cp[2] << 8) \
| ((u32)t_cp[3]) \
; \
(cp) += 4; \
}
#define PUTSHORT(s, cp) { \
u16 t_s = (u16)(s); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_s >> 8; \
*t_cp = t_s; \
(cp) += 2; \
}
#define PUTLONG(l, cp) { \
u32 t_l = (u32)(l); \
unsigned char *t_cp = (unsigned char *)(cp); \
*t_cp++ = t_l >> 24; \
*t_cp++ = t_l >> 16; \
*t_cp++ = t_l >> 8; \
*t_cp = t_l; \
(cp) += 4; \
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,7 +14,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define COPYRIGHT "Copyright (c) 2000-2010 Simon Kelley"
#define COPYRIGHT "Copyright (c) 2000-2012 Simon Kelley"
#ifndef NO_LARGEFILE
/* Ensure we can use files >2GB (log files may grow this big) */
@@ -24,7 +24,9 @@
/* Get linux C library versions and define _GNU_SOURCE for kFreeBSD. */
#if defined(__linux__) || defined(__GLIBC__)
# define _GNU_SOURCE
# ifndef __ANDROID__
# define _GNU_SOURCE
# endif
# include <features.h>
#endif
@@ -37,20 +39,30 @@
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef __APPLE__
# include <nameser.h>
# include <arpa/nameser_compat.h>
#else
# include <arpa/nameser.h>
/* Define before netinet/in.h to select API. OSX Lion onwards. */
# define __APPLE_USE_RFC_3542
#endif
#include <netinet/in.h>
/* and this. */
/* Also needed before config.h. */
#include <getopt.h>
#include "config.h"
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
#include "dns-protocol.h"
#include "dhcp-protocol.h"
#ifdef HAVE_DHCP6
#include "dhcp6-protocol.h"
#include "radv-protocol.h"
#endif
#define gettext_noop(S) (S)
#ifndef LOCALEDIR
# define _(S) (S)
@@ -89,7 +101,7 @@
#include <pwd.h>
#include <grp.h>
#include <stdarg.h>
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun)
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__sun__) || defined (__sun) || defined (__ANDROID__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -125,7 +137,7 @@ extern int capget(cap_user_header_t header, cap_user_data_t data);
/* Async event queue */
struct event_desc {
int event, data;
int event, data, msg_sz;
};
#define EVENT_RELOAD 1
@@ -146,6 +158,8 @@ struct event_desc {
#define EVENT_DIE 16
#define EVENT_LOG_ERR 17
#define EVENT_FORK_ERR 18
#define EVENT_LUA_ERR 19
#define EVENT_TFTP_ERR 20
/* Exit codes. */
#define EC_GOOD 0
@@ -163,38 +177,51 @@ struct event_desc {
*/
#define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
#define OPT_BOGUSPRIV (1u<<0)
#define OPT_FILTER (1u<<1)
#define OPT_LOG (1u<<2)
#define OPT_SELFMX (1u<<3)
#define OPT_NO_HOSTS (1u<<4)
#define OPT_NO_POLL (1u<<5)
#define OPT_DEBUG (1u<<6)
#define OPT_ORDER (1u<<7)
#define OPT_NO_RESOLV (1u<<8)
#define OPT_EXPAND (1u<<9)
#define OPT_LOCALMX (1u<<10)
#define OPT_NO_NEG (1u<<11)
#define OPT_NODOTS_LOCAL (1u<<12)
#define OPT_NOWILD (1u<<13)
#define OPT_ETHERS (1u<<14)
#define OPT_RESOLV_DOMAIN (1u<<15)
#define OPT_NO_FORK (1u<<16)
#define OPT_AUTHORITATIVE (1u<<17)
#define OPT_LOCALISE (1u<<18)
#define OPT_DBUS (1u<<19)
#define OPT_DHCP_FQDN (1u<<20)
#define OPT_NO_PING (1u<<21)
#define OPT_LEASE_RO (1u<<22)
#define OPT_ALL_SERVERS (1u<<23)
#define OPT_RELOAD (1u<<24)
#define OPT_LOCAL_REBIND (1u<<25)
#define OPT_TFTP_SECURE (1u<<26)
#define OPT_TFTP_NOBLOCK (1u<<27)
#define OPT_LOG_OPTS (1u<<28)
#define OPT_TFTP_APREF (1u<<29)
#define OPT_NO_OVERRIDE (1u<<30)
#define OPT_NO_REBIND (1u<<31)
/* Trust the compiler dead-code eliminator.... */
#define option_bool(x) (((x) < 32) ? daemon->options & (1u << (x)) : daemon->options2 & (1u << ((x) - 32)))
#define OPT_BOGUSPRIV 0
#define OPT_FILTER 1
#define OPT_LOG 2
#define OPT_SELFMX 3
#define OPT_NO_HOSTS 4
#define OPT_NO_POLL 5
#define OPT_DEBUG 6
#define OPT_ORDER 7
#define OPT_NO_RESOLV 8
#define OPT_EXPAND 9
#define OPT_LOCALMX 10
#define OPT_NO_NEG 11
#define OPT_NODOTS_LOCAL 12
#define OPT_NOWILD 13
#define OPT_ETHERS 14
#define OPT_RESOLV_DOMAIN 15
#define OPT_NO_FORK 16
#define OPT_AUTHORITATIVE 17
#define OPT_LOCALISE 18
#define OPT_DBUS 19
#define OPT_DHCP_FQDN 20
#define OPT_NO_PING 21
#define OPT_LEASE_RO 22
#define OPT_ALL_SERVERS 23
#define OPT_RELOAD 24
#define OPT_LOCAL_REBIND 25
#define OPT_TFTP_SECURE 26
#define OPT_TFTP_NOBLOCK 27
#define OPT_LOG_OPTS 28
#define OPT_TFTP_APREF 29
#define OPT_NO_OVERRIDE 30
#define OPT_NO_REBIND 31
#define OPT_ADD_MAC 32
#define OPT_DNSSEC 33
#define OPT_CONSEC_ADDR 34
#define OPT_CONNTRACK 35
#define OPT_FQDN_UPDATE 36
#define OPT_RA 37
#define OPT_TFTP_LC 38
#define OPT_CLEVERBIND 39
#define OPT_TFTP 40
#define OPT_LAST 41
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -235,7 +262,8 @@ struct naptr {
};
struct txt_record {
char *name, *txt;
char *name;
unsigned char *txt;
unsigned short class, len;
struct txt_record *next;
};
@@ -250,6 +278,18 @@ struct cname {
struct cname *next;
};
struct host_record {
struct name_list {
char *name;
struct name_list *next;
} *names;
struct in_addr addr;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
struct host_record *next;
};
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
@@ -261,17 +301,30 @@ union bigname {
union bigname *next; /* freelist */
};
struct keydata {
struct keydata *next;
unsigned char key[KEYBLOCK_LEN];
};
struct crec {
struct crec *next, *prev, *hash_next;
time_t ttd; /* time to die */
int uid;
/* union is 16 bytes when doing IPv6, 8 bytes on 32 bit machines without IPv6 */
union {
struct all_addr addr;
struct {
struct crec *cache;
int uid;
} cname;
struct {
struct keydata *keydata;
unsigned char algo;
unsigned char digest; /* DS only */
unsigned short flags_or_keyid; /* flags for DNSKEY, keyid for DS */
} key;
} addr;
time_t ttd; /* time to die */
/* used as keylen if F_DS or F_DNSKEY, index to source for F_HOSTS */
int uid;
unsigned short flags;
union {
char sname[SMALLDNAME];
@@ -280,22 +333,34 @@ struct crec {
} name;
};
#define F_IMMORTAL 1
#define F_CONFIG 2
#define F_REVERSE 4
#define F_FORWARD 8
#define F_DHCP 16
#define F_NEG 32
#define F_HOSTS 64
#define F_IPV4 128
#define F_IPV6 256
#define F_BIGNAME 512
#define F_UPSTREAM 1024
#define F_SERVER 2048
#define F_NXDOMAIN 4096
#define F_QUERY 8192
#define F_CNAME 16384
#define F_NOERR 32768
#define F_IMMORTAL (1u<<0)
#define F_NAMEP (1u<<1)
#define F_REVERSE (1u<<2)
#define F_FORWARD (1u<<3)
#define F_DHCP (1u<<4)
#define F_NEG (1u<<5)
#define F_HOSTS (1u<<6)
#define F_IPV4 (1u<<7)
#define F_IPV6 (1u<<8)
#define F_BIGNAME (1u<<9)
#define F_NXDOMAIN (1u<<10)
#define F_CNAME (1u<<11)
#define F_DNSKEY (1u<<12)
#define F_CONFIG (1u<<13)
#define F_DS (1u<<14)
#define F_DNSSECOK (1u<<15)
/* below here are only valid as args to log_query: cache
entries are limited to 16 bits */
#define F_UPSTREAM (1u<<16)
#define F_RRNAME (1u<<17)
#define F_SERVER (1u<<18)
#define F_QUERY (1u<<19)
#define F_NOERR (1u<<20)
/* composites */
#define F_TYPE (F_IPV4 | F_IPV6 | F_DNSKEY | F_DS) /* Only one may be set */
/* struct sockaddr is not large enough to hold any address,
and specifically not big enough to hold an IPv6 address.
@@ -347,14 +412,14 @@ struct server {
struct irec {
union mysockaddr addr;
struct in_addr netmask; /* only valid for IPv4 */
int tftp_ok, mtu;
int tftp_ok, dhcp_ok, mtu, done, dad;
char *name;
struct irec *next;
};
struct listener {
int fd, tcpfd, tftpfd, family;
struct irec *iface; /* only valid for non-wildcard */
struct irec *iface; /* only sometimes valid for non-wildcard */
struct listener *next;
};
@@ -362,7 +427,7 @@ struct listener {
struct iname {
char *name;
union mysockaddr addr;
int isloop, used;
int used;
struct iname *next;
};
@@ -374,7 +439,7 @@ struct resolvc {
char *name;
};
/* adn-hosts parms from command-line */
/* adn-hosts parms from command-line (also dhcp-hostsfile and dhcp-optsfile */
#define AH_DIR 1
#define AH_INACTIVE 2
struct hostsfile {
@@ -384,6 +449,9 @@ struct hostsfile {
int index; /* matches to cache entries for logging */
};
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
struct frec {
union mysockaddr source;
struct all_addr dest;
@@ -394,39 +462,61 @@ struct frec {
#endif
unsigned int iface;
unsigned short orig_id, new_id;
int fd, forwardall, norebind;
int fd, forwardall, flags;
unsigned int crc;
time_t time;
struct frec *next;
};
/* flags in top of length field for DHCP-option tables */
#define OT_ADDR_LIST 0x8000
#define OT_RFC1035_NAME 0x4000
#define OT_INTERNAL 0x2000
#define OT_NAME 0x1000
#define OT_CSTRING 0x0800
#define OT_DEC 0x0400
#define OT_TIME 0x0200
/* actions in the daemon->helper RPC */
#define ACTION_DEL 1
#define ACTION_OLD_HOSTNAME 2
#define ACTION_OLD 3
#define ACTION_ADD 4
#define ACTION_TFTP 5
#define DHCP_CHADDR_MAX 16
#define LEASE_NEW 1 /* newly created */
#define LEASE_CHANGED 2 /* modified */
#define LEASE_AUX_CHANGED 4 /* CLID or expiry changed */
#define LEASE_AUTH_NAME 8 /* hostname came from config, not from client */
#define LEASE_USED 16 /* used this DHCPv6 transaction */
#define LEASE_NA 32 /* IPv6 no-temporary lease */
#define LEASE_TA 64 /* IPv6 temporary lease */
#define LEASE_HAVE_HWADDR 128 /* Have set hwaddress */
struct dhcp_lease {
int clid_len; /* length of client identifier */
unsigned char *clid; /* clientid */
char *hostname, *fqdn; /* name from client-hostname option or config */
char *old_hostname; /* hostname before it moved to another lease */
char auth_name; /* hostname came from config, not from client */
char new; /* newly created */
char changed; /* modified */
char aux_changed; /* CLID or expiry changed */
int flags;
time_t expires; /* lease expiry */
#ifdef HAVE_BROKEN_RTC
unsigned int length;
#endif
int hwaddr_len, hwaddr_type;
unsigned char hwaddr[DHCP_CHADDR_MAX];
int hwaddr_len, hwaddr_type; /* hw_type used for iaid in v6 */
unsigned char hwaddr[DHCP_CHADDR_MAX]; /* also IPv6 address */
struct in_addr addr, override, giaddr;
unsigned char *extradata;
unsigned int extradata_len, extradata_size;
int last_interface;
#ifdef HAVE_DHCP6
struct slaac_address {
struct in6_addr addr, local;
time_t ping_time;
int backoff; /* zero -> confirmed */
struct slaac_address *next;
} *slaac_address;
#endif
struct dhcp_lease *next;
};
@@ -459,6 +549,9 @@ struct dhcp_config {
unsigned char *clid; /* clientid */
char *hostname, *domain;
struct dhcp_netid_list *netid;
#ifdef HAVE_DHCP6
struct in6_addr addr6;
#endif
struct in_addr addr;
time_t decline_time;
unsigned int lease_time;
@@ -466,6 +559,8 @@ struct dhcp_config {
struct dhcp_config *next;
};
#define have_config(config, mask) ((config) && ((config)->flags & (mask)))
#define CONFIG_DISABLE 1
#define CONFIG_CLID 2
#define CONFIG_TIME 8
@@ -476,6 +571,7 @@ struct dhcp_config {
#define CONFIG_ADDR_HOSTS 512 /* address added by from /etc/hosts */
#define CONFIG_DECLINED 1024 /* address declined by client */
#define CONFIG_BANK 2048 /* from dhcp hosts file */
#define CONFIG_ADDR6 4096
struct dhcp_opt {
int opt, len, flags;
@@ -501,9 +597,11 @@ struct dhcp_opt {
#define DHOPT_HEX 512
#define DHOPT_VENDOR_MATCH 1024
#define DHOPT_RFC3925 2048
#define DHOPT_TAGOK 4096
#define DHOPT_ADDR6 8192
struct dhcp_boot {
char *file, *sname;
char *file, *sname, *tftp_sname;
struct in_addr next_server;
struct dhcp_netid *netid;
struct dhcp_boot *next;
@@ -511,7 +609,7 @@ struct dhcp_boot {
struct pxe_service {
unsigned short CSA, type;
char *menu, *basename;
char *menu, *basename, *sname;
struct in_addr server;
struct dhcp_netid *netid;
struct pxe_service *next;
@@ -525,7 +623,8 @@ struct pxe_service {
/* vendorclass, userclass, remote-id or cicuit-id */
struct dhcp_vendor {
int len, match_type, option;
int len, match_type;
unsigned int enterprise;
char *data;
struct dhcp_netid netid;
struct dhcp_vendor *next;
@@ -547,6 +646,10 @@ struct dhcp_bridge {
struct cond_domain {
char *domain;
struct in_addr start, end;
#ifdef HAVE_IPV6
struct in6_addr start6, end6;
#endif
int is6;
struct cond_domain *next;
};
@@ -555,35 +658,32 @@ struct dhcp_context {
struct in_addr netmask, broadcast;
struct in_addr local, router;
struct in_addr start, end; /* range of available addresses */
#ifdef HAVE_DHCP6
struct in6_addr start6, end6; /* range of available addresses */
struct in6_addr local6;
int prefix, if_index;
time_t ra_time;
#endif
int flags;
char *interface;
struct dhcp_netid netid, *filter;
struct dhcp_context *next, *current;
};
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
struct dhcp_packet {
u8 op, htype, hlen, hops;
u32 xid;
u16 secs, flags;
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
u8 chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
u8 options[312];
};
#define CONTEXT_STATIC 1
#define CONTEXT_NETMASK 2
#define CONTEXT_BRDCAST 4
#define CONTEXT_PROXY 8
#define CONTEXT_RA_ONLY 16
#define CONTEXT_RA_DONE 32
#define CONTEXT_RA_NAME 64
#define CONTEXT_RA_STATELESS 128
#define CONTEXT_DHCP 256
#define CONTEXT_DEPRECATE 512
struct ping_result {
struct in_addr addr;
time_t time;
unsigned int hash;
struct ping_result *next;
};
@@ -601,7 +701,7 @@ struct tftp_transfer {
int backoff;
unsigned int block, blocksize, expansion;
off_t offset;
struct sockaddr_in peer;
union mysockaddr peer;
char opt_blocksize, opt_transize, netascii, carrylf;
struct tftp_file *file;
struct tftp_transfer *next;
@@ -612,11 +712,6 @@ struct addr_list {
struct addr_list *next;
};
struct interface_list {
char *interface;
struct interface_list *next;
};
struct tftp_prefix {
char *interface;
char *prefix;
@@ -629,18 +724,20 @@ extern struct daemon {
config file arguments. All set (including defaults)
in option.c */
unsigned int options;
unsigned int options, options2;
struct resolvc default_resolv, *resolv_files;
time_t last_resolv;
struct mx_srv_record *mxnames;
struct naptr *naptr;
struct txt_record *txt;
struct txt_record *txt, *rr;
struct ptr_record *ptr;
struct host_record *host_records, *host_records_tail;
struct cname *cnames;
struct interface_name *int_names;
char *mxtarget;
char *lease_file;
char *username, *groupname, *scriptuser;
char *luascript;
int group_set, osport;
char *domain_suffix;
struct cond_domain *cond_domain;
@@ -656,9 +753,9 @@ extern struct daemon {
int port, query_port, min_port;
unsigned long local_ttl, neg_ttl, max_ttl;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp;
struct dhcp_context *dhcp, *dhcp6, *ra_contexts;
struct dhcp_config *dhcp_conf;
struct dhcp_opt *dhcp_opts, *dhcp_match;
struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
struct dhcp_vendor *dhcp_vendors;
struct dhcp_mac *dhcp_macs;
struct dhcp_boot *boot_config;
@@ -669,7 +766,7 @@ extern struct daemon {
int enable_pxe;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
char *dhcp_hosts_file, *dhcp_opts_file;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
int dhcp_max, tftp_max;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
@@ -678,8 +775,9 @@ extern struct daemon {
unsigned short edns_pktsz;
char *tftp_prefix;
struct tftp_prefix *if_prefix; /* per-interface TFTP prefixes */
struct interface_list *tftp_interfaces; /* interfaces for limited TFTP service */
int tftp_unlimited;
unsigned int duid_enterprise, duid_config_len;
unsigned char *duid_config;
char *dbus_name;
/* globally used stuff for DNS */
char *packet; /* packet buffer */
@@ -712,7 +810,12 @@ extern struct daemon {
struct ping_result *ping_results;
FILE *lease_stream;
struct dhcp_bridge *bridges;
#ifdef HAVE_DHCP6
int duid_len;
unsigned char *duid;
struct iovec outpacket;
int dhcp6fd, icmp6fd;
#endif
/* DBus stuff */
/* void * here to avoid depending on dbus headers outside dbus.c */
void *dbus;
@@ -721,13 +824,16 @@ extern struct daemon {
#endif
/* TFTP stuff */
struct tftp_transfer *tftp_trans;
struct tftp_transfer *tftp_trans, *tftp_done_trans;
/* utility string buffer, hold max sized IP address as string */
char *addrbuff;
} *daemon;
/* cache.c */
void cache_init(void);
void log_query(unsigned short flags, char *name, struct all_addr *addr, char *arg);
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
char *record_source(int index);
void querystr(char *str, unsigned short type);
struct crec *cache_find_by_addr(struct crec *crecp,
@@ -740,30 +846,39 @@ void cache_start_insert(void);
struct crec *cache_insert(char *name, struct all_addr *addr,
time_t now, unsigned long ttl, unsigned short flags);
void cache_reload(void);
void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
void cache_add_dhcp_entry(char *host_name, int prot, struct all_addr *host_address, time_t ttd);
struct in_addr a_record_from_hosts(char *name, time_t now);
void cache_unhash_dhcp(void);
void dump_cache(time_t now);
char *cache_get_name(struct crec *crecp);
char *get_domain(struct in_addr addr);
#ifdef HAVE_IPV6
char *get_domain6(struct in6_addr *addr);
#endif
#ifdef HAVE_DNSSEC
struct keydata *keydata_alloc(char *data, size_t len);
void keydata_free(struct keydata *blocks);
#endif
/* rfc1035.c */
unsigned short extract_request(HEADER *header, size_t qlen,
unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep);
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags,
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags,
unsigned long local_ttl);
int extract_addresses(HEADER *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind);
size_t answer_request(HEADER *header, char *limit, size_t qlen,
int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
time_t now, int is_sign, int checkrebind, int checking_disabled);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now);
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(HEADER *header, size_t plen,
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
size_t *len, unsigned char **p, int *is_sign);
int check_for_local_domain(char *name, time_t now);
unsigned int questions_crc(HEADER *header, size_t plen, char *buff);
size_t resize_packet(HEADER *header, size_t plen,
unsigned int questions_crc(struct dns_header *header, size_t plen, char *buff);
size_t resize_packet(struct dns_header *header, size_t plen,
unsigned char *pheader, size_t hlen);
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3);
/* util.c */
void rand_init(void);
@@ -779,6 +894,11 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
int hostname_isequal(char *a, char *b);
time_t dnsmasq_time(void);
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen);
u64 addr6part(struct in6_addr *addr);
void setaddr6part(struct in6_addr *addr, u64 host);
#endif
int retry_send(void);
void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
@@ -802,16 +922,23 @@ void flush_log(void);
/* option.c */
void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(unsigned char opt, int *is_ip, int *is_name);
char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
void reread_dhcp(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
/* forward.c */
void reply_query(int fd, int family, time_t now);
void receive_query(struct listener *listen, time_t now);
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask);
union mysockaddr *local_addr, struct in_addr netmask);
void server_gone(struct server *server);
struct frec *get_new_frec(time_t now, int *wait);
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface);
/* network.c */
int indextoname(int fd, int index, char *name);
@@ -821,11 +948,15 @@ void pre_allocate_sfds(void);
int reload_servers(char *fname);
void check_servers(void);
int enumerate_interfaces();
struct listener *create_wildcard_listeners(void);
struct listener *create_bound_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *indexp);
void create_wildcard_listeners(void);
void create_bound_listeners(int die);
int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name);
int fix_fd(int fd);
struct in_addr get_ifaddr(char *intr);
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd);
#endif
/* dhcp.c */
#ifdef HAVE_DHCP
@@ -837,50 +968,59 @@ struct dhcp_context *address_available(struct dhcp_context *context,
struct dhcp_context *narrow_context(struct dhcp_context *context,
struct in_addr taddr,
struct dhcp_netid *netids);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
int address_allocate(struct dhcp_context *context,
struct in_addr *addrp, unsigned char *hwaddr, int hw_len,
struct dhcp_netid *netids, time_t now);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
int config_has_mac(struct dhcp_config *config, unsigned char *hwaddr, int len, int type);
struct dhcp_config *find_config(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *clid, int clid_len,
unsigned char *hwaddr, int hw_len,
int hw_type, char *hostname);
void dhcp_update_configs(struct dhcp_config *configs);
void dhcp_read_ethers(void);
void check_dhcp_hosts(int fatal);
struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
char *strip_hostname(char *hostname);
char *host_from_dns(struct in_addr addr);
char *get_domain(struct in_addr addr);
#endif
/* lease.c */
#ifdef HAVE_DHCP
void lease_update_file(time_t now);
void lease_update_dns();
void lease_update_dns(int force);
void lease_init(time_t now);
struct dhcp_lease *lease_allocate(struct in_addr addr);
struct dhcp_lease *lease4_allocate(struct in_addr addr);
#ifdef HAVE_DHCP6
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type);
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr);
void lease6_filter(int lease_type, int iaid, struct dhcp_context *context);
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
u64 lease_find_max_addr6(struct dhcp_context *context);
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
#endif
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth);
unsigned char *clid, int hw_len, int hw_type, int clid_len, time_t now, int force);
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain);
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
void lease_set_interface(struct dhcp_lease *lease, int interface);
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
unsigned char *clid, int clid_len);
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
struct in_addr lease_find_max_addr(struct dhcp_context *context);
void lease_prune(struct dhcp_lease *target, time_t now);
void lease_update_from_configs(void);
int do_script_run(time_t now);
void rerun_scripts(void);
void lease_find_interfaces(time_t now);
#ifdef HAVE_SCRIPT
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
unsigned int len, int delim);
#endif
#endif
/* rfc2131.c */
#ifdef HAVE_DHCP
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd);
size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe_fd, struct in_addr fallback);
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int clid_len, unsigned char *clid, int *len_out);
#endif
@@ -890,7 +1030,8 @@ unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
int make_icmp_sock(void);
int icmp_ping(struct in_addr addr);
#endif
void send_event(int fd, int event, int data);
void send_alarm(time_t event, time_t now);
void send_event(int fd, int event, int data, char *msg);
void clear_cache_and_reload(time_t now);
void poll_resolv(int force, int do_reload, time_t now);
@@ -908,7 +1049,7 @@ void send_via_bpf(struct dhcp_packet *mess, size_t len,
#endif
/* bpf.c or netlink.c */
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)());
int iface_enumerate(int family, void *parm, int (callback)());
/* dbus.c */
#ifdef HAVE_DBUS
@@ -921,11 +1062,14 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
#endif
/* helper.c */
#if defined(HAVE_DHCP) && !defined(NO_FORK)
#if defined(HAVE_SCRIPT)
int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd);
void helper_write(void);
void queue_script(int action, struct dhcp_lease *lease,
char *hostname, time_t now);
#ifdef HAVE_TFTP
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer);
#endif
int helper_buf_empty(void);
#endif
@@ -933,4 +1077,94 @@ int helper_buf_empty(void);
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(fd_set *rset, time_t now);
int do_tftp_script_run(void);
#endif
/* conntrack.c */
#ifdef HAVE_CONNTRACK
int get_incoming_mark(union mysockaddr *peer_addr, struct all_addr *local_addr,
int istcp, unsigned int *markp);
#endif
/* dhcp6.c */
#ifdef HAVE_DHCP6
void dhcp6_init(void);
void dhcp6_packet(time_t now);
int address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int serial, struct dhcp_netid *netids, struct in6_addr *ans);
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_context *narrow_context6(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids);
struct dhcp_config *find_config6(struct dhcp_config *configs,
struct dhcp_context *context,
unsigned char *duid, int duid_len,
char *hostname);
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net,
int prefix, u64 addr);
void make_duid(time_t now);
#endif
/* rfc3315.c */
#ifdef HAVE_DHCP6
unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
#endif
/* dhcp-common.c */
#ifdef HAVE_DHCP
void dhcp_common_init(void);
ssize_t recv_dhcp_packet(int fd, struct msghdr *msg);
struct dhcp_netid *run_tag_if(struct dhcp_netid *input);
struct dhcp_netid *option_filter(struct dhcp_netid *tags, struct dhcp_netid *context_tags,
struct dhcp_opt *opts);
int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool, int negonly);
char *strip_hostname(char *hostname);
void log_tags(struct dhcp_netid *netid, u32 xid);
int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
void dhcp_update_configs(struct dhcp_config *configs);
void display_opts(void);
u16 lookup_dhcp_opt(int prot, char *name);
u16 lookup_dhcp_len(int prot, u16 val);
char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len);
#ifdef HAVE_LINUX_NETWORK
void bindtodevice(int fd);
#endif
# ifdef HAVE_DHCP6
void display_opts6(void);
void join_multicast(void);
# endif
#endif
/* outpacket.c */
#ifdef HAVE_DHCP6
void end_opt6(int container);
int save_counter(int newval);
void *expand(size_t headroom);
int new_opt6(int opt);
void *put_opt6(void *data, size_t len);
void put_opt6_long(unsigned int val);
void put_opt6_short(unsigned int val);
void put_opt6_char(unsigned int val);
void put_opt6_string(char *s);
#endif
/* radv.c */
#ifdef HAVE_DHCP6
void ra_init(time_t now);
void icmp6_packet(void);
time_t periodic_ra(time_t now);
void ra_start_unsolicted(time_t now, struct dhcp_context *context);
#endif
/* slaac.c */
#ifdef HAVE_DHCP6
void build_subnet_map(void);
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
void schedule_subnet_map(void);
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,9 +26,9 @@ static struct randfd *allocate_rfd(int family);
/* Send a UDP packet with its 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, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
int send_from(int fd, int nowild, char *packet, size_t len,
union mysockaddr *to, struct all_addr *source,
unsigned int iface)
{
struct msghdr msg;
struct iovec iov[1];
@@ -70,7 +70,7 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
p.ipi_spec_dst = source->addr.addr4;
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
cmptr->cmsg_level = SOL_IP;
cmptr->cmsg_level = IPPROTO_IP;
cmptr->cmsg_type = IP_PKTINFO;
#elif defined(IP_SENDSRCADDR)
memcpy(CMSG_DATA(cmptr), &(source->addr.addr4), sizeof(source->addr.addr4));
@@ -88,10 +88,10 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
memcpy(CMSG_DATA(cmptr), &p, sizeof(p));
msg.msg_controllen = cmptr->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
cmptr->cmsg_type = daemon->v6pktinfo;
cmptr->cmsg_level = IPV6_LEVEL;
cmptr->cmsg_level = IPPROTO_IPV6;
}
#else
iface = 0; /* eliminate warning */
(void)iface; /* eliminate warning */
#endif
}
@@ -106,13 +106,20 @@ static void send_from(int fd, int nowild, char *packet, size_t len,
msg.msg_controllen = 0;
goto retry;
}
if (retry_send())
goto retry;
my_syslog(LOG_ERR, _("failed to send packet: %s"), strerror(errno));
return 0;
}
return 1;
}
static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned short qtype, char *qdomain, int *type, char **domain, int *norebind)
static unsigned int search_servers(time_t now, struct all_addr **addrpp,
unsigned int qtype, char *qdomain, int *type, char **domain, int *norebind)
{
/* If the query ends in the domain in one of our servers, set
@@ -122,13 +129,13 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
unsigned int namelen = strlen(qdomain);
unsigned int matchlen = 0;
struct server *serv;
unsigned short flags = 0;
unsigned int flags = 0;
for (serv = daemon->servers; serv; serv=serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && *type != SERV_HAS_DOMAIN && !strchr(qdomain, '.') && namelen != 0)
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = SERV_FOR_NODOTS;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
@@ -158,39 +165,59 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
{
if (serv->flags & SERV_NO_REBIND)
*norebind = 1;
else if (domainlen >= matchlen)
else
{
unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
unsigned int sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
/* implement priority rules for --address and --server for same domain.
--address wins if the address is for the correct AF
--server wins otherwise. */
if (domainlen != 0 && domainlen == matchlen)
{
if (sflag & qtype)
if ((serv->flags & SERV_LITERAL_ADDRESS))
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
if (!(sflag & qtype) && flags == 0)
continue;
}
else
{
if (flags & (F_IPV4 | F_IPV6))
continue;
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else
flags = 0;
}
if (domainlen >= matchlen)
{
*type = serv->flags & (SERV_HAS_DOMAIN | SERV_USE_RESOLV | SERV_NO_REBIND);
*domain = serv->domain;
matchlen = domainlen;
if (serv->flags & SERV_NO_ADDR)
flags = F_NXDOMAIN;
else if (serv->flags & SERV_LITERAL_ADDRESS)
{
if (sflag & qtype)
{
flags = sflag;
if (serv->addr.sa.sa_family == AF_INET)
*addrpp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
else
*addrpp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
}
else if (!flags || (flags & F_NXDOMAIN))
flags = F_NOERR;
}
else
flags = 0;
}
}
}
}
if (flags == 0 && !(qtype & F_BIGNAME) &&
(daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward simple names, make exception for NS queries and empty name. */
flags = F_NXDOMAIN;
if (flags == 0 && !(qtype & F_QUERY) &&
option_bool(OPT_NODOTS_LOCAL) && !strchr(qdomain, '.') && namelen != 0)
/* don't forward A or AAAA queries for simple names, except the empty name */
flags = F_NOERR;
if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now))
flags = F_NOERR;
@@ -214,16 +241,19 @@ static unsigned short search_servers(time_t now, struct all_addr **addrpp,
static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
HEADER *header, size_t plen, time_t now, struct frec *forward)
struct dns_header *header, size_t plen, time_t now, struct frec *forward)
{
char *domain = NULL;
int type = 0, norebind = 0;
struct all_addr *addrp = NULL;
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
unsigned short flags = 0;
unsigned short gotname = extract_request(header, plen, daemon->namebuff, NULL);
unsigned int flags = 0;
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
struct server *start = NULL;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
@@ -232,7 +262,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
forward->sentto->failed_queries++;
if (!(daemon->options & OPT_ORDER))
if (!option_bool(OPT_ORDER))
{
forward->forwardall = 1;
daemon->last_server = NULL;
@@ -261,9 +291,13 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->fd = udpfd;
forward->crc = crc;
forward->forwardall = 0;
forward->norebind = norebind;
header->id = htons(forward->new_id);
if (norebind)
forward->flags |= FREC_NOREBIND;
if (header->hb4 & HB4_CD)
forward->flags |= FREC_CHECKING_DISABLED;
header->id = htons(forward->new_id);
/* In strict_order mode, always try servers in the order
specified in resolv.conf, if a domain is given
always try all the available servers,
@@ -271,7 +305,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (type == 0)
{
if (daemon->options & OPT_ORDER)
if (option_bool(OPT_ORDER))
start = daemon->servers;
else if (!(start = daemon->last_server) ||
daemon->forwardcount++ > FORWARD_TEST ||
@@ -286,7 +320,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else
{
start = daemon->servers;
if (!(daemon->options & OPT_ORDER))
if (!option_bool(OPT_ORDER))
forward->forwardall = 1;
}
}
@@ -300,7 +334,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
{
struct server *firstsentto = start;
int forwarded = 0;
if (udpaddr && option_bool(OPT_ADD_MAC))
plen = add_mac(header, plen, ((char *) header) + PACKETSZ, udpaddr);
while (1)
{
/* only send to servers dealing with our domain.
@@ -336,6 +373,16 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
daemon->rfd_save = forward->rfd4;
fd = forward->rfd4->fd;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
if (get_incoming_mark(udpaddr, dst_addr, 0, &mark))
setsockopt(fd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
if (sendto(fd, (char *)header, plen, 0,
@@ -389,14 +436,14 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
if (udpfd != -1)
{
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
send_from(udpfd, daemon->options & OPT_NOWILD, (char *)header, plen, udpaddr, dst_addr, dst_iface);
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
}
return 0;
}
static size_t process_reply(HEADER *header, time_t now,
struct server *server, size_t n, int check_rebind)
static size_t process_reply(struct dns_header *header, time_t now,
struct server *server, size_t n, int check_rebind, int checking_disabled)
{
unsigned char *pheader, *sizep;
int munged = 0, is_sign;
@@ -416,29 +463,33 @@ static size_t process_reply(HEADER *header, time_t now,
PUTSHORT(daemon->edns_pktsz, psave);
}
if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
/* RFC 4035 sect 4.6 para 3 */
if (!is_sign && !option_bool(OPT_DNSSEC))
header->hb4 &= ~HB4_AD;
if (OPCODE(header) != QUERY || (RCODE(header) != NOERROR && RCODE(header) != NXDOMAIN))
return n;
/* Complain loudly if the upstream server is non-recursive. */
if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
if (!(header->hb4 & HB4_RA) && RCODE(header) == NOERROR && ntohs(header->ancount) == 0 &&
server && !(server->flags & SERV_WARNED_RECURSIVE))
{
prettyprint_addr(&server->addr, daemon->namebuff);
my_syslog(LOG_WARNING, _("nameserver %s refused to do a recursive query"), daemon->namebuff);
if (!(daemon->options & OPT_LOG))
if (!option_bool(OPT_LOG))
server->flags |= SERV_WARNED_RECURSIVE;
}
if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
{
munged = 1;
header->rcode = NXDOMAIN;
header->aa = 0;
SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA;
}
else
{
if (header->rcode == NXDOMAIN &&
if (RCODE(header) == NXDOMAIN &&
extract_request(header, n, daemon->namebuff, NULL) &&
check_for_local_domain(daemon->namebuff, now))
{
@@ -446,11 +497,11 @@ static size_t process_reply(HEADER *header, time_t now,
an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
since we know that the domain exists, even if upstream doesn't */
munged = 1;
header->aa = 1;
header->rcode = NOERROR;
header->hb3 |= HB3_AA;
SET_RCODE(header, NOERROR);
}
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind))
if (extract_addresses(header, n, daemon->namebuff, now, is_sign, check_rebind, checking_disabled))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
@@ -478,7 +529,7 @@ void reply_query(int fd, int family, time_t now)
{
/* packet from peer server, extract data for cache, and send to
original requester */
HEADER *header;
struct dns_header *header;
union mysockaddr serveraddr;
struct frec *forward;
socklen_t addrlen = sizeof(serveraddr);
@@ -502,17 +553,17 @@ void reply_query(int fd, int family, time_t now)
sockaddr_isequal(&server->addr, &serveraddr))
break;
header = (HEADER *)daemon->packet;
header = (struct dns_header *)daemon->packet;
if (!server ||
n < (int)sizeof(HEADER) || !header->qr ||
n < (int)sizeof(struct dns_header) || !(header->hb3 & HB3_QR) ||
!(forward = lookup_frec(ntohs(header->id), questions_crc(header, n, daemon->namebuff))))
return;
server = forward->sentto;
if ((header->rcode == SERVFAIL || header->rcode == REFUSED) &&
!(daemon->options & OPT_ORDER) &&
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
!option_bool(OPT_ORDER) &&
forward->forwardall == 0)
/* for broken servers, attempt to send to another one. */
{
@@ -529,8 +580,7 @@ void reply_query(int fd, int family, time_t now)
header->arcount = htons(0);
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->qr = 0;
header->tc = 0;
header->hb3 &= ~(HB3_QR | HB3_TC);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
return;
}
@@ -539,7 +589,7 @@ void reply_query(int fd, int family, time_t now)
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
if (header->rcode == SERVFAIL || header->rcode == REFUSED)
if (RCODE(header) == SERVFAIL || RCODE(header) == REFUSED)
server = NULL;
else
{
@@ -554,7 +604,7 @@ void reply_query(int fd, int family, time_t now)
break;
}
}
if (!(daemon->options & OPT_ALL_SERVERS))
if (!option_bool(OPT_ALL_SERVERS))
daemon->last_server = server;
}
@@ -563,18 +613,18 @@ void reply_query(int fd, int family, time_t now)
had replies from all to avoid filling the forwarding table when
everything is broken */
if (forward->forwardall == 0 || --forward->forwardall == 1 ||
(header->rcode != REFUSED && header->rcode != SERVFAIL))
(RCODE(header) != REFUSED && RCODE(header) != SERVFAIL))
{
int check_rebind = !forward->norebind;
int check_rebind = !(forward->flags & FREC_NOREBIND);
if (!(daemon->options & OPT_NO_REBIND))
if (!option_bool(OPT_NO_REBIND))
check_rebind = 0;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind)))
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
{
header->id = htons(forward->orig_id);
header->ra = 1; /* recursion if available */
send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, nn,
header->hb4 |= HB4_RA; /* recursion if available */
send_from(forward->fd, option_bool(OPT_NOWILD) || option_bool (OPT_CLEVERBIND), daemon->packet, nn,
&forward->source, &forward->dest, forward->iface);
}
free_frec(forward); /* cancel */
@@ -584,7 +634,7 @@ void reply_query(int fd, int family, time_t now)
void receive_query(struct listener *listen, time_t now)
{
HEADER *header = (HEADER *)daemon->packet;
struct dns_header *header = (struct dns_header *)daemon->packet;
union mysockaddr source_addr;
unsigned short type;
struct all_addr dst_addr;
@@ -614,7 +664,7 @@ void receive_query(struct listener *listen, time_t now)
/* packet buffer overwritten */
daemon->srv_save = NULL;
if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
if (listen->iface && listen->family == AF_INET && option_bool(OPT_NOWILD))
{
dst_addr_4 = listen->iface->addr.in.sin_addr;
netmask = listen->iface->netmask;
@@ -639,9 +689,9 @@ void receive_query(struct listener *listen, time_t now)
if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
return;
if (n < (int)sizeof(HEADER) ||
if (n < (int)sizeof(struct dns_header) ||
(msg.msg_flags & MSG_TRUNC) ||
header->qr)
(header->hb3 & HB3_QR))
return;
source_addr.sa.sa_family = listen->family;
@@ -649,8 +699,8 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6)
source_addr.in6.sin6_flowinfo = 0;
#endif
if (!(daemon->options & OPT_NOWILD))
if (!option_bool(OPT_NOWILD))
{
struct ifreq ifr;
@@ -660,7 +710,7 @@ void receive_query(struct listener *listen, time_t now)
#if defined(HAVE_LINUX_NETWORK)
if (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)
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO)
{
union {
unsigned char *c;
@@ -700,7 +750,7 @@ void receive_query(struct listener *listen, time_t now)
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPV6_LEVEL && cmptr->cmsg_type == daemon->v6pktinfo)
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
@@ -717,15 +767,36 @@ void receive_query(struct listener *listen, time_t now)
/* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) ||
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &if_index))
!iface_check(listen->family, &dst_addr, ifr.ifr_name))
return;
if (listen->family == AF_INET &&
(daemon->options & OPT_LOCALISE) &&
ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
return;
netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
{
struct irec *iface;
/* get the netmask of the interface whch has the address we were sent to.
This is no neccessarily the interface we arrived on. */
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == AF_INET &&
iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
break;
/* interface may be new */
if (!iface)
enumerate_interfaces();
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == AF_INET &&
iface->addr.in.sin_addr.s_addr == dst_addr_4.s_addr)
break;
/* If we failed, abandon localisation */
if (iface)
netmask = iface->netmask;
else
dst_addr_4.s_addr = 0;
}
}
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
@@ -748,8 +819,8 @@ void receive_query(struct listener *listen, time_t now)
dst_addr_4, netmask, now);
if (m >= 1)
{
send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header,
m, &source_addr, &dst_addr, if_index);
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
(char *)header, m, &source_addr, &dst_addr, if_index);
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
@@ -764,17 +835,25 @@ void receive_query(struct listener *listen, time_t now)
about resources for debug mode, when the fork is suppressed: that's
done by the caller. */
unsigned char *tcp_request(int confd, time_t now,
struct in_addr local_addr, struct in_addr netmask)
union mysockaddr *local_addr, struct in_addr netmask)
{
int size = 0, norebind = 0;
size_t size = 0;
int norebind = 0;
int checking_disabled;
size_t m;
unsigned short qtype, gotname;
unsigned char c1, c2;
/* Max TCP packet + slop */
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ);
HEADER *header;
struct dns_header *header;
struct server *last_server;
struct in_addr dst_addr_4;
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
return packet;
while (1)
{
if (!packet ||
@@ -783,51 +862,59 @@ unsigned char *tcp_request(int confd, time_t now,
!read_write(confd, packet, size, 1))
return packet;
if (size < (int)sizeof(HEADER))
if (size < (int)sizeof(struct dns_header))
continue;
header = (HEADER *)packet;
header = (struct dns_header *)packet;
/* save state of "cd" flag in query */
checking_disabled = header->hb4 & HB4_CD;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
if ((gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
{
union mysockaddr peer_addr;
socklen_t peer_len = sizeof(union mysockaddr);
char types[20];
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) != -1)
{
char types[20];
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, types);
querystr(types, qtype);
if (peer_addr.sa.sa_family == AF_INET)
log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in.sin_addr, types);
#ifdef HAVE_IPV6
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
else
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&peer_addr.in6.sin6_addr, types);
#endif
}
}
if (local_addr->sa.sa_family == AF_INET)
dst_addr_4 = local_addr->in.sin_addr;
else
dst_addr_4.s_addr = 0;
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (unsigned int)size,
local_addr, netmask, now);
dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
if (m == 0)
{
unsigned short flags = 0;
unsigned int flags = 0;
struct all_addr *addrp = NULL;
int type = 0;
char *domain = NULL;
if (option_bool(OPT_ADD_MAC))
size = add_mac(header, size, ((char *) header) + 65536, &peer_addr);
if (gotname)
flags = search_servers(now, &addrp, gotname, daemon->namebuff, &type, &domain, &norebind);
if (type != 0 || (daemon->options & OPT_ORDER) || !daemon->last_server)
if (type != 0 || option_bool(OPT_ORDER) || !daemon->last_server)
last_server = daemon->servers;
else
last_server = daemon->last_server;
@@ -857,18 +944,38 @@ unsigned char *tcp_request(int confd, time_t now,
if (type != (last_server->flags & SERV_TYPE) ||
(type == SERV_HAS_DOMAIN && !hostname_isequal(domain, last_server->domain)))
continue;
if ((last_server->tcpfd == -1) &&
(last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) != -1 &&
(!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
if (last_server->tcpfd == -1)
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
if ((last_server->tcpfd = socket(last_server->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
continue;
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
{
close(last_server->tcpfd);
last_server->tcpfd = -1;
continue;
}
#ifdef HAVE_CONNTRACK
/* Copy connection mark of incoming query to outgoing connection. */
if (option_bool(OPT_CONNTRACK))
{
unsigned int mark;
struct all_addr local;
#ifdef HAVE_IPV6
if (local_addr->sa.sa_family == AF_INET6)
local.addr.addr6 = local_addr->in6.sin6_addr;
else
#endif
local.addr.addr4 = local_addr->in.sin_addr;
if (get_incoming_mark(&peer_addr, &local, 1, &mark))
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
}
#endif
}
if (last_server->tcpfd == -1)
continue;
c1 = size >> 8;
c2 = size;
@@ -906,7 +1013,8 @@ unsigned char *tcp_request(int confd, time_t now,
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
m = process_reply(header, now, last_server, (unsigned int)m, (daemon->options & OPT_NO_REBIND) && !norebind );
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
break;
}
@@ -938,6 +1046,7 @@ static struct frec *allocate_frec(time_t now)
f->time = now;
f->sentto = NULL;
f->rfd4 = NULL;
f->flags = 0;
#ifdef HAVE_IPV6
f->rfd6 = NULL;
#endif
@@ -990,6 +1099,7 @@ static void free_frec(struct frec *f)
f->rfd4 = NULL;
f->sentto = NULL;
f->flags = 0;
#ifdef HAVE_IPV6
if (f->rfd6 && --(f->rfd6->refcount) == 0)

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
#include "dnsmasq.h"
#ifdef HAVE_SCRIPT
/* This file has code to fork a helper process which recieves data via a pipe
shared with the main process and which is responsible for calling a script when
DHCP leases change.
@@ -28,15 +30,25 @@
main process.
*/
#if defined(HAVE_DHCP) && defined(HAVE_SCRIPT)
static void my_setenv(const char *name, const char *value, int *error);
static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, char *env, int *err);
#ifdef HAVE_LUASCRIPT
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
lua_State *lua;
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field);
#endif
struct script_data
{
unsigned char action, hwaddr_len, hwaddr_type;
unsigned char clid_len, hostname_len, ed_len;
int flags;
int action, hwaddr_len, hwaddr_type;
int clid_len, hostname_len, ed_len;
struct in_addr addr, giaddr;
unsigned int remaining_time;
#ifdef HAVE_BROKEN_RTC
@@ -46,6 +58,7 @@ struct script_data
#endif
unsigned char hwaddr[DHCP_CHADDR_MAX];
char interface[IF_NAMESIZE];
};
static struct script_data *buf = NULL;
@@ -61,7 +74,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
then fork our process. */
if (pipe(pipefd) == -1 || !fix_fd(pipefd[1]) || (pid = fork()) == -1)
{
send_event(err_fd, EVENT_PIPE_ERR, errno);
send_event(err_fd, EVENT_PIPE_ERR, errno, NULL);
_exit(0);
}
@@ -79,46 +92,108 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
sigaction(SIGTERM, &sigact, NULL);
sigaction(SIGALRM, &sigact, NULL);
if (!(daemon->options & OPT_DEBUG) && uid != 0)
if (!option_bool(OPT_DEBUG) && uid != 0)
{
gid_t dummy;
if (setgroups(0, &dummy) == -1 ||
setgid(gid) == -1 ||
setuid(uid) == -1)
{
if (daemon->options & OPT_NO_FORK)
if (option_bool(OPT_NO_FORK))
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_HUSER_ERR, errno);
send_event(event_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0);
send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */
send_event(err_fd, EVENT_HUSER_ERR, errno);
send_event(err_fd, EVENT_USER_ERR, errno, daemon->scriptuser);
}
_exit(0);
}
}
/* close all the sockets etc, we don't need them here. This closes err_fd, so that
main process can return. */
/* close all the sockets etc, we don't need them here.
Don't close err_fd, in case the lua-init fails.
Note that we have to do this before lua init
so we don't close any lua fds. */
for (max_fd--; max_fd >= 0; max_fd--)
if (max_fd != STDOUT_FILENO && max_fd != STDERR_FILENO &&
max_fd != STDIN_FILENO && max_fd != pipefd[0] && max_fd != event_fd)
max_fd != STDIN_FILENO && max_fd != pipefd[0] &&
max_fd != event_fd && max_fd != err_fd)
close(max_fd);
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
const char *lua_err = NULL;
lua = lua_open();
luaL_openlibs(lua);
/* get Lua to load our script file */
if (luaL_dofile(lua, daemon->luascript) != 0)
lua_err = lua_tostring(lua, -1);
else
{
lua_getglobal(lua, "lease");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_err = _("lease() function missing in Lua script");
}
if (lua_err)
{
if (option_bool(OPT_NO_FORK) || option_bool(OPT_DEBUG))
/* send error to daemon process if no-fork */
send_event(event_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
else
{
/* kill daemon */
send_event(event_fd, EVENT_DIE, 0, NULL);
/* return error */
send_event(err_fd, EVENT_LUA_ERR, 0, (char *)lua_err);
}
_exit(0);
}
lua_pop(lua, 1); /* remove nil from stack */
lua_getglobal(lua, "init");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
else
lua_pop(lua, 1); /* remove nil from stack */
}
#endif
/* All init done, close our copy of the error pipe, so that main process can return */
if (err_fd != -1)
close(err_fd);
/* loop here */
while(1)
{
struct script_data data;
char *p, *action_str, *hostname = NULL;
char *p, *action_str, *hostname = NULL, *domain = NULL;
unsigned char *buf = (unsigned char *)daemon->namebuff;
unsigned char *end, *alloc_buff = NULL;
int err = 0;
unsigned char *end, *extradata, *alloc_buff = NULL;
int is6, err = 0;
free(alloc_buff);
/* we read zero bytes when pipe closed: this is our signal to exit */
if (!read_write(pipefd[0], (unsigned char *)&data, sizeof(data), 1))
_exit(0);
{
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
lua_getglobal(lua, "shutdown");
if (lua_type(lua, -1) == LUA_TFUNCTION)
lua_call(lua, 0, 0);
}
#endif
_exit(0);
}
is6 = !!(data.flags & (LEASE_TA | LEASE_NA));
if (data.action == ACTION_DEL)
action_str = "del";
@@ -126,55 +201,252 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
action_str = "add";
else if (data.action == ACTION_OLD || data.action == ACTION_OLD_HOSTNAME)
action_str = "old";
else if (data.action == ACTION_TFTP)
{
action_str = "tftp";
is6 = (data.flags != AF_INET);
}
else
continue;
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
/* and CLID into packet, avoid overwrite from bad data */
if ((data.clid_len > daemon->packet_buff_sz) || !read_write(pipefd[0], buf, data.clid_len, 1))
continue;
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
if (!is6)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
/* stringify MAC into dhcp_buff */
p = daemon->dhcp_buff;
if (data.hwaddr_type != ARPHRD_ETHER || data.hwaddr_len == 0)
p += sprintf(p, "%.2x-", data.hwaddr_type);
for (i = 0; (i < data.hwaddr_len) && (i < DHCP_CHADDR_MAX); i++)
{
p += sprintf(p, "%.2x", data.hwaddr[i]);
if (i != data.hwaddr_len - 1)
p += sprintf(p, ":");
}
}
/* and expiry or length into dhcp_buff2 */
/* expiry or length into dhcp_buff2 */
#ifdef HAVE_BROKEN_RTC
sprintf(daemon->dhcp_buff2, "%u", data.length);
#else
sprintf(daemon->dhcp_buff2, "%lu", (unsigned long)data.expires);
#endif
/* supplied data may just exceed normal buffer (unlikely) */
if ((data.hostname_len + data.ed_len) > daemon->packet_buff_sz &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len)))
if ((data.hostname_len + data.ed_len + data.clid_len) > MAXDNAME &&
!(alloc_buff = buf = malloc(data.hostname_len + data.ed_len + data.clid_len)))
continue;
if (!read_write(pipefd[0], buf,
data.hostname_len + data.ed_len, 1))
data.hostname_len + data.ed_len + data.clid_len, 1))
continue;
/* CLID into packet */
if (!is6)
for (p = daemon->packet, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
#ifdef HAVE_DHCP6
else
{
/* or IAID and server DUID for IPv6 */
sprintf(daemon->dhcp_buff3, "%s%u", data.flags & LEASE_TA ? "T" : "", data.hwaddr_type);
for (p = daemon->packet, i = 0; i < daemon->duid_len; i++)
{
p += sprintf(p, "%.2x", daemon->duid[i]);
if (i != daemon->duid_len - 1)
p += sprintf(p, ":");
}
/* duid not MAC for IPv6 */
for (p = daemon->dhcp_buff, i = 0; i < data.clid_len; i++)
{
p += sprintf(p, "%.2x", buf[i]);
if (i != data.clid_len - 1)
p += sprintf(p, ":");
}
}
#endif
buf += data.clid_len;
if (data.hostname_len != 0)
{
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (data.action != ACTION_TFTP)
{
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
{
domain = dot+1;
*dot = 0;
}
}
}
extradata = buf + data.hostname_len;
if (!is6)
inet_ntop(AF_INET, &data.addr, daemon->addrbuff, ADDRSTRLEN);
#ifdef HAVE_DHCP6
else
inet_ntop(AF_INET6, &data.hwaddr, daemon->addrbuff, ADDRSTRLEN);
#endif
/* file length */
if (data.action == ACTION_TFTP)
sprintf(daemon->dhcp_buff, "%u", data.hwaddr_len);
#ifdef HAVE_LUASCRIPT
if (daemon->luascript)
{
if (data.action == ACTION_TFTP)
{
lua_getglobal(lua, "tftp");
if (lua_type(lua, -1) != LUA_TFUNCTION)
lua_pop(lua, 1); /* tftp function optional */
else
{
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "destination_address");
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "file_name");
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "file_size");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
else
{
lua_getglobal(lua, "lease"); /* function to call */
lua_pushstring(lua, action_str); /* arg1 - action */
lua_newtable(lua); /* arg2 - data table */
if (is6)
{
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "client_duid");
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "server_duid");
lua_pushstring(lua, daemon->dhcp_buff3);
lua_setfield(lua, -2, "iaid");
}
if (!is6 && data.clid_len != 0)
{
lua_pushstring(lua, daemon->packet);
lua_setfield(lua, -2, "client_id");
}
if (strlen(data.interface) != 0)
{
lua_pushstring(lua, data.interface);
lua_setfield(lua, -2, "interface");
}
#ifdef HAVE_BROKEN_RTC
lua_pushnumber(lua, data.length);
lua_setfield(lua, -2, "lease_length");
#else
lua_pushnumber(lua, data.expires);
lua_setfield(lua, -2, "lease_expires");
#endif
if (hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "hostname");
}
if (domain)
{
lua_pushstring(lua, domain);
lua_setfield(lua, -2, "domain");
}
end = extradata + data.ed_len;
buf = extradata;
if (!is6)
buf = grab_extradata_lua(buf, end, "vendor_class");
#ifdef HAVE_DHCP6
else
for (i = 0; i < data.hwaddr_len; i++)
{
sprintf(daemon->dhcp_buff2, "vendor_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
#endif
buf = grab_extradata_lua(buf, end, "supplied_hostname");
if (!is6)
{
buf = grab_extradata_lua(buf, end, "cpewan_oui");
buf = grab_extradata_lua(buf, end, "cpewan_serial");
buf = grab_extradata_lua(buf, end, "cpewan_class");
}
buf = grab_extradata_lua(buf, end, "tags");
if (is6)
buf = grab_extradata_lua(buf, end, "relay_address");
else if (data.giaddr.s_addr != 0)
{
lua_pushstring(lua, inet_ntoa(data.giaddr));
lua_setfield(lua, -2, "relay_address");
}
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "user_class%i", i);
buf = grab_extradata_lua(buf, end, daemon->dhcp_buff2);
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
lua_pushnumber(lua, data.remaining_time);
lua_setfield(lua, -2, "time_remaining");
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
lua_pushstring(lua, hostname);
lua_setfield(lua, -2, "old_hostname");
}
if (!is6)
{
lua_pushstring(lua, daemon->dhcp_buff);
lua_setfield(lua, -2, "mac_address");
}
lua_pushstring(lua, daemon->addrbuff);
lua_setfield(lua, -2, "ip_address");
lua_call(lua, 2, 0); /* pass 2 values, expect 0 */
}
}
#endif
/* no script, just lua */
if (!daemon->lease_change_command)
continue;
/* possible fork errors are all temporary resource problems */
while ((pid = fork()) == -1 && (errno == EAGAIN || errno == ENOMEM))
sleep(2);
free(alloc_buff);
if (pid == -1)
continue;
/* wait for child to complete */
if (pid != 0)
{
@@ -188,9 +460,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{
/* On error send event back to main process for logging */
if (WIFSIGNALED(status))
send_event(event_fd, EVENT_KILLED, WTERMSIG(status));
send_event(event_fd, EVENT_KILLED, WTERMSIG(status), NULL);
else if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status));
send_event(event_fd, EVENT_EXITED, WEXITSTATUS(status), NULL);
break;
}
@@ -201,62 +473,87 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
continue;
}
if (data.clid_len != 0)
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0)
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
if (data.hostname_len != 0)
if (data.action != ACTION_TFTP)
{
char *dot;
hostname = (char *)buf;
hostname[data.hostname_len - 1] = 0;
if (!legal_hostname(hostname))
hostname = NULL;
else if ((dot = strchr(hostname, '.')))
if (is6)
{
my_setenv("DNSMASQ_DOMAIN", dot+1, &err);
*dot = 0;
}
buf += data.hostname_len;
}
end = buf + data.ed_len;
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
if (data.action != ACTION_DEL)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
hostname = NULL;
my_setenv("DNSMASQ_IAID", daemon->dhcp_buff3, &err);
my_setenv("DNSMASQ_SERVER_DUID", daemon->packet, &err);
}
if (!is6 && data.clid_len != 0)
my_setenv("DNSMASQ_CLIENT_ID", daemon->packet, &err);
if (strlen(data.interface) != 0)
my_setenv("DNSMASQ_INTERFACE", data.interface, &err);
#ifdef HAVE_BROKEN_RTC
my_setenv("DNSMASQ_LEASE_LENGTH", daemon->dhcp_buff2, &err);
#else
my_setenv("DNSMASQ_LEASE_EXPIRES", daemon->dhcp_buff2, &err);
#endif
if (domain)
my_setenv("DNSMASQ_DOMAIN", domain, &err);
end = extradata + data.ed_len;
buf = extradata;
if (!is6)
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS", &err);
#ifdef HAVE_DHCP6
else
{
if (data.hwaddr_len != 0)
{
buf = grab_extradata(buf, end, "DNSMASQ_VENDOR_CLASS_ID", &err);
for (i = 0; i < data.hwaddr_len - 1; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_VENDOR_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
}
}
#endif
buf = grab_extradata(buf, end, "DNSMASQ_SUPPLIED_HOSTNAME", &err);
if (!is6)
{
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_OUI", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_SERIAL", &err);
buf = grab_extradata(buf, end, "DNSMASQ_CPEWAN_CLASS", &err);
}
buf = grab_extradata(buf, end, "DNSMASQ_TAGS", &err);
if (is6)
buf = grab_extradata(buf, end, "DNSMASQ_RELAY_ADDRESS", &err);
else if (data.giaddr.s_addr != 0)
my_setenv("DNSMASQ_RELAY_ADDRESS", inet_ntoa(data.giaddr), &err);
for (i = 0; buf; i++)
{
sprintf(daemon->dhcp_buff2, "DNSMASQ_USER_CLASS%i", i);
buf = grab_extradata(buf, end, daemon->dhcp_buff2, &err);
}
if (data.action != ACTION_DEL && data.remaining_time != 0)
{
sprintf(daemon->dhcp_buff2, "%u", data.remaining_time);
my_setenv("DNSMASQ_TIME_REMAINING", daemon->dhcp_buff2, &err);
}
if (data.action == ACTION_OLD_HOSTNAME && hostname)
{
my_setenv("DNSMASQ_OLD_HOSTNAME", hostname, &err);
hostname = NULL;
}
}
if (option_bool(OPT_LOG_OPTS))
my_setenv("DNSMASQ_LOG_DHCP", "1", &err);
/* we need to have the event_fd around if exec fails */
if ((i = fcntl(event_fd, F_GETFD)) != -1)
fcntl(event_fd, F_SETFD, i | FD_CLOEXEC);
@@ -267,11 +564,11 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd)
{
execl(daemon->lease_change_command,
p ? p+1 : daemon->lease_change_command,
action_str, daemon->dhcp_buff, inet_ntoa(data.addr), hostname, (char*)NULL);
action_str, daemon->dhcp_buff, daemon->addrbuff, hostname, (char*)NULL);
err = errno;
}
/* failed, send event so the main process logs the problem */
send_event(event_fd, EVENT_EXEC_ERR, err);
send_event(event_fd, EVENT_EXEC_ERR, err, NULL);
_exit(0);
}
}
@@ -305,26 +602,30 @@ static unsigned char *grab_extradata(unsigned char *buf, unsigned char *end, ch
return next + 1;
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
#ifdef HAVE_LUASCRIPT
static unsigned char *grab_extradata_lua(unsigned char *buf, unsigned char *end, char *field)
{
unsigned char *p;
size_t size;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
unsigned char *next;
if (!buf || (buf == end))
return NULL;
for (next = buf; *next != 0; next++)
if (next == end)
return NULL;
/* no script */
if (daemon->helperfd == -1)
return;
if (next != buf)
{
lua_pushstring(lua, (char *)buf);
lua_setfield(lua, -2, field);
}
if (lease->extradata)
ed_len = lease->extradata_len;
if (lease->clid)
clid_len = lease->clid_len;
if (hostname)
hostname_len = strlen(hostname) + 1;
size = sizeof(struct script_data) + clid_len + ed_len + hostname_len;
return next + 1;
}
#endif
static void buff_alloc(size_t size)
{
if (size > buf_size)
{
struct script_data *new;
@@ -340,8 +641,35 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
buf = new;
buf_size = size;
}
}
/* pack up lease data into a buffer */
void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t now)
{
unsigned char *p;
unsigned int hostname_len = 0, clid_len = 0, ed_len = 0;
int fd = daemon->dhcpfd;
#ifdef HAVE_DHCP6
if (!daemon->dhcp)
fd = daemon->dhcp6fd;
#endif
/* no script */
if (daemon->helperfd == -1)
return;
if (lease->extradata)
ed_len = lease->extradata_len;
if (lease->clid)
clid_len = lease->clid_len;
if (hostname)
hostname_len = strlen(hostname) + 1;
buff_alloc(sizeof(struct script_data) + clid_len + ed_len + hostname_len);
buf->action = action;
buf->flags = lease->flags;
buf->hwaddr_len = lease->hwaddr_len;
buf->hwaddr_type = lease->hwaddr_type;
buf->clid_len = clid_len;
@@ -349,8 +677,8 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
buf->hostname_len = hostname_len;
buf->addr = lease->addr;
buf->giaddr = lease->giaddr;
memcpy(buf->hwaddr, lease->hwaddr, lease->hwaddr_len);
if (!indextoname(daemon->dhcpfd, lease->last_interface, buf->interface))
memcpy(buf->hwaddr, lease->hwaddr, DHCP_CHADDR_MAX);
if (!indextoname(fd, lease->last_interface, buf->interface))
buf->interface[0] = 0;
#ifdef HAVE_BROKEN_RTC
@@ -358,7 +686,11 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
#else
buf->expires = lease->expires;
#endif
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
if (lease->expires != 0)
buf->remaining_time = (unsigned int)difftime(lease->expires, now);
else
buf->remaining_time = 0;
p = (unsigned char *)(buf+1);
if (clid_len != 0)
@@ -379,6 +711,37 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n
bytes_in_buf = p - (unsigned char *)buf;
}
#ifdef HAVE_TFTP
/* This nastily re-uses DHCP-fields for TFTP stuff */
void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer)
{
unsigned int filename_len;
/* no script */
if (daemon->helperfd == -1)
return;
filename_len = strlen(filename) + 1;
buff_alloc(sizeof(struct script_data) + filename_len);
memset(buf, 0, sizeof(struct script_data));
buf->action = ACTION_TFTP;
buf->hostname_len = filename_len;
buf->hwaddr_len = file_len;
if ((buf->flags = peer->sa.sa_family) == AF_INET)
buf->addr = peer->in.sin_addr;
#ifdef HAVE_IPV6
else
memcpy(buf->hwaddr, &peer->in6.sin6_addr, IN6ADDRSZ);
#endif
memcpy((unsigned char *)(buf+1), filename, filename_len);
bytes_in_buf = sizeof(struct script_data) + filename_len;
}
#endif
int helper_buf_empty(void)
{
return bytes_in_buf == 0;
@@ -408,3 +771,4 @@ void helper_write(void)
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,20 +24,14 @@ static int dns_dirty, file_dirty, leases_left;
void lease_init(time_t now)
{
unsigned long ei;
struct in_addr addr;
struct all_addr addr;
struct dhcp_lease *lease;
int clid_len, hw_len, hw_type;
FILE *leasestream;
/* These each hold a DHCP option max size 255
and get a terminating zero added */
daemon->dhcp_buff = safe_malloc(256);
daemon->dhcp_buff2 = safe_malloc(256);
daemon->dhcp_buff3 = safe_malloc(256);
leases_left = daemon->dhcp_max;
if (daemon->options & OPT_LEASE_RO)
if (option_bool(OPT_LEASE_RO))
{
/* run "<lease_change_script> init" once to get the
initial state of the database. If leasefile-ro is
@@ -73,23 +67,69 @@ void lease_init(time_t now)
/* client-id max length is 255 which is 255*2 digits + 254 colons
borrow DNS packet buffer which is always larger than 1000 bytes */
if (leasestream)
while (fscanf(leasestream, "%lu %255s %16s %255s %764s",
&ei, daemon->dhcp_buff2, daemon->namebuff,
daemon->dhcp_buff, daemon->packet) == 5)
while (fscanf(leasestream, "%255s %255s", daemon->dhcp_buff3, daemon->dhcp_buff2) == 2)
{
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
#ifdef HAVE_DHCP6
if (strcmp(daemon->dhcp_buff3, "duid") == 0)
{
daemon->duid_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, 130, NULL, NULL);
daemon->duid = safe_malloc(daemon->duid_len);
memcpy(daemon->duid, daemon->dhcp_buff2, daemon->duid_len);
continue;
}
#endif
ei = atol(daemon->dhcp_buff3);
addr.s_addr = inet_addr(daemon->namebuff);
if (fscanf(leasestream, " %64s %255s %764s",
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
break;
/* decode hex in place */
clid_len = 0;
if (strcmp(daemon->packet, "*") != 0)
clid_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
if (!(lease = lease_allocate(addr)))
if (inet_pton(AF_INET, daemon->namebuff, &addr.addr.addr4) &&
(lease = lease4_allocate(addr.addr.addr4)))
{
hw_len = parse_hex(daemon->dhcp_buff2, (unsigned char *)daemon->dhcp_buff2, DHCP_CHADDR_MAX, NULL, &hw_type);
/* For backwards compatibility, no explict MAC address type means ether. */
if (hw_type == 0 && hw_len != 0)
hw_type = ARPHRD_ETHER;
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet,
hw_len, hw_type, clid_len, now, 0);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain(lease->addr), NULL);
}
#ifdef HAVE_DHCP6
else if (inet_pton(AF_INET6, daemon->namebuff, &addr.addr.addr6))
{
char *s = daemon->dhcp_buff2;
int lease_type = LEASE_NA;
if (s[0] == 'T')
{
lease_type = LEASE_TA;
s++;
}
hw_type = atoi(s);
if ((lease = lease6_allocate(&addr.addr.addr6, lease_type)))
{
lease_set_hwaddr(lease, NULL, (unsigned char *)daemon->packet, 0, hw_type, clid_len, now, 0);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0, get_domain6((struct in6_addr *)lease->hwaddr), NULL);
}
}
#endif
else
break;
if (!lease)
die (_("too many stored leases"), NULL, EC_MISC);
#ifdef HAVE_BROKEN_RTC
@@ -104,14 +144,9 @@ void lease_init(time_t now)
lease->expires = (time_t)ei;
#endif
lease_set_hwaddr(lease, (unsigned char *)daemon->dhcp_buff2, (unsigned char *)daemon->packet, hw_len, hw_type, clid_len);
if (strcmp(daemon->dhcp_buff, "*") != 0)
lease_set_hostname(lease, daemon->dhcp_buff, 0);
/* set these correctly: the "old" events are generated later from
the startup synthesised SIGHUP. */
lease->new = lease->changed = 0;
lease->flags &= ~(LEASE_NEW | LEASE_CHANGED);
}
#ifdef HAVE_SCRIPT
@@ -150,17 +185,17 @@ void lease_update_from_configs(void)
struct dhcp_lease *lease;
struct dhcp_config *config;
char *name;
for (lease = leases; lease; lease = lease->next)
if ((config = find_config(daemon->dhcp_conf, NULL, lease->clid, lease->clid_len,
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, NULL)) &&
(config->flags & CONFIG_NAME) &&
(!(config->flags & CONFIG_ADDR) || config->addr.s_addr == lease->addr.s_addr))
lease_set_hostname(lease, config->hostname, 1);
lease_set_hostname(lease, config->hostname, 1, get_domain(lease->addr), NULL);
else if ((name = host_from_dns(lease->addr)))
lease_set_hostname(lease, name, 1); /* updates auth flag only */
lease_set_hostname(lease, name, 1, get_domain(lease->addr), NULL); /* updates auth flag only */
}
static void ourprintf(int *errp, char *format, ...)
{
va_list ap;
@@ -186,11 +221,18 @@ void lease_update_file(time_t now)
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
if (lease->hwaddr_type != ARPHRD_ETHER || lease->hwaddr_len == 0)
ourprintf(&err, "%.2x-", lease->hwaddr_type);
for (i = 0; i < lease->hwaddr_len; i++)
@@ -199,8 +241,10 @@ void lease_update_file(time_t now)
if (i != lease->hwaddr_len - 1)
ourprintf(&err, ":");
}
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
ourprintf(&err, " %s ", inet_ntoa(lease->addr));
ourprintf(&err, " %s ", daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
@@ -213,6 +257,44 @@ void lease_update_file(time_t now)
ourprintf(&err, "*\n");
}
#ifdef HAVE_DHCP6
if (daemon->duid)
{
ourprintf(&err, "duid ");
for (i = 0; i < daemon->duid_len - 1; i++)
ourprintf(&err, "%.2x:", daemon->duid[i]);
ourprintf(&err, "%.2x\n", daemon->duid[i]);
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
#ifdef HAVE_BROKEN_RTC
ourprintf(&err, "%u ", lease->length);
#else
ourprintf(&err, "%lu ", (unsigned long)lease->expires);
#endif
inet_ntop(AF_INET6, lease->hwaddr, daemon->addrbuff, ADDRSTRLEN);
ourprintf(&err, "%s%u %s ", (lease->flags & LEASE_TA) ? "T" : "",
lease->hwaddr_type, daemon->addrbuff);
ourprintf(&err, "%s ", lease->hostname ? lease->hostname : "*");
if (lease->clid && lease->clid_len != 0)
{
for (i = 0; i < lease->clid_len - 1; i++)
ourprintf(&err, "%.2x:", lease->clid[i]);
ourprintf(&err, "%.2x\n", lease->clid[i]);
}
else
ourprintf(&err, "*\n");
}
}
#endif
if (fflush(daemon->lease_stream) != 0 ||
fsync(fileno(daemon->lease_stream)) < 0)
err = errno;
@@ -222,7 +304,29 @@ void lease_update_file(time_t now)
}
/* Set alarm for when the first lease expires + slop. */
for (next_event = 0, lease = leases; lease; lease = lease->next)
next_event = 0;
#ifdef HAVE_DHCP6
/* do timed RAs and determine when the next is, also pings to potential SLAAC addresses */
if (daemon->ra_contexts)
{
time_t event;
if ((event = periodic_slaac(now, leases)) != 0)
{
if (next_event == 0 || difftime(next_event, event) > 0.0)
next_event = event;
}
if ((event = periodic_ra(now)) != 0)
{
if (next_event == 0 || difftime(next_event, event) > 0.0)
next_event = event;
}
}
#endif
for (lease = leases; lease; lease = lease->next)
if (lease->expires != 0 &&
(next_event == 0 || difftime(next_event, lease->expires + 10) > 0.0))
next_event = lease->expires + 10;
@@ -237,25 +341,118 @@ void lease_update_file(time_t now)
(unsigned int)difftime(next_event, now));
}
if (next_event != 0)
alarm((unsigned)difftime(next_event, now));
send_alarm(next_event, now);
}
void lease_update_dns(void)
static int find_interface_v4(struct in_addr local, int if_index,
struct in_addr netmask, struct in_addr broadcast, void *vparam)
{
struct dhcp_lease *lease;
if (daemon->port != 0 && dns_dirty)
(void) broadcast;
(void) vparam;
for (lease = leases; lease; lease = lease->next)
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net(local, lease->addr, netmask))
lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
#ifdef HAVE_DHCP6
static int find_interface_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_lease *lease;
(void)scope;
(void)dad;
for (lease = leases; lease; lease = lease->next)
if ((lease->flags & (LEASE_TA | LEASE_NA)))
if (is_same_net6(local, (struct in6_addr *)&lease->hwaddr, prefix))
lease_set_interface(lease, if_index, *((time_t *)vparam));
return 1;
}
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface)
{
/* We may be doing RA but not DHCPv4, in which case the lease
database may not exist and we have nothing to do anyway */
if (daemon->dhcp)
slaac_ping_reply(sender, packet, interface, leases);
}
#endif
/* Find interfaces associated with leases at start-up. This gets updated as
we do DHCP transactions, but information about directly-connected subnets
is useful from scrips and necessary for determining SLAAC addresses from
start-time. */
void lease_find_interfaces(time_t now)
{
#ifdef HAVE_DHCP6
build_subnet_map();
#endif
iface_enumerate(AF_INET, &now, find_interface_v4);
#ifdef HAVE_DHCP6
iface_enumerate(AF_INET6, &now, find_interface_v6);
/* If we're not doing DHCPv6, and there are not v6 leases, don't add the DUID to the database */
if (!daemon->duid && daemon->dhcp6)
{
file_dirty = 1;
make_duid(now);
}
#endif
}
void lease_update_dns(int force)
{
struct dhcp_lease *lease;
if (daemon->port != 0 && (dns_dirty || force))
{
cache_unhash_dhcp();
for (lease = leases; lease; lease = lease->next)
{
int prot = AF_INET;
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
prot = AF_INET6;
else if (lease->hostname || lease->fqdn)
{
struct slaac_address *slaac;
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
if (slaac->backoff == 0)
{
if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, AF_INET6, (struct all_addr *)&slaac->addr, lease->expires);
}
}
#endif
if (lease->fqdn)
cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
cache_add_dhcp_entry(lease->fqdn, prot,
prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr,
lease->expires);
if (!(daemon->options & OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
if (!option_bool(OPT_DHCP_FQDN) && lease->hostname)
cache_add_dhcp_entry(lease->hostname, prot,
prot == AF_INET ? (struct all_addr *)&lease->addr : (struct all_addr *)&lease->hwaddr,
lease->expires);
}
dns_dirty = 0;
@@ -275,7 +472,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
if (lease->hostname)
dns_dirty = 1;
*up = lease->next; /* unlink */
*up = lease->next; /* unlink */
/* Put on old_leases list 'till we
can run the script */
@@ -297,18 +494,30 @@ struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int h
if (clid)
for (lease = leases; lease; lease = lease->next)
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (lease->clid && clid_len == lease->clid_len &&
memcmp(clid, lease->clid, clid_len) == 0)
return lease;
}
for (lease = leases; lease; lease = lease->next)
if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if ((!lease->clid || !clid) &&
hw_len != 0 &&
lease->hwaddr_len == hw_len &&
lease->hwaddr_type == hw_type &&
memcmp(hwaddr, lease->hwaddr, hw_len) == 0)
return lease;
}
return NULL;
}
@@ -317,23 +526,138 @@ struct dhcp_lease *lease_find_by_addr(struct in_addr addr)
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
if (lease->addr.s_addr == addr.s_addr)
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (lease->addr.s_addr == addr.s_addr)
return lease;
}
return NULL;
}
#ifdef HAVE_DHCP6
/* addr or clid may be NULL for "don't care, both NULL resets "USED" flags both
set activates USED check */
struct dhcp_lease *lease6_find(unsigned char *clid, int clid_len,
int lease_type, int iaid, struct in6_addr *addr)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
if (clid && addr && (lease->flags & LEASE_USED))
continue;
if (addr && memcmp(lease->hwaddr, addr, IN6ADDRSZ) != 0)
continue;
if (clid &&
(clid_len != lease->clid_len ||
memcmp(clid, lease->clid, clid_len) != 0))
continue;
lease->flags |= LEASE_USED;
return lease;
}
return NULL;
}
void lease6_filter(int lease_type, int iaid, struct dhcp_context *context)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
/* reset "USED flag */
lease->flags &= ~LEASE_USED;
if (!(lease->flags & lease_type) || lease->hwaddr_type != iaid)
continue;
/* leases on the wrong interface get filtered out here */
if (!address6_available(context, (struct in6_addr *)&lease->hwaddr, NULL))
lease->flags |= LEASE_USED;
}
}
struct dhcp_lease *lease_allocate(struct in_addr addr)
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr)
{
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
if (is_same_net6((struct in6_addr *)lease->hwaddr, net, prefix) &&
(prefix == 128 || addr6part((struct in6_addr *)lease->hwaddr) == addr))
return lease;
}
return NULL;
}
/* Find largest assigned address in context */
u64 lease_find_max_addr6(struct dhcp_context *context)
{
struct dhcp_lease *lease;
u64 addr = addr6part(&context->start6);
if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
for (lease = leases; lease; lease = lease->next)
{
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
continue;
if (is_same_net6((struct in6_addr *)lease->hwaddr, &context->start6, 64) &&
addr6part((struct in6_addr *)lease->hwaddr) > addr6part(&context->start6) &&
addr6part((struct in6_addr *)lease->hwaddr) <= addr6part(&context->end6) &&
addr6part((struct in6_addr *)lease->hwaddr) > addr)
addr = addr6part((struct in6_addr *)lease->hwaddr);
}
return addr;
}
#endif
/* Find largest assigned address in context */
struct in_addr lease_find_max_addr(struct dhcp_context *context)
{
struct dhcp_lease *lease;
struct in_addr addr = context->start;
if (!(context->flags & (CONTEXT_STATIC | CONTEXT_PROXY)))
for (lease = leases; lease; lease = lease->next)
{
#ifdef HAVE_DHCP6
if (lease->flags & (LEASE_TA | LEASE_NA))
continue;
#endif
if (((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(context->start.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) <= ((unsigned)ntohl(context->end.s_addr)) &&
((unsigned)ntohl(lease->addr.s_addr)) > ((unsigned)ntohl(addr.s_addr)))
addr = lease->addr;
}
return addr;
}
static struct dhcp_lease *lease_allocate(void)
{
struct dhcp_lease *lease;
if (!leases_left || !(lease = whine_malloc(sizeof(struct dhcp_lease))))
return NULL;
memset(lease, 0, sizeof(struct dhcp_lease));
lease->new = 1;
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
lease->flags = LEASE_NEW;
lease->expires = 1;
#ifdef HAVE_BROKEN_RTC
lease->length = 0xffffffff; /* illegal value */
@@ -347,6 +671,26 @@ struct dhcp_lease *lease_allocate(struct in_addr addr)
return lease;
}
struct dhcp_lease *lease4_allocate(struct in_addr addr)
{
struct dhcp_lease *lease = lease_allocate();
lease->addr = addr;
lease->hwaddr_len = 256; /* illegal value */
return lease;
}
#ifdef HAVE_DHCP6
struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
{
struct dhcp_lease *lease = lease_allocate();
memcpy(lease->hwaddr, addrp, sizeof(*addrp)) ;
lease->flags |= lease_type;
return lease;
}
#endif
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
{
time_t exp = now + (time_t)len;
@@ -362,7 +706,8 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
dns_dirty = 1;
lease->expires = exp;
#ifndef HAVE_BROKEN_RTC
lease->aux_changed = file_dirty = 1;
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
#endif
}
@@ -370,22 +715,36 @@ void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now)
if (len != lease->length)
{
lease->length = len;
lease->aux_changed = file_dirty = 1;
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
}
#endif
}
void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
unsigned char *clid, int hw_len, int hw_type, int clid_len)
unsigned char *clid, int hw_len, int hw_type, int clid_len,
time_t now, int force)
{
#ifdef HAVE_DHCP6
int change = force;
lease->flags |= LEASE_HAVE_HWADDR;
#endif
(void)force;
if (hw_len != lease->hwaddr_len ||
hw_type != lease->hwaddr_type ||
(hw_len != 0 && memcmp(lease->hwaddr, hwaddr, hw_len) != 0))
{
memcpy(lease->hwaddr, hwaddr, hw_len);
if (hw_len != 0)
memcpy(lease->hwaddr, hwaddr, hw_len);
lease->hwaddr_len = hw_len;
lease->hwaddr_type = hw_type;
lease->changed = file_dirty = 1; /* run script on change */
lease->flags |= LEASE_CHANGED;
file_dirty = 1; /* run script on change */
#ifdef HAVE_DHCP6
change = 1;
#endif
}
/* only update clid when one is available, stops packets
@@ -398,18 +757,32 @@ void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
if (lease->clid_len != clid_len)
{
lease->aux_changed = file_dirty = 1;
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
free(lease->clid);
if (!(lease->clid = whine_malloc(clid_len)))
return;
#ifdef HAVE_DHCP6
change = 1;
#endif
}
else if (memcmp(lease->clid, clid, clid_len) != 0)
lease->aux_changed = file_dirty = 1;
{
lease->flags |= LEASE_AUX_CHANGED;
file_dirty = 1;
#ifdef HAVE_DHCP6
change = 1;
#endif
}
lease->clid_len = clid_len;
memcpy(lease->clid, clid, clid_len);
}
#ifdef HAVE_DHCP6
if (change)
slaac_add_addrs(lease, now, force);
#endif
}
static void kill_name(struct dhcp_lease *lease)
@@ -421,7 +794,7 @@ static void kill_name(struct dhcp_lease *lease)
free(lease->old_hostname);
/* If we know the fqdn, pass that. The helper will derive the
unqualified name from it, free the unqulaified name here. */
unqualified name from it, free the unqualified name here. */
if (lease->fqdn)
{
@@ -434,14 +807,18 @@ static void kill_name(struct dhcp_lease *lease)
lease->hostname = lease->fqdn = NULL;
}
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth, char *domain, char *config_domain)
{
struct dhcp_lease *lease_tmp;
char *new_name = NULL, *new_fqdn = NULL;
if (config_domain && (!domain || !hostname_isequal(domain, config_domain)))
my_syslog(MS_DHCP | LOG_WARNING, _("Ignoring domain %s for DHCP host name %s"), config_domain, name);
if (lease->hostname && name && hostname_isequal(lease->hostname, name))
{
lease->auth_name = auth;
if (auth)
lease->flags |= LEASE_AUTH_NAME;
return;
}
@@ -451,28 +828,30 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
/* If a machine turns up on a new net without dropping the old lease,
or two machines claim the same name, then we end up with two interfaces with
the same name. Check for that here and remove the name from the old lease.
Note that IPv6 leases are different. All the leases to the same DUID are
allowed the same name.
Don't allow a name from the client to override a name from dnsmasq config. */
if (name)
{
if ((new_name = whine_malloc(strlen(name) + 1)))
{
char *suffix = get_domain(lease->addr);
strcpy(new_name, name);
if (suffix && (new_fqdn = whine_malloc(strlen(new_name) + strlen(suffix) + 2)))
if (domain && (new_fqdn = whine_malloc(strlen(new_name) + strlen(domain) + 2)))
{
strcpy(new_fqdn, name);
strcat(new_fqdn, ".");
strcat(new_fqdn, suffix);
strcat(new_fqdn, domain);
}
}
/* Depending on mode, we check either unqualified name or FQDN. */
for (lease_tmp = leases; lease_tmp; lease_tmp = lease_tmp->next)
{
if (daemon->options & OPT_DHCP_FQDN)
if (option_bool(OPT_DHCP_FQDN))
{
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn) )
if (!new_fqdn || !lease_tmp->fqdn || !hostname_isequal(lease_tmp->fqdn, new_fqdn))
continue;
}
else
@@ -480,8 +859,22 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
if (!new_name || !lease_tmp->hostname || !hostname_isequal(lease_tmp->hostname, new_name) )
continue;
}
if (lease_tmp->auth_name && !auth)
if (lease->flags & (LEASE_TA | LEASE_NA))
{
if (!(lease_tmp->flags & (LEASE_TA | LEASE_NA)))
continue;
/* another lease for the same DUID is OK for IPv6 */
if (lease->clid_len == lease_tmp->clid_len &&
lease->clid && lease_tmp->clid &&
memcmp(lease->clid, lease_tmp->clid, lease->clid_len) == 0)
continue;
}
else if (lease_tmp->flags & (LEASE_TA | LEASE_NA))
continue;
if ((lease_tmp->flags & LEASE_AUTH_NAME) && !auth)
{
free(new_name);
free(new_fqdn);
@@ -498,20 +891,26 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, int auth)
lease->hostname = new_name;
lease->fqdn = new_fqdn;
lease->auth_name = auth;
if (auth)
lease->flags |= LEASE_AUTH_NAME;
file_dirty = 1;
dns_dirty = 1;
lease->changed = 1; /* run script on change */
lease->flags |= LEASE_CHANGED; /* run script on change */
}
void lease_set_interface(struct dhcp_lease *lease, int interface)
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
{
if (lease->last_interface == interface)
return;
lease->last_interface = interface;
lease->changed = 1;
lease->flags |= LEASE_CHANGED;
#ifdef HAVE_DHCP6
slaac_add_addrs(lease, now, 0);
#endif
}
void rerun_scripts(void)
@@ -519,7 +918,7 @@ void rerun_scripts(void)
struct dhcp_lease *lease;
for (lease = leases; lease; lease = lease->next)
lease->changed = 1;
lease->flags |= LEASE_CHANGED;
}
/* deleted leases get transferred to the old_leases list.
@@ -534,7 +933,7 @@ int do_script_run(time_t now)
#ifdef HAVE_DBUS
/* If we're going to be sending DBus signals, but the connection is not yet up,
delay everything until it is. */
if ((daemon->options & OPT_DBUS) && !daemon->dbus)
if (option_bool(OPT_DBUS) && !daemon->dbus)
return 0;
#endif
@@ -554,6 +953,14 @@ int do_script_run(time_t now)
}
else
{
#ifdef HAVE_DHCP6
struct slaac_address *slaac, *tmp;
for (slaac = lease->slaac_address; slaac; slaac = tmp)
{
tmp = slaac->next;
free(slaac);
}
#endif
kill_name(lease);
#ifdef HAVE_SCRIPT
queue_script(ACTION_DEL, lease, lease->old_hostname, now);
@@ -585,18 +992,18 @@ int do_script_run(time_t now)
}
for (lease = leases; lease; lease = lease->next)
if (lease->new || lease->changed ||
(lease->aux_changed && (daemon->options & OPT_LEASE_RO)))
if ((lease->flags & (LEASE_NEW | LEASE_CHANGED)) ||
((lease->flags & LEASE_AUX_CHANGED) && option_bool(OPT_LEASE_RO)))
{
#ifdef HAVE_SCRIPT
queue_script(lease->new ? ACTION_ADD : ACTION_OLD, lease,
queue_script((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname, now);
#endif
#ifdef HAVE_DBUS
emit_dbus_signal(lease->new ? ACTION_ADD : ACTION_OLD, lease,
emit_dbus_signal((lease->flags & LEASE_NEW) ? ACTION_ADD : ACTION_OLD, lease,
lease->fqdn ? lease->fqdn : lease->hostname);
#endif
lease->new = lease->changed = lease->aux_changed = 0;
lease->flags &= ~(LEASE_NEW | LEASE_CHANGED | LEASE_AUX_CHANGED);
/* this is used for the "add" call, then junked, since they're not in the database */
free(lease->extradata);
@@ -608,6 +1015,44 @@ int do_script_run(time_t now)
return 0; /* nothing to do */
}
#ifdef HAVE_SCRIPT
void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data, unsigned int len, int delim)
{
unsigned int i;
/* check for embeded NULLs */
for (i = 0; i < len; i++)
if (data[i] == 0)
{
len = i;
break;
}
if ((lease->extradata_size - lease->extradata_len) < (len + 1))
{
size_t newsz = lease->extradata_len + len + 100;
unsigned char *new = whine_malloc(newsz);
if (!new)
return;
if (lease->extradata)
{
memcpy(new, lease->extradata, lease->extradata_len);
free(lease->extradata);
}
lease->extradata = new;
lease->extradata_size = newsz;
}
if (len != 0)
memcpy(lease->extradata + lease->extradata_len, data, len);
lease->extradata[lease->extradata_len + len] = delim;
lease->extradata_len += len + 1;
}
#endif
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,6 +16,10 @@
#include "dnsmasq.h"
#ifdef __ANDROID__
# include <android/log.h>
#endif
/* Implement logging to /dev/log asynchronously. If syslogd is
making DNS lookups through dnsmasq, and dnsmasq blocks awaiting
syslogd, then the two daemons can deadlock. We get around this
@@ -55,12 +59,12 @@ int log_start(struct passwd *ent_pw, int errfd)
{
int ret = 0;
echo_stderr = !!(daemon->options & OPT_DEBUG);
echo_stderr = option_bool(OPT_DEBUG);
if (daemon->log_fac != -1)
log_fac = daemon->log_fac;
#ifdef LOG_LOCAL0
else if (daemon->options & OPT_DEBUG)
else if (option_bool(OPT_DEBUG))
log_fac = LOG_LOCAL0;
#endif
@@ -80,7 +84,7 @@ int log_start(struct passwd *ent_pw, int errfd)
if (!log_reopen(daemon->log_file))
{
send_event(errfd, EVENT_LOG_ERR, errno);
send_event(errfd, EVENT_LOG_ERR, errno, daemon->log_file ? daemon->log_file : "");
_exit(0);
}
@@ -117,7 +121,7 @@ int log_reopen(char *log_file)
log_fd = open(log_file, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP);
else
{
#ifdef HAVE_SOLARIS_NETWORK
#if defined(HAVE_SOLARIS_NETWORK) || defined(__ANDROID__)
/* Solaris logging is "different", /dev/log is not unix-domain socket.
Just leave log_fd == -1 and use the vsyslog call for everything.... */
# define _PATH_LOG "" /* dummy */
@@ -150,6 +154,19 @@ static void log_write(void)
while (entries)
{
/* The data in the payoad is written with a terminating zero character
and the length reflects this. For a stream connection we need to
send the zero as a record terminator, but this isn't done for a
datagram connection, so treat the length as one less than reality
to elide the zero. If we're logging to a file, turn the zero into
a newline, and leave the length alone. */
int len_adjust = 0;
if (log_to_file)
entries->payload[entries->offset + entries->length - 1] = '\n';
else if (connection_type == SOCK_DGRAM)
len_adjust = 1;
/* Avoid duplicates over a fork() */
if (entries->pid != getpid())
{
@@ -159,11 +176,11 @@ static void log_write(void)
connection_good = 1;
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length)) != -1)
if ((rc = write(log_fd, entries->payload + entries->offset, entries->length - len_adjust)) != -1)
{
entries->length -= rc;
entries->offset += rc;
if (entries->length == 0)
if (entries->length == len_adjust)
{
free_entry();
if (entries_lost != 0)
@@ -179,7 +196,7 @@ static void log_write(void)
if (errno == EINTR)
continue;
if (errno == EAGAIN)
if (errno == EAGAIN || errno == EWOULDBLOCK)
return; /* syslogd busy, go again when select() or poll() says so */
if (errno == ENOBUFS)
@@ -227,7 +244,8 @@ static void log_write(void)
errno == ECONNREFUSED ||
errno == EISCONN ||
errno == EINTR ||
errno == EAGAIN)
errno == EAGAIN ||
errno == EWOULDBLOCK)
{
/* try again on next syslog() call */
connection_good = 0;
@@ -289,8 +307,28 @@ void my_syslog(int priority, const char *format, ...)
if (log_fd == -1)
{
/* fall-back to syslog if we die during startup or fail during running. */
#ifdef __ANDROID__
/* do android-specific logging.
log_fd is always -1 on Android except when logging to a file. */
int alog_lvl;
if (priority <= LOG_ERR)
alog_lvl = ANDROID_LOG_ERROR;
else if (priority == LOG_WARNING)
alog_lvl = ANDROID_LOG_WARN;
else if (priority <= LOG_INFO)
alog_lvl = ANDROID_LOG_INFO;
else
alog_lvl = ANDROID_LOG_DEBUG;
va_start(ap, format);
__android_log_vprint(alog_lvl, "dnsmasq", format, ap);
va_end(ap);
#else
/* fall-back to syslog if we die during startup or
fail during running (always on Solaris). */
static int isopen = 0;
if (!isopen)
{
openlog("dnsmasq", LOG_PID, log_fac);
@@ -299,6 +337,8 @@ void my_syslog(int priority, const char *format, ...)
va_start(ap, format);
vsyslog(priority, format, ap);
va_end(ap);
#endif
return;
}
@@ -327,7 +367,11 @@ void my_syslog(int priority, const char *format, ...)
if (!log_to_file)
p += sprintf(p, "<%d>", priority | log_fac);
p += sprintf(p, "%.15s dnsmasq%s[%d]: ", ctime(&time_now) + 4, func, (int)pid);
/* Omit timestamp for default daemontools situation */
if (!log_stderr || !option_bool(OPT_NO_FORK))
p += sprintf(p, "%.15s ", ctime(&time_now) + 4);
p += sprintf(p, "dnsmasq%s[%d]: ", func, (int)pid);
len = p - entry->payload;
va_start(ap, format);
@@ -336,10 +380,6 @@ void my_syslog(int priority, const char *format, ...)
entry->length = len > MAX_MESSAGE ? MAX_MESSAGE : len;
entry->offset = 0;
entry->pid = pid;
/* replace terminator with \n */
if (log_to_file)
entry->payload[entry->length - 1] = '\n';
}
/* almost always, logging won't block, so try and write this now,
@@ -398,12 +438,13 @@ void check_log_writer(fd_set *set)
void flush_log(void)
{
/* write until queue empty */
/* write until queue empty, but don't loop forever if there's
no connection to the syslog in existance */
while (log_fd != -1)
{
struct timespec waiter;
log_write();
if (!entries)
if (!entries || !connection_good)
{
close(log_fd);
break;

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -30,11 +30,15 @@
# include <linux/if_addr.h>
#endif
#ifndef NDA_RTA
# define NDA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
#endif
static struct iovec iov;
static u32 netlink_pid;
static void nl_err(struct nlmsghdr *h);
static void nl_routechange(struct nlmsghdr *h);
static int nl_async(struct nlmsghdr *h);
void netlink_init(void)
{
@@ -44,10 +48,13 @@ void netlink_init(void)
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0; /* autobind */
#ifdef HAVE_IPV6
addr.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE;
#else
addr.nl_groups = RTMGRP_IPV4_ROUTE;
if (option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV4_IFADDR;
#ifdef HAVE_IPV6
addr.nl_groups |= RTMGRP_IPV6_ROUTE;
if (daemon->ra_contexts || option_bool(OPT_CLEVERBIND))
addr.nl_groups |= RTMGRP_IPV6_IFADDR;
#endif
/* May not be able to have permission to set multicast groups don't die in that case */
@@ -122,13 +129,16 @@ static ssize_t netlink_recv(void)
return rc;
}
int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
/* family = AF_UNSPEC finds ARP table entries.
family = AF_LOCAL finds MAC addresses. */
int iface_enumerate(int family, void *parm, int (*callback)())
{
struct sockaddr_nl addr;
struct nlmsghdr *h;
ssize_t len;
static unsigned int seq = 0;
int family = AF_INET;
int callback_ok = 1, newaddr = 0;
struct {
struct nlmsghdr nlh;
@@ -139,10 +149,16 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
addr.nl_pad = 0;
addr.nl_groups = 0;
addr.nl_pid = 0; /* address to kernel */
again:
if (family == AF_UNSPEC)
req.nlh.nlmsg_type = RTM_GETNEIGH;
else if (family == AF_LOCAL)
req.nlh.nlmsg_type = RTM_GETLINK;
else
req.nlh.nlmsg_type = RTM_GETADDR;
again:
req.nlh.nlmsg_len = sizeof(req);
req.nlh.nlmsg_type = RTM_GETADDR;
req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST | NLM_F_ACK;
req.nlh.nlmsg_pid = 0;
req.nlh.nlmsg_seq = ++seq;
@@ -168,76 +184,132 @@ int iface_enumerate(void *parm, int (*ipv4_callback)(), int (*ipv6_callback)())
}
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid)
nl_routechange(h); /* May be multicast arriving async */
else if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
if (h->nlmsg_seq != seq || h->nlmsg_pid != netlink_pid || h->nlmsg_type == NLMSG_ERROR)
{
/* May be multicast arriving async */
if (nl_async(h) && option_bool(OPT_CLEVERBIND))
newaddr = 1;
}
else if (h->nlmsg_type == NLMSG_DONE)
{
#ifdef HAVE_IPV6
if (family == AF_INET && ipv6_callback)
/* handle async new interface address arrivals, these have to be done
after we complete as we're not re-entrant */
if (newaddr)
{
family = AF_INET6;
goto again;
enumerate_interfaces();
create_bound_listeners(0);
}
#endif
return 1;
return callback_ok;
}
else if (h->nlmsg_type == RTM_NEWADDR)
else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
{
struct ifaddrmsg *ifa = NLMSG_DATA(h);
struct rtattr *rta = IFA_RTA(ifa);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
if (ifa->ifa_family == AF_INET)
if (ifa->ifa_family == family)
{
struct in_addr netmask, addr, broadcast;
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
if (ifa->ifa_family == AF_INET)
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
struct in_addr netmask, addr, broadcast;
rta = RTA_NEXT(rta, len1);
netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
addr.s_addr = 0;
broadcast.s_addr = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_LOCAL)
addr = *((struct in_addr *)(rta+1));
else if (rta->rta_type == IFA_BROADCAST)
broadcast = *((struct in_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
if (addr.s_addr && callback_ok)
if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
callback_ok = 0;
}
if (addr.s_addr && ipv4_callback)
if (!((*ipv4_callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
return 0;
}
#ifdef HAVE_IPV6
else if (ifa->ifa_family == AF_INET6)
{
struct in6_addr *addrp = NULL;
while (RTA_OK(rta, len1))
else if (ifa->ifa_family == AF_INET6)
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
struct in6_addr *addrp = NULL;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFA_ADDRESS)
addrp = ((struct in6_addr *)(rta+1));
rta = RTA_NEXT(rta, len1);
}
rta = RTA_NEXT(rta, len1);
if (addrp && callback_ok)
if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope),
(int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm)))
callback_ok = 0;
}
#endif
}
}
else if (h->nlmsg_type == RTM_NEWNEIGH && family == AF_UNSPEC)
{
struct ndmsg *neigh = NLMSG_DATA(h);
struct rtattr *rta = NDA_RTA(neigh);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*neigh));
size_t maclen = 0;
char *inaddr = NULL, *mac = NULL;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == NDA_DST)
inaddr = (char *)(rta+1);
else if (rta->rta_type == NDA_LLADDR)
{
maclen = rta->rta_len - sizeof(struct rtattr);
mac = (char *)(rta+1);
}
if (addrp && ipv6_callback)
if (!((*ipv6_callback)(addrp, ifa->ifa_index, ifa->ifa_index, parm)))
return 0;
rta = RTA_NEXT(rta, len1);
}
#endif
if (inaddr && mac && callback_ok)
if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
callback_ok = 0;
}
#ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
{
struct ifinfomsg *link = NLMSG_DATA(h);
struct rtattr *rta = IFLA_RTA(link);
unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*link));
char *mac = NULL;
size_t maclen = 0;
while (RTA_OK(rta, len1))
{
if (rta->rta_type == IFLA_ADDRESS)
{
maclen = rta->rta_len - sizeof(struct rtattr);
mac = (char *)(rta+1);
}
rta = RTA_NEXT(rta, len1);
}
if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) &&
!((*callback)((int)link->ifi_index, (unsigned int)link->ifi_type, mac, maclen, parm)))
callback_ok = 0;
}
#endif
}
}
void netlink_multicast(void)
{
ssize_t len;
struct nlmsghdr *h;
int flags;
int flags, newaddr = 0;
/* don't risk blocking reading netlink messages here. */
if ((flags = fcntl(daemon->netlinkfd, F_GETFL)) == -1 ||
@@ -245,59 +317,78 @@ void netlink_multicast(void)
return;
if ((len = netlink_recv()) != -1)
{
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (h->nlmsg_type == NLMSG_ERROR)
nl_err(h);
else
nl_routechange(h);
}
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
}
static void nl_err(struct nlmsghdr *h)
{
struct nlmsgerr *err = NLMSG_DATA(h);
for (h = (struct nlmsghdr *)iov.iov_base; NLMSG_OK(h, (size_t)len); h = NLMSG_NEXT(h, len))
if (nl_async(h) && option_bool(OPT_CLEVERBIND))
newaddr = 1;
if (err->error != 0)
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
}
/* restore non-blocking status */
fcntl(daemon->netlinkfd, F_SETFL, flags);
/* We arrange to receive netlink multicast messages whenever the network route is added.
If this happens and we still have a DNS packet in the buffer, we re-send it.
This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. Note that we only accept these messages from the kernel (pid == 0) */
static void nl_routechange(struct nlmsghdr *h)
{
if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
if (newaddr)
{
struct rtmsg *rtm = NLMSG_DATA(h);
int fd;
if (rtm->rtm_type != RTN_UNICAST || rtm->rtm_scope != RT_SCOPE_LINK)
return;
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save)
{
if (daemon->srv_save->sfd)
fd = daemon->srv_save->sfd->fd;
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
fd = daemon->rfd_save->fd;
else
return;
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
enumerate_interfaces();
create_bound_listeners(0);
}
}
static int nl_async(struct nlmsghdr *h)
{
if (h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr *err = NLMSG_DATA(h);
my_syslog(LOG_ERR, _("netlink returns error: %s"), strerror(-(err->error)));
return 0;
}
else if (h->nlmsg_pid == 0 && h->nlmsg_type == RTM_NEWROUTE)
{
/* We arrange to receive netlink multicast messages whenever the network route is added.
If this happens and we still have a DNS packet in the buffer, we re-send it.
This helps on DoD links, where frequently the packet which triggers dialling is
a DNS query, which then gets lost. By re-sending, we can avoid the lookup
failing. */
struct rtmsg *rtm = NLMSG_DATA(h);
if (rtm->rtm_type == RTN_UNICAST && rtm->rtm_scope == RT_SCOPE_LINK)
{
/* Force re-reading resolv file right now, for luck. */
daemon->last_resolv = 0;
if (daemon->srv_save)
{
int fd;
if (daemon->srv_save->sfd)
fd = daemon->srv_save->sfd->fd;
else if (daemon->rfd_save && daemon->rfd_save->refcount != 0)
fd = daemon->rfd_save->fd;
else
return 0;
while(sendto(fd, daemon->packet, daemon->packet_len, 0,
&daemon->srv_save->addr.sa, sa_len(&daemon->srv_save->addr)) == -1 && retry_send());
}
}
return 0;
}
#ifdef HAVE_DHCP6
else if (h->nlmsg_type == RTM_NEWADDR)
{
/* force RAs to sync new network and pick up new interfaces. */
if (daemon->ra_contexts)
{
schedule_subnet_map();
ra_start_unsolicted(dnsmasq_time(), NULL);
/* cause lease_update_file to run after we return, in case we were called from
iface_enumerate and can't re-enter it now */
send_alarm(0, 0);
}
return 1; /* clever bind mode - rescan */
}
#endif
return 0;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -34,6 +34,67 @@ int indextoname(int fd, int index, char *name)
return 1;
}
#elif defined(HAVE_SOLARIS_NETWORK)
#include <zone.h>
#include <alloca.h>
#ifndef LIFC_UNDER_IPMP
# define LIFC_UNDER_IPMP 0
#endif
int indextoname(int fd, int index, char *name)
{
int64_t lifc_flags;
struct lifnum lifn;
int numifs, bufsize, i;
struct lifconf lifc;
struct lifreq *lifrp;
if (index == 0)
return 0;
if (getzoneid() == GLOBAL_ZONEID)
{
if (!if_indextoname(index, name))
return 0;
return 1;
}
lifc_flags = LIFC_NOXMIT | LIFC_TEMPORARY | LIFC_ALLZONES | LIFC_UNDER_IPMP;
lifn.lifn_family = AF_UNSPEC;
lifn.lifn_flags = lifc_flags;
if (ioctl(fd, SIOCGLIFNUM, &lifn) < 0)
return 0;
numifs = lifn.lifn_count;
bufsize = numifs * sizeof(struct lifreq);
lifc.lifc_family = AF_UNSPEC;
lifc.lifc_flags = lifc_flags;
lifc.lifc_len = bufsize;
lifc.lifc_buf = alloca(bufsize);
if (ioctl(fd, SIOCGLIFCONF, &lifc) < 0)
return 0;
lifrp = lifc.lifc_req;
for (i = lifc.lifc_len / sizeof(struct lifreq); i; i--, lifrp++)
{
struct lifreq lifr;
strncpy(lifr.lifr_name, lifrp->lifr_name, IF_NAMESIZE);
if (ioctl(fd, SIOCGLIFINDEX, &lifr) < 0)
return 0;
if (lifr.lifr_index == index) {
strncpy(name, lifr.lifr_name, IF_NAMESIZE);
return 1;
}
}
return 0;
}
#else
int indextoname(int fd, int index, char *name)
@@ -46,7 +107,7 @@ int indextoname(int fd, int index, char *name)
#endif
int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
int iface_check(int family, struct all_addr *addr, char *name)
{
struct iname *tmp;
int ret = 1;
@@ -54,26 +115,16 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
/* Note: have to check all and not bail out early, so that we set the
"used" flags. */
if (daemon->if_names || (addr && daemon->if_addrs))
if (daemon->if_names || daemon->if_addrs)
{
#ifdef HAVE_DHCP
struct dhcp_context *range;
#endif
ret = 0;
#ifdef HAVE_DHCP
for (range = daemon->dhcp; range; range = range->next)
if (range->interface && strcmp(range->interface, name) == 0)
ret = 1;
#endif
for (tmp = daemon->if_names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = tmp->used = 1;
for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
if (addr && tmp->addr.sa.sa_family == family)
if (tmp->addr.sa.sa_family == family)
{
if (family == AF_INET &&
tmp->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
@@ -90,59 +141,31 @@ int iface_check(int family, struct all_addr *addr, char *name, int *indexp)
for (tmp = daemon->if_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
ret = 0;
if (indexp)
{
/* One form of bridging on BSD has the property that packets
can be recieved on bridge interfaces which do not have an IP address.
We allow these to be treated as aliases of another interface which does have
an IP address with --dhcp-bridge=interface,alias,alias */
struct dhcp_bridge *bridge, *alias;
for (bridge = daemon->bridges; bridge; bridge = bridge->next)
{
for (alias = bridge->alias; alias; alias = alias->next)
if (strncmp(name, alias->iface, IF_NAMESIZE) == 0)
{
int newindex;
if (!(newindex = if_nametoindex(bridge->iface)))
{
my_syslog(LOG_WARNING, _("unknown interface %s in bridge-interface"), name);
return 0;
}
else
{
*indexp = newindex;
strncpy(name, bridge->iface, IF_NAMESIZE);
break;
}
}
if (alias)
break;
}
}
return ret;
}
static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask)
union mysockaddr *addr, struct in_addr netmask, int dad)
{
struct irec *iface;
int fd, mtu = 0, loopback;
struct ifreq ifr;
int tftp_ok = daemon->tftp_unlimited;
int tftp_ok = !!option_bool(OPT_TFTP);
int dhcp_ok = 1;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
struct interface_list *ir = NULL;
/* check whether the interface IP has been added already
we call this routine multiple times. */
for (iface = *irecp; iface; iface = iface->next)
if (sockaddr_isequal(&iface->addr, addr))
return 1;
{
iface->dad = dad;
return 1;
}
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1 ||
!indextoname(fd, if_index, ifr.ifr_name) ||
ioctl(fd, SIOCGIFFLAGS, &ifr) == -1)
@@ -157,6 +180,9 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
loopback = ifr.ifr_flags & IFF_LOOPBACK;
if (loopback)
dhcp_ok = 0;
if (ioctl(fd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
@@ -170,51 +196,38 @@ static int iface_allowed(struct irec **irecp, int if_index,
struct iname *lo;
for (lo = daemon->if_names; lo; lo = lo->next)
if (lo->name && strcmp(lo->name, ifr.ifr_name) == 0)
{
lo->isloop = 1;
break;
}
break;
if (!lo &&
(lo = whine_malloc(sizeof(struct iname))) &&
(lo->name = whine_malloc(strlen(ifr.ifr_name)+1)))
{
strcpy(lo->name, ifr.ifr_name);
lo->isloop = lo->used = 1;
lo->used = 1;
lo->next = daemon->if_names;
daemon->if_names = lo;
}
}
#ifdef HAVE_TFTP
/* implement wierd TFTP service rules */
if (addr->sa.sa_family == AF_INET)
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, ifr.ifr_name) == 0)
{
tftp_ok = 1;
break;
}
#endif
if (!ir)
{
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name, NULL))
return 1;
if (addr->sa.sa_family == AF_INET &&
!iface_check(AF_INET, (struct all_addr *)&addr->in.sin_addr, ifr.ifr_name))
return 1;
#ifdef HAVE_DHCP
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
tftp_ok = 0;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, ifr.ifr_name) == 0))
{
tftp_ok = 0;
dhcp_ok = 0;
}
#endif
#ifdef HAVE_IPV6
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, NULL))
return 1;
if (addr->sa.sa_family == AF_INET6 &&
!iface_check(AF_INET6, (struct all_addr *)&addr->in6.sin6_addr, ifr.ifr_name))
return 1;
#endif
}
/* add to list */
if ((iface = whine_malloc(sizeof(struct irec))))
@@ -222,12 +235,18 @@ static int iface_allowed(struct irec **irecp, int if_index,
iface->addr = *addr;
iface->netmask = netmask;
iface->tftp_ok = tftp_ok;
iface->dhcp_ok = dhcp_ok;
iface->mtu = mtu;
iface->dad = dad;
iface->done = 0;
if ((iface->name = whine_malloc(strlen(ifr.ifr_name)+1)))
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
{
strcpy(iface->name, ifr.ifr_name);
iface->next = *irecp;
*irecp = iface;
return 1;
}
free(iface);
}
errno = ENOMEM;
@@ -235,13 +254,15 @@ static int iface_allowed(struct irec **irecp, int if_index,
}
#ifdef HAVE_IPV6
static int iface_allowed_v6(struct in6_addr *local,
int scope, int if_index, void *vparam)
static int iface_allowed_v6(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
union mysockaddr addr;
struct in_addr netmask; /* dummy */
netmask.s_addr = 0;
(void)prefix; /* warning */
(void)scope; /* warning */
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
@@ -250,9 +271,9 @@ static int iface_allowed_v6(struct in6_addr *local,
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = *local;
addr.in6.sin6_port = htons(daemon->port);
addr.in6.sin6_scope_id = scope;
addr.in6.sin6_scope_id = if_index;
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, dad);
}
#endif
@@ -270,16 +291,17 @@ static int iface_allowed_v4(struct in_addr local, int if_index,
addr.in.sin_addr = local;
addr.in.sin_port = htons(daemon->port);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask);
return iface_allowed((struct irec **)vparam, if_index, &addr, netmask, 0);
}
int enumerate_interfaces(void)
{
#ifdef HAVE_IPV6
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, iface_allowed_v6);
#else
return iface_enumerate(&daemon->interfaces, iface_allowed_v4, NULL);
if (!iface_enumerate(AF_INET6, &daemon->interfaces, iface_allowed_v6))
return 0;
#endif
return iface_enumerate(AF_INET, &daemon->interfaces, iface_allowed_v4);
}
/* set NONBLOCK bit on fd: See Stevens 16.6 */
@@ -294,237 +316,240 @@ int fix_fd(int fd)
return 1;
}
#if defined(HAVE_IPV6)
static int create_ipv6_listener(struct listener **link, int port)
static int make_sock(union mysockaddr *addr, int type, int dienow)
{
union mysockaddr addr;
int tcpfd, fd;
struct listener *l;
int opt = 1;
memset(&addr, 0, sizeof(addr));
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
#endif
/* No error of the kernel doesn't support IPv6 */
if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1)
return (errno == EPROTONOSUPPORT ||
errno == EAFNOSUPPORT ||
errno == EINVAL);
int family = addr->sa.sa_family;
int fd, rc, opt = 1;
if ((tcpfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
return 0;
if ((fd = socket(family, type, 0)) == -1)
{
int port;
char *s;
/* No error if the kernel just doesn't support this IP flavour */
if (errno == EPROTONOSUPPORT ||
errno == EAFNOSUPPORT ||
errno == EINVAL)
return -1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
!fix_fd(fd) ||
!fix_fd(tcpfd) ||
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
listen(tcpfd, 5) == -1 ||
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return 0;
err:
port = prettyprint_addr(addr, daemon->addrbuff);
if (!option_bool(OPT_NOWILD) && !option_bool(OPT_CLEVERBIND))
sprintf(daemon->addrbuff, "port %d", port);
s = _("failed to create listening socket for %s: %s");
if (fd != -1)
close (fd);
if (dienow)
{
/* failure to bind addresses given by --listen-address at this point
is OK if we're doing bind-dynamic */
if (!option_bool(OPT_CLEVERBIND))
die(s, daemon->addrbuff, EC_BADNET);
}
else
my_syslog(LOG_WARNING, s, daemon->addrbuff, strerror(errno));
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || !fix_fd(fd))
goto err;
#ifdef HAVE_IPV6
if (family == AF_INET6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
goto err;
#endif
if ((rc = bind(fd, (struct sockaddr *)addr, sa_len(addr))) == -1)
goto err;
if (type == SOCK_STREAM)
{
if (listen(fd, 5) == -1)
goto err;
}
else if (!option_bool(OPT_NOWILD))
{
if (family == AF_INET)
{
#if defined(HAVE_LINUX_NETWORK)
if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1)
goto err;
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1)
goto err;
#endif
}
#ifdef HAVE_IPV6
else if (!set_ipv6pktinfo(fd))
goto err;
#endif
}
return fd;
}
#ifdef HAVE_IPV6
int set_ipv6pktinfo(int fd)
{
int opt = 1;
/* The API changed around Linux 2.6.14 but the old ABI is still supported:
handle all combinations of headers and kernel.
OpenWrt note that this fixes the problem addressed by your very broken patch. */
daemon->v6pktinfo = IPV6_PKTINFO;
#ifdef IPV6_RECVPKTINFO
# ifdef IPV6_2292PKTINFO
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt, sizeof(opt)) != -1)
return 1;
# ifdef IPV6_2292PKTINFO
else if (errno == ENOPROTOOPT && setsockopt(fd, IPPROTO_IPV6, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
{
if (errno == ENOPROTOOPT && setsockopt(fd, IPV6_LEVEL, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1)
daemon->v6pktinfo = IPV6_2292PKTINFO;
else
return 0;
daemon->v6pktinfo = IPV6_2292PKTINFO;
return 1;
}
# else
if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1)
return 0;
# endif
# endif
#else
if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1)
return 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_PKTINFO, &opt, sizeof(opt)) != -1)
return 1;
#endif
l = safe_malloc(sizeof(struct listener));
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = -1;
l->family = AF_INET6;
l->next = NULL;
*link = l;
return 1;
return 0;
}
#endif
struct listener *create_wildcard_listeners(void)
static struct listener *create_listeners(union mysockaddr *addr, int do_tftp, int dienow)
{
union mysockaddr addr;
int opt = 1;
struct listener *l, *l6 = NULL;
int tcpfd = -1, fd = -1, tftpfd = -1;
memset(&addr, 0, sizeof(addr));
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(struct sockaddr_in);
#endif
struct listener *l = NULL;
int fd = -1, tcpfd = -1, tftpfd = -1;
if (daemon->port != 0)
{
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
(tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return NULL;
if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 ||
listen(tcpfd, 5) == -1 ||
!fix_fd(tcpfd) ||
#ifdef HAVE_IPV6
!create_ipv6_listener(&l6, daemon->port) ||
#endif
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(fd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
fd = make_sock(addr, SOCK_DGRAM, dienow);
tcpfd = make_sock(addr, SOCK_STREAM, dienow);
}
#ifdef HAVE_TFTP
if (daemon->tftp_unlimited || daemon->tftp_interfaces)
if (do_tftp)
{
addr.in.sin_port = htons(TFTP_PORT);
if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
return NULL;
if (!fix_fd(tftpfd) ||
#if defined(HAVE_LINUX_NETWORK)
setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
setsockopt(tftpfd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(tftpfd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(tftpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
return NULL;
if (addr->sa.sa_family == AF_INET)
{
/* port must be restored to DNS port for TCP code */
short save = addr->in.sin_port;
addr->in.sin_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in.sin_port = save;
}
# ifdef HAVE_IPV6
else
{
short save = addr->in6.sin6_port;
addr->in6.sin6_port = htons(TFTP_PORT);
tftpfd = make_sock(addr, SOCK_DGRAM, dienow);
addr->in6.sin6_port = save;
}
# endif
}
#endif
l = safe_malloc(sizeof(struct listener));
l->family = AF_INET;
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = tftpfd;
l->next = l6;
if (fd != -1 || tcpfd != -1 || tftpfd != -1)
{
l = safe_malloc(sizeof(struct listener));
l->next = NULL;
l->family = addr->sa.sa_family;
l->fd = fd;
l->tcpfd = tcpfd;
l->tftpfd = tftpfd;
}
return l;
}
struct listener *create_bound_listeners(void)
void create_wildcard_listeners(void)
{
struct listener *listeners = NULL;
struct irec *iface;
int rc, opt = 1;
union mysockaddr addr;
struct listener *l, *l6;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in.sin_len = sizeof(addr.in);
#endif
addr.in.sin_family = AF_INET;
addr.in.sin_addr.s_addr = INADDR_ANY;
addr.in.sin_port = htons(daemon->port);
l = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
#ifdef HAVE_IPV6
static int dad_count = 0;
memset(&addr, 0, sizeof(addr));
# ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
# endif
addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_addr = in6addr_any;
addr.in6.sin6_port = htons(daemon->port);
l6 = create_listeners(&addr, !!option_bool(OPT_TFTP), 1);
if (l)
l->next = l6;
else
l = l6;
#endif
for (iface = daemon->interfaces; iface; iface = iface->next)
{
struct listener *new = safe_malloc(sizeof(struct listener));
new->family = iface->addr.sa.sa_family;
new->iface = iface;
new->next = listeners;
new->tftpfd = -1;
new->tcpfd = -1;
new->fd = -1;
listeners = new;
if (daemon->port != 0)
{
if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
(new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(new->tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tcpfd) ||
!fix_fd(new->fd))
die(_("failed to create listening socket: %s"), NULL, EC_BADNET);
#ifdef HAVE_IPV6
if (iface->addr.sa.sa_family == AF_INET6)
{
if (setsockopt(new->fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
setsockopt(new->tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1)
die(_("failed to set IPV6 options on listening socket: %s"), NULL, EC_BADNET);
}
#endif
while(1)
{
if ((rc = bind(new->fd, &iface->addr.sa, sa_len(&iface->addr))) != -1)
break;
#ifdef HAVE_IPV6
/* An interface may have an IPv6 address which is still undergoing DAD.
If so, the bind will fail until the DAD completes, so we try over 20 seconds
before failing. */
if (iface->addr.sa.sa_family == AF_INET6 && (errno == ENODEV || errno == EADDRNOTAVAIL) &&
dad_count++ < DAD_WAIT)
{
sleep(1);
continue;
}
#endif
break;
}
if (rc == -1 || bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
{
prettyprint_addr(&iface->addr, daemon->namebuff);
die(_("failed to bind listening socket for %s: %s"),
daemon->namebuff, EC_BADNET);
}
if (listen(new->tcpfd, 5) == -1)
die(_("failed to listen on socket: %s"), NULL, EC_BADNET);
}
#ifdef HAVE_TFTP
if (iface->addr.sa.sa_family == AF_INET && iface->tftp_ok)
{
short save = iface->addr.in.sin_port;
iface->addr.in.sin_port = htons(TFTP_PORT);
if ((new->tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ||
setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
!fix_fd(new->tftpfd) ||
bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1)
die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET);
iface->addr.in.sin_port = save;
}
#endif
}
return listeners;
daemon->listeners = l;
}
void create_bound_listeners(int dienow)
{
struct listener *new;
struct irec *iface;
struct iname *if_tmp;
for (iface = daemon->interfaces; iface; iface = iface->next)
if (!iface->done && !iface->dad &&
(new = create_listeners(&iface->addr, iface->tftp_ok, dienow)))
{
new->iface = iface;
new->next = daemon->listeners;
daemon->listeners = new;
iface->done = 1;
}
/* Check for --listen-address options that haven't been used because there's
no interface with a matching address. These may be valid: eg it's possible
to listen on 127.0.1.1 even if the loopback interface is 127.0.0.1
If the address isn't valid the bind() will fail and we'll die()
(except in bind-dynamic mode, when we'll complain but keep trying.)
The resulting listeners have the ->iface field NULL, and this has to be
handled by the DNS and TFTP code. It disables --localise-queries processing
(no netmask) and some MTU login the tftp code. */
for (if_tmp = daemon->if_addrs; if_tmp; if_tmp = if_tmp->next)
if (!if_tmp->used &&
(new = create_listeners(&if_tmp->addr, !!option_bool(OPT_TFTP), dienow)))
{
new->iface = NULL;
new->next = daemon->listeners;
daemon->listeners = new;
}
}
int is_dad_listeners(void)
{
struct irec *iface;
if (option_bool(OPT_NOWILD))
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->dad && !iface->done)
return 1;
return 0;
}
/* return a UDP socket bound to a random port, have to cope with straying into
occupied port nos and reserved ones. */
int random_sock(int family)
@@ -700,7 +725,7 @@ void pre_allocate_sfds(void)
if (!(srv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)) &&
!allocate_sfd(&srv->source_addr, srv->interface) &&
errno != 0 &&
(daemon->options & OPT_NOWILD))
option_bool(OPT_NOWILD))
{
prettyprint_addr(&srv->source_addr, daemon->namebuff);
if (srv->interface[0] != 0)
@@ -721,7 +746,7 @@ void check_servers(void)
int port = 0;
/* interface may be new since startup */
if (!(daemon->options & OPT_NOWILD))
if (!option_bool(OPT_NOWILD))
enumerate_interfaces();
for (new = daemon->servers; new; new = tmp)
@@ -861,20 +886,38 @@ int reload_servers(char *fname)
source_addr.in.sin_port = htons(daemon->query_port);
}
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
else
{
int scope_index = 0;
char *scope_id = strchr(token, '%');
if (scope_id)
{
*(scope_id++) = 0;
scope_index = if_nametoindex(scope_id);
}
if (inet_pton(AF_INET6, token, &addr.in6.sin6_addr) > 0)
{
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
source_addr.in6.sin6_len = addr.in6.sin6_len = sizeof(source_addr.in6);
#endif
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
source_addr.in6.sin6_family = addr.in6.sin6_family = AF_INET6;
source_addr.in6.sin6_flowinfo = addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_port = htons(NAMESERVER_PORT);
addr.in6.sin6_scope_id = scope_index;
source_addr.in6.sin6_addr = in6addr_any;
source_addr.in6.sin6_port = htons(daemon->query_port);
source_addr.in6.sin6_scope_id = 0;
}
else
continue;
}
#endif /* IPV6 */
#else /* IPV6 */
else
continue;
#endif
if (old_servers)
{
serv = old_servers;
@@ -921,7 +964,9 @@ struct in_addr get_ifaddr(char *intr)
ret.sin_addr.s_addr = -1;
for (l = daemon->listeners; l && l->family != AF_INET; l = l->next);
for (l = daemon->listeners;
l && (l->family != AF_INET || l->fd == -1);
l = l->next);
strncpy(ifr.ifr_name, intr, IF_NAMESIZE);
ifr.ifr_addr.sa_family = AF_INET;

File diff suppressed because it is too large Load Diff

108
src/outpacket.c Normal file
View File

@@ -0,0 +1,108 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
static size_t outpacket_counter;
void end_opt6(int container)
{
void *p = daemon->outpacket.iov_base + container + 2;
u16 len = outpacket_counter - container - 4 ;
PUTSHORT(len, p);
}
int save_counter(int newval)
{
int ret = outpacket_counter;
if (newval != -1)
outpacket_counter = newval;
return ret;
}
void *expand(size_t headroom)
{
void *ret;
if (expand_buf(&daemon->outpacket, outpacket_counter + headroom))
{
ret = daemon->outpacket.iov_base + outpacket_counter;
outpacket_counter += headroom;
return ret;
}
return NULL;
}
int new_opt6(int opt)
{
int ret = outpacket_counter;
void *p;
if ((p = expand(4)))
{
PUTSHORT(opt, p);
PUTSHORT(0, p);
}
return ret;
}
void *put_opt6(void *data, size_t len)
{
void *p;
if ((p = expand(len)))
memcpy(p, data, len);
return p;
}
void put_opt6_long(unsigned int val)
{
void *p;
if ((p = expand(4)))
PUTLONG(val, p);
}
void put_opt6_short(unsigned int val)
{
void *p;
if ((p = expand(2)))
PUTSHORT(val, p);
}
void put_opt6_char(unsigned int val)
{
unsigned char *p;
if ((p = expand(1)))
*p = val;
}
void put_opt6_string(char *s)
{
put_opt6(s, strlen(s));
}
#endif

49
src/radv-protocol.h Normal file
View File

@@ -0,0 +1,49 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define ALL_NODES "FF02::1"
#define ALL_ROUTERS "FF02::2"
struct ping_packet {
u8 type, code;
u16 checksum;
u16 identifier;
u16 sequence_no;
};
struct ra_packet {
u8 type, code;
u16 checksum;
u8 hop_limit, flags;
u16 lifetime;
u32 reachable_time;
u32 retrans_time;
};
struct prefix_opt {
u8 type, len, prefix_len, flags;
u32 valid_lifetime, preferred_lifetime, reserved;
struct in6_addr prefix;
};
#define ICMP6_OPT_SOURCE_MAC 1
#define ICMP6_OPT_PREFIX 3
#define ICMP6_OPT_MTU 5
#define ICMP6_OPT_RDNSS 25
#define ICMP6_OPT_DNSSL 31

526
src/radv.c Normal file
View File

@@ -0,0 +1,526 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
It therefore cannot use any DHCP buffer resources except outpacket, which is
not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
active, so we ensure that outpacket is allocated here too */
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
#include <netinet/icmp6.h>
struct ra_param {
int ind, managed, other, found_context, first;
char *if_name;
struct dhcp_netid *tags;
struct in6_addr link_local;
};
struct search_param {
time_t now; int iface;
};
static void send_ra(int iface, char *iface_name, struct in6_addr *dest);
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam);
static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm);
static int hop_limit;
static time_t ra_short_period_start;
void ra_init(time_t now)
{
struct icmp6_filter filter;
int fd;
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
int class = IPTOS_CLASS_CS6;
#endif
int val = 255; /* radvd uses this value */
socklen_t len = sizeof(int);
struct dhcp_context *context;
/* ensure this is around even if we're not doing DHCPv6 */
expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
/* See if we're guessing SLAAC addresses, if so we need to recieve ping replies */
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
if (context)
ICMP6_FILTER_SETPASS(ICMP6_ECHO_REPLY, &filter);
if ((fd = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) == -1 ||
getsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hop_limit, &len) ||
#if defined(IPV6_TCLASS) && defined(IPTOS_CLASS_CS6)
setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &class, sizeof(class)) == -1 ||
#endif
!fix_fd(fd) ||
!set_ipv6pktinfo(fd) ||
setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)) ||
setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)) ||
setsockopt(fd, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)) == -1)
die (_("cannot create ICMPv6 socket: %s"), NULL, EC_BADNET);
daemon->icmp6fd = fd;
ra_start_unsolicted(now, NULL);
}
void ra_start_unsolicted(time_t now, struct dhcp_context *context)
{
/* init timers so that we do ra's for some/all soon. some ra_times will end up zeroed
if it's not appropriate to advertise those contexts.
This gets re-called on a netlink route-change to re-do the advertisement
and pick up new interfaces */
if (context)
context->ra_time = now;
else
for (context = daemon->ra_contexts; context; context = context->next)
context->ra_time = now + (rand16()/13000); /* range 0 - 5 */
/* re-do frequently for a minute or so, in case the first gets lost. */
ra_short_period_start = now;
}
void icmp6_packet(void)
{
char interface[IF_NAMESIZE+1];
ssize_t sz;
int if_index = 0;
struct cmsghdr *cmptr;
struct msghdr msg;
union {
struct cmsghdr align; /* this ensures alignment */
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
} control_u;
struct sockaddr_in6 from;
unsigned char *packet;
struct iname *tmp;
/* Note: use outpacket for input buffer */
msg.msg_control = control_u.control6;
msg.msg_controllen = sizeof(control_u);
msg.msg_flags = 0;
msg.msg_name = &from;
msg.msg_namelen = sizeof(from);
msg.msg_iov = &daemon->outpacket;
msg.msg_iovlen = 1;
if ((sz = recv_dhcp_packet(daemon->icmp6fd, &msg)) == -1 || sz < 8)
return;
packet = (unsigned char *)daemon->outpacket.iov_base;
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
if_index = p.p->ipi6_ifindex;
}
if (!indextoname(daemon->icmp6fd, if_index, interface))
return;
if (!iface_check(AF_LOCAL, NULL, interface))
return;
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, interface) == 0))
return;
if (packet[1] != 0)
return;
if (packet[0] == ICMP6_ECHO_REPLY)
lease_ping_reply(&from.sin6_addr, packet, interface);
else if (packet[0] == ND_ROUTER_SOLICIT)
{
char *mac = "";
/* look for link-layer address option for logging */
if (sz >= 16 && packet[8] == ICMP6_OPT_SOURCE_MAC && (packet[9] * 8) + 8 <= sz)
{
print_mac(daemon->namebuff, &packet[10], (packet[9] * 8) - 2);
mac = daemon->namebuff;
}
my_syslog(MS_DHCP | LOG_INFO, "RTR-SOLICIT(%s) %s", interface, mac);
/* source address may not be valid in solicit request. */
send_ra(if_index, interface, !IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr) ? &from.sin6_addr : NULL);
}
}
static void send_ra(int iface, char *iface_name, struct in6_addr *dest)
{
struct ra_packet *ra;
struct ra_param parm;
struct ifreq ifr;
struct sockaddr_in6 addr;
struct dhcp_context *context;
struct dhcp_netid iface_id;
struct dhcp_opt *opt_cfg;
int done_dns = 0;
save_counter(0);
ra = expand(sizeof(struct ra_packet));
ra->type = ND_ROUTER_ADVERT;
ra->code = 0;
ra->hop_limit = hop_limit;
ra->flags = 0x00;
ra->lifetime = htons(1800); /* AdvDefaultLifetime*/
ra->reachable_time = 0;
ra->retrans_time = 0;
parm.ind = iface;
parm.managed = 0;
parm.other = 0;
parm.found_context = 0;
parm.if_name = iface_name;
parm.first = 1;
/* set tag with name == interface */
iface_id.net = iface_name;
iface_id.next = NULL;
parm.tags = &iface_id;
for (context = daemon->ra_contexts; context; context = context->next)
{
context->flags &= ~CONTEXT_RA_DONE;
context->netid.next = &context->netid;
}
if (!iface_enumerate(AF_INET6, &parm, add_prefixes) ||
!parm.found_context)
return;
strncpy(ifr.ifr_name, iface_name, IF_NAMESIZE);
if (ioctl(daemon->icmp6fd, SIOCGIFMTU, &ifr) != -1)
{
put_opt6_char(ICMP6_OPT_MTU);
put_opt6_char(1);
put_opt6_short(0);
put_opt6_long(ifr.ifr_mtu);
}
iface_enumerate(AF_LOCAL, &iface, add_lla);
/* RDNSS, RFC 6106, use relevant DHCP6 options */
(void)option_filter(parm.tags, NULL, daemon->dhcp_opts6);
for (opt_cfg = daemon->dhcp_opts6; opt_cfg; opt_cfg = opt_cfg->next)
{
int i;
/* netids match and not encapsulated? */
if (!(opt_cfg->flags & DHOPT_TAGOK))
continue;
if (opt_cfg->opt == OPTION6_DNS_SERVER)
{
struct in6_addr *a = (struct in6_addr *)opt_cfg->val;
done_dns = 1;
if (opt_cfg->len == 0)
continue;
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char((opt_cfg->len/8) + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
/* zero means "self" */
for (i = 0; i < opt_cfg->len; i += IN6ADDRSZ, a++)
if (IN6_IS_ADDR_UNSPECIFIED(a))
put_opt6(&parm.link_local, IN6ADDRSZ);
else
put_opt6(a, IN6ADDRSZ);
}
if (opt_cfg->opt == OPTION6_DOMAIN_SEARCH && opt_cfg->len != 0)
{
int len = ((opt_cfg->len+7)/8);
put_opt6_char(ICMP6_OPT_DNSSL);
put_opt6_char(len + 1);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(opt_cfg->val, opt_cfg->len);
/* pad */
for (i = opt_cfg->len; i < len * 8; i++)
put_opt6_char(0);
}
}
if (!done_dns)
{
/* default == us. */
put_opt6_char(ICMP6_OPT_RDNSS);
put_opt6_char(3);
put_opt6_short(0);
put_opt6_long(1800); /* lifetime - twice RA retransmit */
put_opt6(&parm.link_local, IN6ADDRSZ);
}
/* set managed bits unless we're providing only RA on this link */
if (parm.managed)
ra->flags |= 0x80; /* M flag, managed, */
if (parm.other)
ra->flags |= 0x40; /* O flag, other */
/* decide where we're sending */
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
#endif
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(IPPROTO_ICMPV6);
if (dest)
{
addr.sin6_addr = *dest;
if (IN6_IS_ADDR_LINKLOCAL(dest) ||
IN6_IS_ADDR_MC_LINKLOCAL(dest))
addr.sin6_scope_id = iface;
}
else
inet_pton(AF_INET6, ALL_NODES, &addr.sin6_addr);
send_from(daemon->icmp6fd, 0, daemon->outpacket.iov_base, save_counter(0),
(union mysockaddr *)&addr, (struct all_addr *)&parm.link_local, iface);
}
static int add_prefixes(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct ra_param *param = vparam;
(void)scope; /* warning */
(void)dad;
if (if_index == param->ind)
{
if (IN6_IS_ADDR_LINKLOCAL(local))
param->link_local = *local;
else if (!IN6_IS_ADDR_LOOPBACK(local) &&
!IN6_IS_ADDR_LINKLOCAL(local) &&
!IN6_IS_ADDR_MULTICAST(local))
{
int do_prefix = 0;
int do_slaac = 0;
int deprecate = 0;
unsigned int time = 0xffffffff;
struct dhcp_context *context;
for (context = daemon->ra_contexts; context; context = context->next)
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
if ((context->flags &
(CONTEXT_RA_ONLY | CONTEXT_RA_NAME | CONTEXT_RA_STATELESS)))
{
do_slaac = 1;
if (context->flags & CONTEXT_DHCP)
{
param->other = 1;
if (!(context->flags & CONTEXT_RA_STATELESS))
param->managed = 1;
}
}
else
{
/* don't do RA for non-ra-only unless --enable-ra is set */
if (!option_bool(OPT_RA))
continue;
param->managed = 1;
param->other = 1;
}
/* find floor time */
if (time > context->lease_time)
time = context->lease_time;
if (context->flags & CONTEXT_DEPRECATE)
deprecate = 1;
/* collect dhcp-range tags */
if (context->netid.next == &context->netid && context->netid.net)
{
context->netid.next = param->tags;
param->tags = &context->netid;
}
/* subsequent prefixes on the same interface
and subsequent instances of this prefix don't need timers.
Be careful not to find the same prefix twice with different
addresses. */
if (!(context->flags & CONTEXT_RA_DONE))
{
if (!param->first)
context->ra_time = 0;
context->flags |= CONTEXT_RA_DONE;
do_prefix = 1;
}
param->first = 0;
param->found_context = 1;
}
if (do_prefix)
{
struct prefix_opt *opt;
if ((opt = expand(sizeof(struct prefix_opt))))
{
/* zero net part of address */
setaddr6part(local, addr6part(local) & ~((prefix == 64) ? (u64)-1LL : (1LLU << (128 - prefix)) - 1LLU));
/* lifetimes must be min 2 hrs, by RFC 2462 */
if (time < 7200)
time = 7200;
opt->type = ICMP6_OPT_PREFIX;
opt->len = 4;
opt->prefix_len = prefix;
/* autonomous only if we're not doing dhcp, always set "on-link" */
opt->flags = do_slaac ? 0xC0 : 0x80;
opt->valid_lifetime = htonl(time);
opt->preferred_lifetime = htonl(deprecate ? 0 : time);
opt->reserved = 0;
opt->prefix = *local;
inet_ntop(AF_INET6, local, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "RTR-ADVERT(%s) %s", param->if_name, daemon->addrbuff);
}
}
}
}
return 1;
}
static int add_lla(int index, unsigned int type, char *mac, size_t maclen, void *parm)
{
(void)type;
if (index == *((int *)parm))
{
/* size is in units of 8 octets and includes type and length (2 bytes)
add 7 to round up */
int len = (maclen + 9) >> 3;
unsigned char *p = expand(len << 3);
memset(p, 0, len << 3);
*p++ = ICMP6_OPT_SOURCE_MAC;
*p++ = len;
memcpy(p, mac, maclen);
return 0;
}
return 1;
}
time_t periodic_ra(time_t now)
{
struct search_param param;
struct dhcp_context *context;
time_t next_event;
char interface[IF_NAMESIZE+1];
param.now = now;
while (1)
{
/* find overdue events, and time of first future event */
for (next_event = 0, context = daemon->ra_contexts; context; context = context->next)
if (context->ra_time != 0)
{
if (difftime(context->ra_time, now) <= 0.0)
break; /* overdue */
if (next_event == 0 || difftime(next_event, context->ra_time) > 0.0)
next_event = context->ra_time;
}
/* none overdue */
if (!context)
break;
/* There's a context overdue, but we can't find an interface
associated with it, because it's for a subnet we dont
have an interface on. Probably we're doing DHCP on
a remote subnet via a relay. Zero the timer, since we won't
ever be able to send ra's and satistfy it. */
if (iface_enumerate(AF_INET6, &param, iface_search))
context->ra_time = 0;
else if (indextoname(daemon->icmp6fd, param.iface, interface))
send_ra(param.iface, interface, NULL);
}
return next_event;
}
static int iface_search(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct search_param *param = vparam;
struct dhcp_context *context;
(void)scope;
(void)dad;
for (context = daemon->ra_contexts; context; context = context->next)
if (prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
if (context->ra_time != 0 && difftime(context->ra_time, param->now) <= 0.0)
{
/* found an interface that's overdue for RA determine new
timeout value and zap other contexts on the same interface
so they don't timeout independently .*/
param->iface = if_index;
if (difftime(param->now, ra_short_period_start) < 60.0)
/* range 5 - 20 */
context->ra_time = param->now + 5 + (rand16()/4400);
else
/* range 450 - 600 */
context->ra_time = param->now + 450 + (rand16()/440);
return 0; /* found, abort */
}
return 1; /* keep searching */
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
#include "dnsmasq.h"
static int add_resource_record(HEADER *header, char *limit, int *truncp,
static int add_resource_record(struct dns_header *header, char *limit, int *truncp,
unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type,
unsigned short class, char *format, ...);
@@ -25,9 +25,9 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp,
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
#define ADD_RDLEN(header, pp, plen, len) \
(!CHECK_LEN(header, pp, plen, len) ? 0 : (long)((pp) += (len)), 1)
(!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1))
static int extract_name(HEADER *header, size_t plen, unsigned char **pp,
static int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes)
{
unsigned char *cp = (unsigned char *)name, *p = *pp, *p1 = NULL;
@@ -217,7 +217,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
as CNAME targets according to RFC 2317 */
char *cp;
for (cp = cp1; *cp; cp++)
if (!isdigit((int)*cp))
if (!isdigit((unsigned char)*cp))
return 0;
addr[3] = addr[2];
@@ -244,7 +244,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
if (*name == '\\' && *(name+1) == '[' &&
(*(name+2) == 'x' || *(name+2) == 'X'))
{
for (j = 0, cp1 = name+3; *cp1 && isxdigit((int) *cp1) && j < 32; cp1++, j++)
for (j = 0, cp1 = name+3; *cp1 && isxdigit((unsigned char) *cp1) && j < 32; cp1++, j++)
{
char xdig[2];
xdig[0] = *cp1;
@@ -262,7 +262,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
{
for (cp1 = name; cp1 != penchunk; cp1 += strlen(cp1)+1)
{
if (*(cp1+1) || !isxdigit((int)*cp1))
if (*(cp1+1) || !isxdigit((unsigned char)*cp1))
return 0;
for (j = sizeof(struct all_addr)-1; j>0; j--)
@@ -278,7 +278,7 @@ static int in_arpa_name_2_addr(char *namein, struct all_addr *addrp)
return 0;
}
static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen, int extrabytes)
static unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes)
{
while(1)
{
@@ -333,7 +333,7 @@ static unsigned char *skip_name(unsigned char *ansp, HEADER *header, size_t plen
return ansp;
}
static unsigned char *skip_questions(HEADER *header, size_t plen)
static unsigned char *skip_questions(struct dns_header *header, size_t plen)
{
int q;
unsigned char *ansp = (unsigned char *)(header+1);
@@ -348,7 +348,7 @@ static unsigned char *skip_questions(HEADER *header, size_t plen)
return ansp;
}
static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, size_t plen)
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
{
int i, rdlen;
@@ -371,7 +371,7 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
than CRC the raw bytes, since replies might be compressed differently.
We ignore case in the names for the same reason. Return all-ones
if there is not question section. */
unsigned int questions_crc(HEADER *header, size_t plen, char *name)
unsigned int questions_crc(struct dns_header *header, size_t plen, char *name)
{
int q;
unsigned int crc = 0xffffffff;
@@ -413,7 +413,7 @@ unsigned int questions_crc(HEADER *header, size_t plen, char *name)
}
size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t hlen)
size_t resize_packet(struct dns_header *header, size_t plen, unsigned char *pheader, size_t hlen)
{
unsigned char *ansp = skip_questions(header, plen);
@@ -437,7 +437,7 @@ size_t resize_packet(HEADER *header, size_t plen, unsigned char *pheader, size_t
return ansp - (unsigned char *)header;
}
unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen, size_t *len, unsigned char **p, int *is_sign)
{
/* See if packet has an RFC2671 pseudoheader, and if so return a pointer to it.
also return length of pseudoheader in *len and pointer to the UDP size in *p
@@ -453,7 +453,7 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
{
*is_sign = 0;
if (header->opcode == QUERY)
if (OPCODE(header) == QUERY)
{
for (i = ntohs(header->qdcount); i != 0; i--)
{
@@ -510,8 +510,122 @@ unsigned char *find_pseudoheader(HEADER *header, size_t plen, size_t *len, unsi
return ret;
}
struct macparm {
unsigned char *limit;
struct dns_header *header;
size_t plen;
union mysockaddr *l3;
};
static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *parmv)
{
struct macparm *parm = parmv;
int match = 0;
unsigned short rdlen;
struct dns_header *header = parm->header;
unsigned char *lenp, *datap, *p;
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
if (!match)
return 1; /* continue */
if (ntohs(header->arcount) == 0)
{
/* We are adding the pseudoheader */
if (!(p = skip_questions(header, parm->plen)) ||
!(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount),
header, parm->plen)))
return 0;
*p++ = 0; /* empty name */
PUTSHORT(T_OPT, p);
PUTSHORT(PACKETSZ, p); /* max packet length - is 512 suitable default for non-EDNS0 resolvers? */
PUTLONG(0, p); /* extended RCODE */
lenp = p;
PUTSHORT(0, p); /* RDLEN */
rdlen = 0;
if (((ssize_t)maclen) > (parm->limit - (p + 4)))
return 0; /* Too big */
header->arcount = htons(1);
datap = p;
}
else
{
int i, is_sign;
unsigned short code, len;
if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, parm->plen, NULL, NULL, &is_sign)) ||
is_sign ||
(!(p = skip_name(p, header, parm->plen, 10))))
return 0;
p += 8; /* skip UDP length and RCODE */
lenp = p;
GETSHORT(rdlen, p);
if (!CHECK_LEN(header, p, parm->plen, rdlen))
return 0; /* bad packet */
datap = p;
/* check if option already there */
for (i = 0; i + 4 < rdlen; i += len + 4)
{
GETSHORT(code, p);
GETSHORT(len, p);
if (code == EDNS0_OPTION_MAC)
return 0;
p += len;
}
if (((ssize_t)maclen) > (parm->limit - (p + 4)))
return 0; /* Too big */
}
PUTSHORT(EDNS0_OPTION_MAC, p);
PUTSHORT(maclen, p);
memcpy(p, mac, maclen);
p += maclen;
PUTSHORT(p - datap, lenp);
parm->plen = p - (unsigned char *)header;
return 0; /* done */
}
size_t add_mac(struct dns_header *header, size_t plen, char *limit, union mysockaddr *l3)
{
struct macparm parm;
/* Must have an existing pseudoheader as the only ar-record,
or have no ar-records. Must also not be signed */
if (ntohs(header->arcount) > 1)
return plen;
parm.header = header;
parm.limit = (unsigned char *)limit;
parm.plen = plen;
parm.l3 = l3;
iface_enumerate(AF_UNSPEC, &parm, filter_mac);
return parm.plen;
}
/* is addr in the non-globally-routed IP space? */
static int private_net(struct in_addr addr, int ban_localhost)
{
@@ -525,14 +639,13 @@ static int private_net(struct in_addr addr, int ban_localhost)
((ip_addr & 0xFFFF0000) == 0xA9FE0000) /* 169.254.0.0/16 (zeroconf) */ ;
}
static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, size_t qlen, char *name)
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name)
{
int i, qtype, qclass, rdlen;
unsigned long ttl;
for (i = count; i != 0; i--)
{
if (name && (daemon->options & OPT_LOG))
if (name && option_bool(OPT_LOG))
{
if (!extract_name(header, qlen, &p, name, 1, 10))
return 0;
@@ -542,7 +655,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
GETSHORT(qtype, p);
GETSHORT(qclass, p);
GETLONG(ttl, p);
p += 4; /* ttl */
GETSHORT(rdlen, p);
if (qclass == C_IN && qtype == T_A)
@@ -570,12 +683,12 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
addr.s_addr &= ~doctor->mask.s_addr;
addr.s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
/* Since we munged the data, the server it came from is no longer authoritative */
header->aa = 0;
header->hb3 &= ~HB3_AA;
memcpy(p, &addr, INADDRSZ);
break;
}
}
else if (qtype == T_TXT && name && (daemon->options & OPT_LOG))
else if (qtype == T_TXT && name && option_bool(OPT_LOG))
{
unsigned char *p1 = p;
if (!CHECK_LEN(header, p1, qlen, rdlen))
@@ -586,15 +699,17 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
unsigned char *p2 = p1;
/* make counted string zero-term and sanitise */
for (i = 0; i < len; i++)
if (isprint(*(p2+1)))
{
*p2 = *(p2+1);
p2++;
}
{
if (!isprint((int)*(p2+1)))
break;
*p2 = *(p2+1);
p2++;
}
*p2 = 0;
my_syslog(LOG_DEBUG, "reply %s is %s", name, p1);
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
/* restore */
memmove(p1 + 1, p1, len);
memmove(p1 + 1, p1, i);
*p1 = len;
p1 += len+1;
}
@@ -607,7 +722,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, HEADER *header, siz
return p;
}
static int find_soa(HEADER *header, size_t qlen, char *name)
static int find_soa(struct dns_header *header, size_t qlen, char *name)
{
unsigned char *p;
int qtype, qclass, rdlen;
@@ -665,7 +780,8 @@ static int find_soa(HEADER *header, size_t qlen, char *name)
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
expired and cleaned out that way.
Return 1 if we reject an address because it look like part of dns-rebinding attack. */
int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int is_sign, int check_rebind)
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
int is_sign, int check_rebind, int checking_disabled)
{
unsigned char *p, *p1, *endrr, *namep;
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
@@ -675,7 +791,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
cache_start_insert();
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
if (daemon->doctors || (daemon->options & OPT_LOG))
if (daemon->doctors || option_bool(OPT_LOG))
{
searched_soa = 1;
ttl = find_soa(header, qlen, name);
@@ -688,7 +804,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
{
int found = 0, cname_count = 5;
struct crec *cpp = NULL;
int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
unsigned long cttl = ULONG_MAX, attl;
namep = p;
@@ -729,7 +845,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
@@ -761,7 +877,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
}
}
if (!found && !(daemon->options & OPT_NO_NEG))
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
@@ -809,7 +925,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= NS_INT32SZ;
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
@@ -822,10 +938,14 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
if (!cname_count--)
return 0; /* looped CNAMES */
newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
if (newc && cpp)
if (newc)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
newc->addr.cname.cache = NULL;
if (cpp)
{
cpp->addr.cname.cache = newc;
cpp->addr.cname.uid = newc->uid;
}
}
cpp = newc;
@@ -848,7 +968,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
/* check for returned address in private space */
if (check_rebind &&
(flags & F_IPV4) &&
private_net(addr.addr.addr4, !(daemon->options & OPT_LOCAL_REBIND)))
private_net(addr.addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
return 1;
newc = cache_insert(name, &addr, now, attl, flags | F_FORWARD);
@@ -867,7 +987,7 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
}
}
if (!found && !(daemon->options & OPT_NO_NEG))
if (!found && !option_bool(OPT_NO_NEG))
{
if (!searched_soa)
{
@@ -889,18 +1009,25 @@ int extract_addresses(HEADER *header, size_t qlen, char *name, time_t now, int i
}
}
/* Don't put stuff from a truncated packet into the cache, but do everything else */
if (!header->tc)
/* Don't put stuff from a truncated packet into the cache.
Don't cache replies where DNSSEC validation was turned off, either
the upstream server told us so, or the original query specified it.
Don't cache replies from non-recursive nameservers, since we may get a
reply containing a CNAME but not its target, even though the target
does exist. */
if (!(header->hb3 & HB3_TC) &&
!(header->hb4 & HB4_CD) &&
(header->hb4 & HB4_RA) &&
!checking_disabled)
cache_end_insert();
return 0;
}
/* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name.
Abuse F_BIGNAME to indicate an NS query - yuck. */
return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned short *typep)
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{
unsigned char *p = (unsigned char *)(header+1);
int qtype, qclass;
@@ -908,7 +1035,7 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
if (typep)
*typep = 0;
if (ntohs(header->qdcount) != 1 || header->opcode != QUERY)
if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
return 0; /* must be exactly one query. */
if (!extract_name(header, qlen, &p, name, 1, 4))
@@ -928,50 +1055,49 @@ unsigned short extract_request(HEADER *header, size_t qlen, char *name, unsigned
return F_IPV6;
if (qtype == T_ANY)
return F_IPV4 | F_IPV6;
if (qtype == T_NS || qtype == T_SOA)
return F_QUERY | F_BIGNAME;
}
return F_QUERY;
}
size_t setup_reply(HEADER *header, size_t qlen,
struct all_addr *addrp, unsigned short flags, unsigned long ttl)
size_t setup_reply(struct dns_header *header, size_t qlen,
struct all_addr *addrp, unsigned int flags, unsigned long ttl)
{
unsigned char *p = skip_questions(header, qlen);
header->qr = 1; /* response */
header->aa = 0; /* authoritive */
header->ra = 1; /* recursion if available */
header->tc = 0; /* not truncated */
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
/* set RA flag */
header->hb4 |= HB4_RA;
header->nscount = htons(0);
header->arcount = htons(0);
header->ancount = htons(0); /* no answers unless changed below */
if (flags == F_NEG)
header->rcode = SERVFAIL; /* couldn't get memory */
SET_RCODE(header, SERVFAIL); /* couldn't get memory */
else if (flags == F_NOERR)
header->rcode = NOERROR; /* empty domain */
SET_RCODE(header, NOERROR); /* empty domain */
else if (flags == F_NXDOMAIN)
header->rcode = NXDOMAIN;
SET_RCODE(header, NXDOMAIN);
else if (p && flags == F_IPV4)
{ /* we know the address */
header->rcode = NOERROR;
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_A, C_IN, "4", addrp);
}
#ifdef HAVE_IPV6
else if (p && flags == F_IPV6)
{
header->rcode = NOERROR;
SET_RCODE(header, NOERROR);
header->ancount = htons(1);
header->aa = 1;
add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
header->hb3 |= HB3_AA;
add_resource_record(header, NULL, NULL, sizeof(struct dns_header), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
}
#endif
else /* nowhere to forward to */
header->rcode = REFUSED;
SET_RCODE(header, REFUSED);
return p - (unsigned char *)header;
}
@@ -984,12 +1110,17 @@ int check_for_local_domain(char *name, time_t now)
struct txt_record *txt;
struct interface_name *intr;
struct ptr_record *ptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
struct naptr *naptr;
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME)) &&
(crecp->flags & (F_HOSTS | F_DHCP)))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
for (naptr = daemon->naptr; naptr; naptr = naptr->next)
if (hostname_isequal(name, naptr->name))
return 1;
for (mx = daemon->mxnames; mx; mx = mx->next)
if (hostname_isequal(name, mx->name))
return 1;
@@ -1011,7 +1142,7 @@ int check_for_local_domain(char *name, time_t now)
/* Is the packet a reply with the answer address equal to addr?
If so mung is into an NXDOMAIN reply and also put that information
in the cache. */
int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *baddr, time_t now)
{
unsigned char *p;
@@ -1058,7 +1189,7 @@ int check_for_bogus_wildcard(HEADER *header, size_t qlen, char *name,
return 0;
}
static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
static int add_resource_record(struct dns_header *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp,
unsigned long ttl, unsigned int *offset, unsigned short type, unsigned short class, char *format, ...)
{
va_list ap;
@@ -1119,7 +1250,8 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
case 't':
usval = va_arg(ap, int);
sval = va_arg(ap, char *);
memcpy(p, sval, usval);
if (usval != 0)
memcpy(p, sval, usval);
p += usval;
break;
@@ -1168,7 +1300,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(HEADER *header, char *limit, size_t qlen,
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask, time_t now)
{
char *name = daemon->namebuff;
@@ -1192,11 +1324,11 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{
unsigned short udpsz, ext_rcode, flags;
unsigned short udpsz, flags;
unsigned char *psave = pheader;
GETSHORT(udpsz, pheader);
GETSHORT(ext_rcode, pheader);
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
@@ -1211,7 +1343,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
dryrun = 1;
}
if (ntohs(header->qdcount) == 0 || header->opcode != QUERY )
if (ntohs(header->qdcount) == 0 || OPCODE(header) != QUERY )
return 0;
for (rec = daemon->mxnames; rec; rec = rec->next)
@@ -1249,7 +1381,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<TXT>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
T_TXT, t->class, "t", t->len, t->txt))
@@ -1262,6 +1394,22 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qclass == C_IN)
{
struct txt_record *t;
for (t = daemon->rr; t; t = t->next)
if ((t->class == qtype || qtype == T_ANY) && hostname_isequal(name, t->name))
{
ans = 1;
if (!dryrun)
{
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
daemon->local_ttl, NULL,
t->class, C_IN, "t", t->len, t->txt))
anscount ++;
}
}
if (qtype == T_PTR || qtype == T_ANY)
{
/* see if it's w.z.y.z.in-addr.arpa format */
@@ -1300,7 +1448,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<PTR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<PTR>");
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
if (hostname_isequal(name, ptr->name) &&
add_resource_record(header, limit, &trunc, nameoffset, &ansp,
@@ -1344,7 +1492,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
} while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
else if (is_arpa == F_IPV4 &&
(daemon->options & OPT_BOGUSPRIV) &&
option_bool(OPT_BOGUSPRIV) &&
private_net(addr.addr.addr4, 1))
{
/* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
@@ -1380,7 +1528,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
for (cp = name, i = 0, a = 0; *cp; i++)
{
if (!isdigit(*cp) || (x = strtol(cp, &cp, 10)) > 255)
if (!isdigit((unsigned char)*cp) || (x = strtol(cp, &cp, 10)) > 255)
{
i = 5;
break;
@@ -1442,7 +1590,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
/* See if a putative address is on the network from which we recieved
the query, is so we'll filter other answers. */
if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
if (local_addr.s_addr != 0 && option_bool(OPT_LOCALISE) && flag == F_IPV4)
{
struct crec *save = crecp;
do {
@@ -1514,6 +1662,23 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
} while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
}
}
if (qtype == T_CNAME || qtype == T_ANY)
{
if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
(qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP))))
{
ans = 1;
if (!dryrun)
{
log_query(crecp->flags, name, NULL, record_source(crecp->uid));
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
anscount++;
}
}
}
if (qtype == T_MX || qtype == T_ANY)
{
@@ -1525,7 +1690,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_MX, C_IN, "sd", rec->weight, rec->target))
{
@@ -1536,16 +1701,16 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
}
if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) &&
if (!found && (option_bool(OPT_SELFMX) || option_bool(OPT_LOCALMX)) &&
cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
{
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<MX>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL,
T_MX, C_IN, "sd", 1,
(daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
option_bool(OPT_SELFMX) ? name : daemon->mxtarget))
anscount++;
}
}
@@ -1554,7 +1719,8 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype == T_SRV || qtype == T_ANY)
{
int found = 0;
struct mx_srv_record *move = NULL, **up = &daemon->mxnames;
for (rec = daemon->mxnames; rec; rec = rec->next)
if (rec->issrv && hostname_isequal(name, rec->name))
{
@@ -1562,7 +1728,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (!dryrun)
{
unsigned int offset;
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<SRV>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
&offset, T_SRV, C_IN, "sssd",
rec->priority, rec->weight, rec->srvport, rec->target))
@@ -1572,9 +1738,27 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
rec->offset = offset;
}
}
/* unlink first SRV record found */
if (!move)
{
move = rec;
*up = rec->next;
}
else
up = &rec->next;
}
else
up = &rec->next;
/* put first SRV record back at the end. */
if (move)
{
*up = move;
move->next = NULL;
}
if (!found && (daemon->options & OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
if (!found && option_bool(OPT_FILTER) && (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
{
ans = 1;
if (!dryrun)
@@ -1591,7 +1775,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
ans = 1;
if (!dryrun)
{
log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, "<NAPTR>");
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
NULL, T_NAPTR, C_IN, "sszzzd",
na->order, na->pref, na->flags, na->services, na->regexp, na->replace))
@@ -1603,7 +1787,7 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
if (qtype == T_MAILB)
ans = 1, nxdomain = 1;
if (qtype == T_SOA && (daemon->options & OPT_FILTER))
if (qtype == T_SOA && option_bool(OPT_FILTER))
{
ans = 1;
if (!dryrun)
@@ -1650,14 +1834,23 @@ size_t answer_request(HEADER *header, char *limit, size_t qlen,
}
/* done all questions, set up header and return length of result */
header->qr = 1; /* response */
header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
header->ra = 1; /* recursion if available */
header->tc = trunc; /* truncation */
/* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC)) | HB3_QR;
/* set RA flag */
header->hb4 |= HB4_RA;
/* authoritive - only hosts and DHCP derived names. */
if (auth)
header->hb3 |= HB3_AA;
/* truncation */
if (trunc)
header->hb3 |= HB3_TC;
if (anscount == 0 && nxdomain)
header->rcode = NXDOMAIN;
SET_RCODE(header, NXDOMAIN);
else
header->rcode = NOERROR; /* no error */
SET_RCODE(header, NOERROR); /* no error */
header->ancount = htons(anscount);
header->nscount = htons(0);
header->arcount = htons(addncount);

File diff suppressed because it is too large Load Diff

1507
src/rfc3315.c Normal file

File diff suppressed because it is too large Load Diff

261
src/slaac.c Normal file
View File

@@ -0,0 +1,261 @@
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DHCP6
#include <netinet/icmp6.h>
static int map_rebuild = 0;
static int ping_id = 0;
void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force)
{
struct slaac_address *slaac, *old, **up;
struct dhcp_context *context;
int dns_dirty = 0;
if (!(lease->flags & LEASE_HAVE_HWADDR) ||
(lease->flags & (LEASE_TA | LEASE_NA)) ||
lease->last_interface == 0 ||
!lease->hostname)
return ;
old = lease->slaac_address;
lease->slaac_address = NULL;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) && lease->last_interface == context->if_index)
{
struct in6_addr addr = context->start6;
if (lease->hwaddr_len == 6 &&
(lease->hwaddr_type == ARPHRD_ETHER || lease->hwaddr_type == ARPHRD_IEEE802))
{
/* convert MAC address to EUI-64 */
memcpy(&addr.s6_addr[8], lease->hwaddr, 3);
memcpy(&addr.s6_addr[13], &lease->hwaddr[3], 3);
addr.s6_addr[11] = 0xff;
addr.s6_addr[12] = 0xfe;
}
#if defined(ARPHRD_EUI64)
else if (lease->hwaddr_len == 8 &&
lease->hwaddr_type == ARPHRD_EUI64)
memcpy(&addr.s6_addr[8], lease->hwaddr, 8);
#endif
#if defined(ARPHRD_IEEE1394) && defined(ARPHRD_EUI64)
else if (lease->clid_len == 9 &&
lease->clid[0] == ARPHRD_EUI64 &&
lease->hwaddr_type == ARPHRD_IEEE1394)
/* firewire has EUI-64 identifier as clid */
memcpy(&addr.s6_addr[8], &lease->clid[1], 8);
#endif
else
continue;
addr.s6_addr[8] ^= 0x02;
/* check if we already have this one */
for (up = &old, slaac = old; slaac; slaac = slaac->next)
{
if (IN6_ARE_ADDR_EQUAL(&addr, &slaac->addr))
{
*up = slaac->next;
/* recheck when DHCPv4 goes through init-reboot */
if (force)
{
slaac->ping_time = now;
slaac->backoff = 1;
dns_dirty = 1;
}
break;
}
up = &slaac->next;
}
/* No, make new one */
if (!slaac && (slaac = whine_malloc(sizeof(struct slaac_address))))
{
slaac->ping_time = now;
slaac->backoff = 1;
slaac->addr = addr;
slaac->local = context->local6;
/* Do RA's to prod it */
ra_start_unsolicted(now, context);
}
if (slaac)
{
slaac->next = lease->slaac_address;
lease->slaac_address = slaac;
}
}
if (old || dns_dirty)
lease_update_dns(1);
/* Free any no reused */
for (; old; old = slaac)
{
slaac = old->next;
free(old);
}
}
time_t periodic_slaac(time_t now, struct dhcp_lease *leases)
{
struct dhcp_context *context;
struct dhcp_lease *lease;
struct slaac_address *slaac;
time_t next_event = 0;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME))
break;
/* nothing configured */
if (!context)
return 0;
while (ping_id == 0)
ping_id = rand16();
if (map_rebuild)
{
map_rebuild = 0;
build_subnet_map();
}
for (lease = leases; lease; lease = lease->next)
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
{
/* confirmed or given up? */
if (slaac->backoff == 0 || slaac->ping_time == 0)
continue;
if (difftime(slaac->ping_time, now) <= 0.0)
{
struct ping_packet *ping;
struct sockaddr_in6 addr;
save_counter(0);
ping = expand(sizeof(struct ping_packet));
ping->type = ICMP6_ECHO_REQUEST;
ping->code = 0;
ping->identifier = ping_id;
ping->sequence_no = slaac->backoff;
memset(&addr, 0, sizeof(addr));
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin6_len = sizeof(struct sockaddr_in6);
#endif
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(IPPROTO_ICMPV6);
addr.sin6_addr = slaac->addr;
if (sendto(daemon->icmp6fd, daemon->outpacket.iov_base, save_counter(0), 0,
(struct sockaddr *)&addr, sizeof(addr)) == -1 &&
errno == EHOSTUNREACH)
slaac->ping_time = 0; /* Give up */
else
{
slaac->ping_time += (1 << (slaac->backoff - 1)) + (rand16()/21785); /* 0 - 3 */
if (slaac->backoff > 4)
slaac->ping_time += rand16()/4000; /* 0 - 15 */
if (slaac->backoff < 12)
slaac->backoff++;
}
}
if (slaac->ping_time != 0 &&
(next_event == 0 || difftime(next_event, slaac->ping_time) >= 0.0))
next_event = slaac->ping_time;
}
return next_event;
}
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases)
{
struct dhcp_lease *lease;
struct slaac_address *slaac;
struct ping_packet *ping = (struct ping_packet *)packet;
int gotone = 0;
if (ping->identifier == ping_id)
for (lease = leases; lease; lease = lease->next)
for (slaac = lease->slaac_address; slaac; slaac = slaac->next)
if (slaac->backoff != 0 && IN6_ARE_ADDR_EQUAL(sender, &slaac->addr))
{
slaac->backoff = 0;
gotone = 1;
inet_ntop(AF_INET6, sender, daemon->addrbuff, ADDRSTRLEN);
my_syslog(MS_DHCP | LOG_INFO, "SLAAC-CONFIRM(%s) %s %s", interface, daemon->addrbuff, lease->hostname);
}
lease_update_dns(gotone);
}
/* Build a map from ra-names subnets to corresponding interfaces. This
is used to go from DHCPv4 leases to SLAAC addresses,
interface->IPv6-subnet, IPv6-subnet + MAC address -> SLAAC.
*/
static int add_subnet(struct in6_addr *local, int prefix,
int scope, int if_index, int dad, void *vparam)
{
struct dhcp_context *context;
(void)scope;
(void)dad;
(void)vparam;
for (context = daemon->ra_contexts; context; context = context->next)
if ((context->flags & CONTEXT_RA_NAME) &&
prefix == context->prefix &&
is_same_net6(local, &context->start6, prefix) &&
is_same_net6(local, &context->end6, prefix))
{
context->if_index = if_index;
context->local6 = *local;
}
return 1;
}
void build_subnet_map(void)
{
struct dhcp_context *context;
int ok = 0;
for (context = daemon->ra_contexts; context; context = context->next)
{
context->if_index = 0;
if ((context->flags & CONTEXT_RA_NAME))
ok = 1;
}
/* ra-names configured */
if (ok)
iface_enumerate(AF_INET6, NULL, add_subnet);
}
void schedule_subnet_map(void)
{
map_rebuild = 1;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -18,12 +18,13 @@
#ifdef HAVE_TFTP
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special);
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix);
static void free_transfer(struct tftp_transfer *transfer);
static ssize_t tftp_err(int err, char *packet, char *mess, char *file);
static ssize_t tftp_err_oops(char *packet, char *file);
static ssize_t get_block(char *packet, struct tftp_transfer *transfer);
static char *next(char **p, char *end);
static void sanitise(char *buf);
#define OP_RRQ 1
#define OP_WRQ 2
@@ -43,11 +44,11 @@ void tftp_request(struct listener *listen, time_t now)
ssize_t len;
char *packet = daemon->packet;
char *filename, *mode, *p, *end, *opt;
struct sockaddr_in addr, peer;
union mysockaddr addr, peer;
struct msghdr msg;
struct iovec iov;
struct ifreq ifr;
int is_err = 1, if_index = 0, mtu = 0, special = 0;
int is_err = 1, if_index = 0, mtu = 0;
#ifdef HAVE_DHCP
struct iname *tmp;
#endif
@@ -57,13 +58,15 @@ void tftp_request(struct listener *listen, time_t now)
int mtuflag = IP_PMTUDISC_DONT;
#endif
char namebuff[IF_NAMESIZE];
char *name;
char *name = NULL;
char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref;
struct interface_list *ir;
union {
struct cmsghdr align; /* this ensures alignment */
#ifdef HAVE_IPV6
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
#if defined(HAVE_LINUX_NETWORK)
char control[CMSG_SPACE(sizeof(struct in_pktinfo))];
#elif defined(HAVE_SOLARIS_NETWORK)
@@ -90,115 +93,157 @@ void tftp_request(struct listener *listen, time_t now)
if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2)
return;
if (daemon->options & OPT_NOWILD)
if (option_bool(OPT_NOWILD))
{
addr = listen->iface->addr.in;
mtu = listen->iface->mtu;
name = listen->iface->name;
if (listen->iface)
{
addr = listen->iface->addr;
mtu = listen->iface->mtu;
name = listen->iface->name;
}
else
{
/* we're listening on an address that doesn't appear on an interface,
ask the kernel what the socket is bound to */
socklen_t tcp_len = sizeof(union mysockaddr);
if (getsockname(listen->tftpfd, (struct sockaddr *)&addr, &tcp_len) == -1)
return;
}
}
else
{
struct cmsghdr *cmptr;
int check;
struct interface_list *ir;
addr.sin_addr.s_addr = 0;
if (msg.msg_controllen < sizeof(struct cmsghdr))
return;
addr.sa.sa_family = listen->family;
#if defined(HAVE_LINUX_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
if (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_PKTINFO)
{
union {
unsigned char *c;
struct in_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.in.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
}
#elif defined(HAVE_SOLARIS_NETWORK)
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
union {
unsigned char *c;
struct in_pktinfo *p;
struct in_addr *a;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
addr.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.in.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *(p.i);
}
#elif defined(HAVE_SOLARIS_NETWORK)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
union {
unsigned char *c;
struct in_addr *a;
unsigned int *i;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = *(p.i);
}
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
union {
unsigned char *c;
struct in_addr *a;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = p.s->sdl_index;
}
if (listen->family == AF_INET)
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
{
union {
unsigned char *c;
struct in_addr *a;
struct sockaddr_dl *s;
} p;
p.c = CMSG_DATA(cmptr);
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
addr.in.sin_addr = *(p.a);
else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
if_index = p.s->sdl_index;
}
#endif
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
{
union {
unsigned char *c;
struct in6_pktinfo *p;
} p;
p.c = CMSG_DATA(cmptr);
addr.in6.sin6_addr = p.p->ipi6_addr;
if_index = p.p->ipi6_ifindex;
}
}
#endif
if (!indextoname(listen->tftpfd, if_index, namebuff) ||
addr.sin_addr.s_addr == 0)
if (!indextoname(listen->tftpfd, if_index, namebuff))
return;
name = namebuff;
check = iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index);
/* wierd TFTP service override */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
break;
if (!ir)
#ifdef HAVE_IPV6
if (listen->family == AF_INET6)
{
if (!daemon->tftp_unlimited || !check)
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name))
return;
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return;
#endif
}
else
#endif
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name))
return;
#ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
return;
#endif
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1)
mtu = ifr.ifr_mtu;
}
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
if (name)
{
/* check for per-interface prefix */
for (pref = daemon->if_prefix; pref; pref = pref->next)
if (strcmp(pref->interface, name) == 0)
prefix = pref->prefix;
}
/* wierd TFTP interfaces disable special options. */
for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
special = 1;
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (listen->family == AF_INET)
{
addr.in.sin_port = htons(port);
#ifdef HAVE_SOCKADDR_SA_LEN
addr.sin_len = sizeof(addr);
addr.in.sin_len = sizeof(addr.in);
#endif
}
#ifdef HAVE_IPV6
else
{
addr.in6.sin6_port = htons(port);
addr.in6.sin6_flowinfo = 0;
addr.in6.sin6_scope_id = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr.in6.sin6_len = sizeof(addr.in6);
#endif
}
#endif
if (!(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return;
if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1)
{
free(transfer);
return;
@@ -213,13 +258,15 @@ void tftp_request(struct listener *listen, time_t now)
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
transfer->netascii = transfer->carrylf = 0;
prettyprint_addr(&peer, daemon->addrbuff);
/* if we have a nailed-down range, iterate until we find a free one. */
while (1)
{
if (bind(transfer->sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1 ||
if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 ||
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
setsockopt(transfer->sockfd, SOL_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
setsockopt(transfer->sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == -1 ||
#endif
!fix_fd(transfer->sockfd))
{
@@ -227,7 +274,12 @@ void tftp_request(struct listener *listen, time_t now)
{
if (++port <= daemon->end_tftp_port)
{
addr.sin_port = htons(port);
if (listen->family == AF_INET)
addr.in.sin_port = htons(port);
#ifdef HAVE_IPV6
else
addr.in6.sin6_port = htons(port);
#endif
continue;
}
my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP"));
@@ -245,7 +297,10 @@ void tftp_request(struct listener *listen, time_t now)
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr));
{
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff);
is_err = 1;
}
else
{
if (strcasecmp(mode, "netascii") == 0)
@@ -255,8 +310,7 @@ void tftp_request(struct listener *listen, time_t now)
{
if (strcasecmp(opt, "blksize") == 0)
{
if ((opt = next(&p, end)) &&
(special || !(daemon->options & OPT_TFTP_NOBLOCK)))
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
{
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
@@ -278,9 +332,12 @@ void tftp_request(struct listener *listen, time_t now)
}
/* cope with backslashes from windows boxen. */
while ((p = strchr(filename, '\\')))
*p = '/';
for (p = filename; *p; p++)
if (*p == '\\')
*p = '/';
else if (option_bool(OPT_TFTP_LC))
*p = tolower(*p);
strcpy(daemon->namebuff, "/");
if (prefix)
{
@@ -290,12 +347,12 @@ void tftp_request(struct listener *listen, time_t now)
if (prefix[strlen(prefix)-1] != '/')
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
if (!special && (daemon->options & OPT_TFTP_APREF))
if (option_bool(OPT_TFTP_APREF))
{
size_t oldlen = strlen(daemon->namebuff);
struct stat statbuf;
strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
/* remove unique-directory if it doesn't exist */
@@ -317,7 +374,7 @@ void tftp_request(struct listener *listen, time_t now)
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len, prefix, special)))
if ((transfer->file = check_tftp_fileperm(&len, prefix)))
{
if ((len = get_block(packet, transfer)) == -1)
len = tftp_err_oops(packet, daemon->namebuff);
@@ -327,19 +384,18 @@ void tftp_request(struct listener *listen, time_t now)
}
while (sendto(transfer->sockfd, packet, len, 0,
(struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR);
(struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR);
if (is_err)
free_transfer(transfer);
else
{
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr));
transfer->next = daemon->tftp_trans;
daemon->tftp_trans = transfer;
}
}
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special)
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix)
{
char *packet = daemon->packet, *namebuff = daemon->namebuff;
struct tftp_file *file;
@@ -376,7 +432,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int spe
goto perm;
}
/* in secure mode, must be owned by user running dnsmasq */
else if (!special && (daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid)
else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid)
goto perm;
/* If we're doing many tranfers from the same file, only
@@ -436,6 +492,8 @@ void check_tftp_listeners(fd_set *rset, time_t now)
{
tmp = transfer->next;
prettyprint_addr(&transfer->peer, daemon->addrbuff);
if (FD_ISSET(transfer->sockfd, rset))
{
/* we overwrote the buffer... */
@@ -456,20 +514,16 @@ void check_tftp_listeners(fd_set *rset, time_t now)
char *p = daemon->packet + sizeof(struct ack);
char *end = daemon->packet + len;
char *err = next(&p, end);
/* Sanitise error message */
if (!err)
err = "";
else
{
char *q, *r;
for (q = r = err; *r; r++)
if (isprint((int)*r))
*(q++) = *r;
*q = 0;
}
sanitise(err);
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"),
(int)ntohs(mess->block), err,
inet_ntoa(transfer->peer.sin_addr));
daemon->addrbuff);
/* Got err, ensure we take abort */
transfer->timeout = now;
@@ -493,31 +547,39 @@ void check_tftp_listeners(fd_set *rset, time_t now)
len = tftp_err_oops(daemon->packet, transfer->file->filename);
endcon = 1;
}
else if (++transfer->backoff > 5)
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
else if (++transfer->backoff > 5 && len != 0)
{
/* don't complain about timeout when we're awaiting the last
ACK, some clients never send it */
if (len != 0)
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"),
transfer->file->filename, inet_ntoa(transfer->peer.sin_addr));
endcon = 1;
len = 0;
}
if (len != 0)
while(sendto(transfer->sockfd, daemon->packet, len, 0,
(struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR);
(struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR);
if (endcon || len == 0)
{
strcpy(daemon->namebuff, transfer->file->filename);
sanitise(daemon->namebuff);
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
/* unlink */
*up = tmp;
free_transfer(transfer);
if (endcon)
free_transfer(transfer);
else
{
/* put on queue to be sent to script and deleted */
transfer->next = daemon->tftp_done_trans;
daemon->tftp_done_trans = transfer;
}
continue;
}
}
up = &transfer->next;
}
}
}
static void free_transfer(struct tftp_transfer *transfer)
@@ -545,6 +607,16 @@ static char *next(char **p, char *end)
return ret;
}
static void sanitise(char *buf)
{
unsigned char *q, *r;
for (q = r = (unsigned char *)buf; *r; r++)
if (isprint((int)*r))
*(q++) = *r;
*q = 0;
}
static ssize_t tftp_err(int err, char *packet, char *message, char *file)
{
struct errmess {
@@ -553,7 +625,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
} *mess = (struct errmess *)packet;
ssize_t ret = 4;
char *errstr = strerror(errno);
sanitise(file);
mess->op = htons(OP_ERR);
mess->err = htons(err);
ret += (snprintf(mess->message, 500, message, file, errstr) + 1);
@@ -564,7 +638,9 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file)
static ssize_t tftp_err_oops(char *packet, char *file)
{
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), file);
/* May have >1 refs to file, so potentially mangle a copy of the name */
strcpy(daemon->namebuff, file);
return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff);
}
/* return -1 for error, zero for done. */
@@ -628,15 +704,13 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
for (i = 0, newcarrylf = 0; i < size; i++)
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
{
if (size == transfer->blocksize)
{
transfer->expansion++;
if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
}
else
transfer->expansion++;
if (size != transfer->blocksize)
size++; /* room in this block */
else if (i == size - 1)
newcarrylf = 1; /* don't expand LF again if it moves to the next block */
/* make space and insert CR */
memmove(&mess->data[i+1], &mess->data[i], size - (i + 1));
mess->data[i] = '\r';
@@ -651,4 +725,21 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
}
}
int do_tftp_script_run(void)
{
struct tftp_transfer *transfer;
if ((transfer = daemon->tftp_done_trans))
{
daemon->tftp_done_trans = transfer->next;
#ifdef HAVE_SCRIPT
queue_tftp(transfer->file->size, transfer->file->filename, &transfer->peer);
#endif
free_transfer(transfer);
return 1;
}
return 0;
}
#endif

View File

@@ -1,4 +1,4 @@
/* dnsmasq is Copyright (c) 2000-2010 Simon Kelley
/* dnsmasq is Copyright (c) 2000-2012 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include <sys/times.h>
#endif
#ifdef LOCALEDIR
#if defined(LOCALEDIR) || defined(HAVE_IDN)
#include <idna.h>
#endif
@@ -43,11 +43,9 @@ unsigned short rand16(void)
/* SURF random number generator */
typedef unsigned int uint32;
static uint32 seed[32];
static uint32 in[12];
static uint32 out[8];
static u32 seed[32];
static u32 in[12];
static u32 out[8];
void rand_init()
{
@@ -66,7 +64,7 @@ void rand_init()
static void surf(void)
{
uint32 t[12]; uint32 x; uint32 sum = 0;
u32 t[12]; u32 x; u32 sum = 0;
int r; int i; int loop;
for (i = 0;i < 12;++i) t[i] = in[i] ^ seed[12 + i];
@@ -120,11 +118,11 @@ static int check_name(char *in)
dotgap = 0;
else if (++dotgap > MAXLABEL)
return 0;
else if (isascii(c) && iscntrl(c))
else if (isascii((unsigned char)c) && iscntrl((unsigned char)c))
/* iscntrl only gives expected results for ascii */
return 0;
#ifndef LOCALEDIR
else if (!isascii(c))
#if !defined(LOCALEDIR) && !defined(HAVE_IDN)
else if (!isascii((unsigned char)c))
return 0;
#endif
else if (c != ' ')
@@ -170,7 +168,7 @@ int legal_hostname(char *name)
char *canonicalise(char *in, int *nomem)
{
char *ret = NULL;
#ifdef LOCALEDIR
#if defined(LOCALEDIR) || defined(HAVE_IDN)
int rc;
#endif
@@ -180,7 +178,7 @@ char *canonicalise(char *in, int *nomem)
if (!check_name(in))
return NULL;
#ifdef LOCALEDIR
#if defined(LOCALEDIR) || defined(HAVE_IDN)
if ((rc = idna_to_ascii_lz(in, &ret, 0)) != IDNA_SUCCESS)
{
if (ret)
@@ -322,6 +320,48 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
}
#ifdef HAVE_IPV6
int is_same_net6(struct in6_addr *a, struct in6_addr *b, int prefixlen)
{
int pfbytes = prefixlen >> 3;
int pfbits = prefixlen & 7;
if (memcmp(&a->s6_addr, &b->s6_addr, pfbytes) != 0)
return 0;
if (pfbits == 0 ||
(a->s6_addr[pfbytes] >> (8 - pfbits) == b->s6_addr[pfbytes] >> (8 - pfbits)))
return 1;
return 0;
}
/* return least signigicant 64 bits if IPv6 address */
u64 addr6part(struct in6_addr *addr)
{
int i;
u64 ret = 0;
for (i = 8; i < 16; i++)
ret = (ret << 8) + addr->s6_addr[i];
return ret;
}
void setaddr6part(struct in6_addr *addr, u64 host)
{
int i;
for (i = 15; i >= 8; i--)
{
addr->s6_addr[i] = host;
host = host >> 8;
}
}
#endif
/* returns port number from address */
int prettyprint_addr(union mysockaddr *addr, char *buf)
{
@@ -335,7 +375,15 @@ int prettyprint_addr(union mysockaddr *addr, char *buf)
}
else if (addr->sa.sa_family == AF_INET6)
{
char name[IF_NAMESIZE];
inet_ntop(AF_INET6, &addr->in6.sin6_addr, buf, ADDRSTRLEN);
if (addr->in6.sin6_scope_id != 0 &&
if_indextoname(addr->in6.sin6_scope_id, name) &&
strlen(buf) + strlen(name) + 2 <= ADDRSTRLEN)
{
strcat(buf, "%");
strcat(buf, name);
}
port = ntohs(addr->in6.sin6_port);
}
#else
@@ -365,7 +413,8 @@ void prettyprint_time(char *buf, unsigned int t)
}
/* in may equal out, when maxlen may be -1 (No max len). */
/* in may equal out, when maxlen may be -1 (No max len).
Return -1 for extraneous no-hex chars found. */
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type)
{
@@ -377,7 +426,10 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
while (maxlen == -1 || i < maxlen)
{
for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
if (*r != '*' && !isxdigit((unsigned char)*r))
return -1;
if (*r == 0)
maxlen = i;
@@ -392,12 +444,29 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
else
{
*r = 0;
mask = mask << 1;
if (strcmp(in, "*") == 0)
mask |= 1;
{
mask = (mask << 1) | 1;
i++;
}
else
out[i] = strtol(in, NULL, 16);
i++;
{
int j, bytes = (1 + (r - in))/2;
for (j = 0; j < bytes; j++)
{
char sav;
if (j < bytes - 1)
{
sav = in[(j+1)*2];
in[(j+1)*2] = 0;
}
out[i] = strtol(&in[j*2], NULL, 16);
mask = mask << 1;
i++;
if (j < bytes - 1)
in[(j+1)*2] = sav;
}
}
}
}
in = r+1;
@@ -473,7 +542,7 @@ void bump_maxfd(int fd, int *max)
int retry_send(void)
{
struct timespec waiter;
if (errno == EAGAIN)
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
waiter.tv_sec = 0;
waiter.tv_nsec = 10000;