]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
import of dnsmasq-2.3.tar.gz
authorSimon Kelley <simon@thekelleys.org.uk>
Wed, 10 Mar 2004 20:04:35 +0000 (20:04 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Thu, 5 Jan 2012 17:31:10 +0000 (17:31 +0000)
19 files changed:
CHANGELOG
Makefile
dnsmasq-mdk.spec
dnsmasq-rh.spec
dnsmasq-suse.spec
dnsmasq.8
dnsmasq.conf.example
src/cache.c
src/config.h
src/dhcp.c
src/dnsmasq.c
src/dnsmasq.h
src/forward.c
src/lease.c
src/network.c
src/option.c
src/rfc1035.c
src/rfc2131.c
src/util.c

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