]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
import of dnsmasq-2.21.tar.gz
authorSimon Kelley <simon@thekelleys.org.uk>
Wed, 23 Mar 2005 20:28:59 +0000 (20:28 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Thu, 5 Jan 2012 17:31:11 +0000 (17:31 +0000)
21 files changed:
CHANGELOG
FAQ
dnsmasq-rh.spec
dnsmasq-suse.spec
dnsmasq.8
dnsmasq.conf.example
doc.html
src/Makefile
src/cache.c
src/config.h
src/dhcp.c
src/dnsmasq.c
src/dnsmasq.h
src/forward.c
src/lease.c
src/netlink.c [new file with mode: 0644]
src/network.c
src/option.c
src/rfc1035.c
src/rfc2131.c
src/util.c

index 0904b3c257bd72b141ed422372b31bc5c8383f43..c193890b229888d6815e436d659e9284e3822b96 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1369,5 +1369,56 @@ version 2.20
            Added preference values for MX records.
 
            Added the --localise-queries option.
-           
-           
+
+version 2.21
+            Improve handling of SERVFAIL and REFUSED errors. Receiving
+           these now initiates search for a new good server, and a 
+           server which returns them is not a candidate as a good
+            server. Thanks to Istvan Varadi for pointing out the
+            problem.
+
+           Tweak the time code in BROKEN_RTC mode.
+
+           Sanity check lease times in dhcp-range and dhcp-host
+           configurations and force them to be at least two minutes
+           (120s) leases shorter than a minute confuse some clients,
+           notably Apple MacOS X. Rory Campbell-Lange found this
+           problem.
+
+           Only warn once about an upstream server which is refusing to do
+           recursive queries.
+
+           Fix DHCP address allocation problem when netid tags are in
+           use. Thanks to Will Murnane for the bug report and
+           subsequent testing.
+
+           Add an additional data section to the reply for MX and SRV
+           queries. Add support for DNS TXT records. Thanks to Robert 
+           Kean and John Hampton for prompts and testing of these.
+
+           Apply address rewriting to records in the additional data section
+           of DNS packets. This makes things like MX records work
+           with the alias function. Thanks to Chad Skeeters for
+           pointing out the need for this.
+
+           Added support for quoted strings in config file.
+
+           Detect and defeat cache-poisoning attacks which attempt to
+           send (malicious) answers to questions we didn't
+           send. These are ignored now even if the attacker manages
+           to guess a random query-id.
+
+           Provide DHCP support for interfaces with multiple IP
+           addresses or aliases. This in only enabled under Linux.
+           See the FAQ entry for details.
+
+           Revisit the MAC-address and client-id matching code to
+           provide saner behaviour with PXE boots, where some
+           requests have a client-id and some don't.
+
+           Fixed off-by-one buffer overflow in lease file reading
+           code. Thanks to Rob Holland for the bug report.
+
+           Added wildcard matching for MAC addresses in dhcp-host
+           options. A sensible suggestion by Nathaniel McCallum.
+
diff --git a/FAQ b/FAQ
index bd800cd19ee1706543eaf0ac2ec7a508d7b87b2d..c468de6278e6c75c84766260f1adfe0887160792 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -21,8 +21,7 @@ Q: Why doesn't dnsmasq support DNS queries over TCP? Don't the RFC's specify
    that?
 
 A: Update: from version 2.10, it does. There are a few limitations:
-   data obtained via TCP is not cached, and dynamically-created
-   interfaces may break under certain circumstances. Source-address
+   data obtained via TCP is not cached, and source-address
    or query-port specifications are ignored for TCP.
 
 Q: When I send SIGUSR1 to dump the contents of the cache, some entries have
@@ -112,7 +111,7 @@ A: Resolver code sometime does strange things when given names without
    resolver will attempt to look up "myhost.localnet" so you need to
    have dnsmasq reply to that name. The way to do that is to include
    the domain in each name on /etc/hosts and/or to use the
-   --expand-hosts and --domain-suffix options.
+   --expand-hosts and --domain options.
 
 Q: Can I get dnsmasq to save the contents of its cache to disk when
    I shut my machine down and re-load when it starts again?
@@ -316,5 +315,30 @@ A: By default, the identity of a machine is determined by using the
    dhcpcd uses the "-I" flag. Windows uses a registry setting,
    see http://www.jsiinc.com/SUBF/TIP2800/rh2845.htm
 
+Q: Can dnsmasq do DHCP on IP-alias interfaces?
+
+A: Yes, from version-2.21. The support is only available running under
+   Linux, on a kernel which provides the RT-netlink facility. All 2.4 
+   and 2.6 kernels provide RT-netlink and it's an option in 2.2
+   kernels. If dnsmasq is built under uclibc, even on Linux, then
+   the support is not included.
+   
+   If a physical interface has more than one IP address or aliases
+   with extra IP addresses, then any dhcp-ranges corresponding to
+   these addresses can be used for address allocation. So is and
+   interface has addresses 192.168.1.0/24 and 192.68.2.0/24 and there
+   are DHCP ranges 192.168.1.100-192.168.1.200 and
+   192.168.2.100-192.168.2.200 then both ranges would be used for host
+   connected to the physical interface. A more typical use might be to
+   have one of the address-ranges as static-only, and have known
+   hosts allocated addresses on that subnet using dhcp-host options,
+   while  anonymous hosts go on the other.
+
+  
+
+
+
+
+   
 
              
index c5fbb215daaa4496ab334f77d6823a16d3027ea8..e306d4045a21db50764c41350c7edce8de83582d 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.20
+Version: 2.21
 Release: 1
 Copyright: GPL
 Group: System Environment/Daemons
index b52242a36d450e1d7b650bd0bb980c1a60497cf2..912fa29272a04ca68591c0f1660498c4de948e58 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.20
+Version: 2.21
 Release: 1
 Copyright: GPL
 Group: Productivity/Networking/DNS/Servers
index 091145e0b9d142e7260323823ad174d4094d668d..65568fb70838ebc1e96071115f691b2123f1b647 100644 (file)
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -310,6 +310,10 @@ domain given by the
 option. The data in these is stereotyped, but is enough for resolvers
 to deduce that the domain is a valid one for resolving SRV records.
 .TP
+.B \-Y, --txt-record=<name>[[,<text>],<text>]
+Return a TXT DNS record. The value of TXT record is a set of strings,
+so  any number may be included, split by commas.
+.TP
 .B \-c, --cache-size=<cachesize>
 Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching.
 .TP
@@ -319,7 +323,7 @@ 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=[network-id,]<start-addr>,<end-addr>[[,<netmask>],<broadcast>][,<default lease time>]
+.B \-F, --dhcp-range=[[net:]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> and from statically defined addresses given
 in 
@@ -339,8 +343,10 @@ given using the
 option. This limitation currently affects OpenBSD. It is always
 allowed to have more than one dhcp-range in a single subnet. The optional
 network-id is a alphanumeric label which marks this network so that
-dhcp options may be specified on a per-network basis. The end address
-may be replaced by the keyword 
+dhcp options may be specified on a per-network basis. 
+When it is prefixed with 'net:' then its meaning changes from setting
+a tag to matching it.
+The end address may be replaced by the keyword 
 .B static
 which tells dnsmasq to enable DHCP for the network specified, but not
 to dynamically allocate IP addresses. Only hosts which have static
@@ -387,6 +393,12 @@ useful when there is another DHCP server on the network which should
 be used by some machines. The net:<network-id> parameter enables DHCP options just
 for this host in the same way as the the network-id in
 .B dhcp-range.
+Ethernet addresses (but not client-ids) may have
+wildcard bytes, so for example 
+.B --dhcp-host=00:20:e0:3b:13:*,ignore 
+will cause dnsmasq to ignore a range of ethernet addresses. Note that
+the "*" will need to be escaped or quoted on a command line, but not
+in the configuration file.
 .TP 
 .B \-Z, --read-ethers
 Read /etc/ethers for information about hosts for the DHCP server. The
@@ -500,7 +512,10 @@ options which may only be specified once, the configuration file overrides
 the command line. Use the --conf-file option to specify a different
 configuration file. The conf-file option is also allowed in
 configuration files, to include multiple configuration files. Only one
-level of nesting is allowed.
+level of nesting is allowed. Quoting is allowed in a config file:
+between " quotes the special meaning of , and # is removed and the
+following escapes are allowed: \\\\ \\" \\t and \\n. The later two
+corresponding to newline and tab.
 .SH NOTES
 When it receives a SIGHUP, 
 .B dnsmasq 
index cd1e4baddbb3484e07558694022c57d9db003a3d..a39c35d4243f9fc2e4719b1eaa95918016500674 100644 (file)
@@ -167,6 +167,10 @@ bogus-priv
 # the machine with ethernet address 11:22:33:44:55:66
 #dhcp-host=11:22:33:44:55:66,net:red
 
+# Send extra options which are tagged as "red" to 
+# any machine with ethernet address starting 11:22:33:
+#dhcp-host=11:22:33:*:*:*,net:red
+
 # Send extra options which are tagged as "red" to any machine whose
 # DHCP vendorclass string includes the substring "Linux"
 #dhcp-vendorclass=red,Linux
@@ -310,7 +314,8 @@ bogus-priv
 # The fields are <name>,<target>,<port>,<priority>,<weight>
 # If the domain part if missing from the name (so that is just has the
 # service and protocol sections) then the domain given by the domain=
-# config option is used.
+# config option is used. (Note that expand-hosts does not need to be
+# set for this to work.)
 
 # A SRV record sending LDAP for the example.com domain to
 # ldapserver.example.com port 289
@@ -329,6 +334,19 @@ bogus-priv
 # example.com 
 #srv-host=_ldap._tcp.example.com
 
+
+# Change the following lines to enable dnsmasq to serve TXT records.
+# These are used for things like SPF and zeroconf. (Note that the
+# domain-name expansion done for SRV records _does_not 
+# occur for TXT records.)
+
+#Example SPF.
+#txt-record=example.com,v=spf1 a -all
+
+#Example zeroconf
+#txt-record=_http._tcp.example.com,name=value,paper=A4
+
+
 # For debugging purposes, log each DNS query as it passes through
 # dnsmasq.
 #log-queries
index c8fbd225582468758294433bd10aa2c5bde74cf2..735cae8391e06dda3716128df88115628493c623 100644 (file)
--- a/doc.html
+++ b/doc.html
@@ -108,7 +108,7 @@ HREF="http://howto.linux-hardware-shop.de/dnsmasq.html">http://howto.linux-hardw
 and Damien Raude-Morvan has one in French at <A HREF="http://www.drazzib.com/docs-dnsmasq.html">http://www.drazzib.com/docs-dnsmasq.html</A>
 There is a good article about dnsmasq at <A
 HREF="http://www.enterprisenetworkingplanet.com/netos/article.php/3377351">http://www.enterprisenetworkingplanet.com/netos/article.php/3377351</A>
-
+and Ilya Evseev has an article in Russian about dnsmasq to be found at <A HREF="http://ilya-evseev.narod.ru/articles/dnsmasq"> http://ilya-evseev.narod.ru/articles/dnsmasq</A>
 <H2>License.</H2>
 Dnsmasq is distributed under the GPL. See the file COPYING in the distribution 
 for details.
index 77229d7d0cb277184d88f0a023e2c02c438da295..fe87c0d956f7201a90bfd06030df6149fb8e24e4 100644 (file)
@@ -4,7 +4,7 @@
 CFLAGS?= -O2
 
 OBJS = cache.o rfc1035.o util.o option.o forward.o isc.o \
-       network.o dnsmasq.o dhcp.o lease.o rfc2131.o 
+       network.o dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o
 
 .c.o: dnsmasq.h config.h 
        $(CC) $(CFLAGS) $(RPM_OPT_FLAGS) -Wall -W -c $*.c 
index b3e7d76824a73ebda270a6d395deb8f813676fbe..efd6aaf94407b5883937ddade3647e207b50a431 100644 (file)
@@ -846,8 +846,6 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
   if (!log_queries)
     return;
   
-  strcpy(types, " ");
-  
   if (flags & F_NEG)
     {
       if (flags & F_REVERSE)
@@ -875,6 +873,8 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
        strcpy(addrbuff, "<MX>");
       else if (flags & F_IPV6)
        strcpy(addrbuff, "<SRV>");
+      else if (flags & F_NXDOMAIN)
+       strcpy(addrbuff, "<TXT>");
       else
        strcpy(addrbuff, "<CNAME>");
     }
@@ -937,19 +937,19 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
       
       if (type != 0)
         {
-          sprintf(types, "[type=%d] ", type); 
+          sprintf(types, "query[type=%d]", type); 
           for (i = 0; i < (sizeof(typestr)/sizeof(typestr[0])); i++)
            if (typestr[i].type == type)
-             sprintf(types,"[%s] ", typestr[i].name);
+             sprintf(types,"query[%s]", typestr[i].name);
        }
-      source = "query";
+      source = types;
       verb = "from";
     }
   else
     source = "cached";
   
   if ((flags & F_FORWARD) | (flags & F_NEG))
-    syslog(LOG_DEBUG, "%s %s%s%s %s", source, name, types, verb, addrbuff);
+    syslog(LOG_DEBUG, "%s %s %s %s", source, name, verb, addrbuff);
   else if (flags & F_REVERSE)
     syslog(LOG_DEBUG, "%s %s is %s", source, addrbuff, name);
 }
index 997213ca7c123d97ec6dc1c9aa7fcf48e613d745..1b6158077a4dd62ff77442c3d24c97c891dc58e4 100644 (file)
@@ -12,7 +12,7 @@
 
 /* Author's email: simon@thekelleys.org.uk */
 
-#define VERSION "2.20"
+#define VERSION "2.21"
 
 #define FTABSIZ 150 /* max number of outstanding requests */
 #define MAX_PROCS 20 /* max no children for TCP requests */
@@ -158,6 +158,12 @@ HAVE_PSELECT
 HAVE_BPF
    If your OS implements Berkeley Packet filter, define this.
 
+HAVE_RTNETLINK
+   If your OS has the Linux Routing netlink socket API and suitable
+   C library headers, define this. Note that the code will fall
+   back to the Berkley API at runtime if netlink support is not
+   configured into the kernel.
+
 NOTES:
    For Linux you should define 
       HAVE_LINUX_IPV6_PROC 
@@ -165,6 +171,7 @@ NOTES:
       HAVE_RANDOM
       HAVE_DEV_RANDOM
       HAVE_DEV_URANDOM
+      HAVE_RTNETLINK
   you should NOT define 
       HAVE_ARC4RANDOM
       HAVE_SOCKADDR_SA_LEN
@@ -175,6 +182,7 @@ NOTES:
      HAVE_BPF
    you should NOT define  
      HAVE_LINUX_IPV6_PROC 
+     HAVE_RTNETLINK
    and you MAY define  
      HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
      HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
@@ -199,6 +207,7 @@ NOTES:
 #if defined(__uClinux__) || defined(__UCLIBC__)
 #undef HAVE_LINUX_IPV6_PROC
 #define HAVE_GETOPT_LONG
+#undef HAVE_RTNETLINK /* headers broken */
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
 #define HAVE_DEV_URANDOM
@@ -223,6 +232,7 @@ NOTES:
       (_LINUX_C_LIB_VERSION_MAJOR == 5 )
 #undef HAVE_IPV6
 #undef HAVE_LINUX_IPV6_PROC
+#undef HAVE_RTNETLINK
 #define HAVE_GETOPT_LONG
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
@@ -237,6 +247,7 @@ typedef size_t socklen_t;
 /* This is for glibc 2.x */
 #elif defined(__linux__)
 #define HAVE_LINUX_IPV6_PROC
+#define HAVE_RTNETLINK
 #define HAVE_GETOPT_LONG
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
@@ -256,6 +267,7 @@ typedef unsigned long in_addr_t;
 
 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
 #undef HAVE_LINUX_IPV6_PROC
+#undef HAVE_RTNETLINK
 /* Later verions of FreeBSD have getopt_long() */
 #if defined(optional_argument) && defined(required_argument)
 #   define HAVE_GETOPT_LONG
@@ -271,6 +283,7 @@ typedef unsigned long in_addr_t;
 
 #elif defined(__APPLE__)
 #undef HAVE_LINUX_IPV6_PROC
+#undef HAVE_RTNETLINK
 #undef HAVE_GETOPT_LONG
 #define HAVE_ARC4RANDOM
 #define HAVE_RANDOM
@@ -285,6 +298,7 @@ typedef unsigned long in_addr_t;
  
 #elif defined(__NetBSD__)
 #undef HAVE_LINUX_IPV6_PROC
+#undef HAVE_RTNETLINK
 #define HAVE_GETOPT_LONG
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
@@ -297,6 +311,7 @@ typedef unsigned long in_addr_t;
 /* env "LIBS=-lsocket -lnsl" make */
 #elif defined(__sun) || defined(__sun__)
 #undef HAVE_LINUX_IPV6_PROC
+#undef HAVE_RTNETLINK
 #undef HAVE_GETOPT_LONG
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
index 26ca7ac0d7a3f338b72acc572d49174c2bb6a846..b12215bb821313eaef37412e9287cbf3468954a7 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2003 Simon Kelley
+/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -98,10 +98,10 @@ void dhcp_init(struct daemon *daemon)
        die("duplicate IP address %s in dhcp-config directive.", inet_ntoa(cp->addr));
   
   daemon->dhcp_packet = safe_malloc(sizeof(struct udp_dhcp_packet));
-  /* These two each hold a DHCP option max size 25
+  /* These two each hold a DHCP option max size 255
      and get a terminating zero added */
-  daemon->dhcp_buff = safe_malloc(257);
-  daemon->dhcp_buff2 = safe_malloc(257); 
+  daemon->dhcp_buff = safe_malloc(256);
+  daemon->dhcp_buff2 = safe_malloc(256); 
 
 }
 
@@ -116,7 +116,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
   struct iovec iov[2];
   struct cmsghdr *cmptr;
   int sz, newlen, iface_index = 0;
-  struct in_addr iface_netmask, iface_addr, iface_broadcast;
+  struct in_addr iface_addr;
 #ifdef HAVE_BPF
   unsigned char iface_hwaddr[ETHER_ADDR_LEN];
 #endif
@@ -206,66 +206,29 @@ void dhcp_packet(struct daemon *daemon, time_t now)
        return; 
     }
   
-  iface_netmask.s_addr = 0;
-  iface_broadcast.s_addr = 0;
-  
   for (context = daemon->dhcp; context; context = context->next)
+    context->current = NULL;
+  
+#ifdef HAVE_RTNETLINK
+  if (!netlink_process(daemon, iface_index, mess->giaddr, iface_addr, &context))
+#endif
     {
-      /* Fill in missing netmask and broadcast address values for any approriate
-        dhcp-ranges which match this interface and don't have them. */
-      if (!context->netmask.s_addr)
-       {
-         if (!iface_netmask.s_addr && ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) != -1)
-           iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-         
-         if (iface_netmask.s_addr &&
-             (is_same_net(iface_addr, context->start, iface_netmask) ||
-              is_same_net(iface_addr, context->end, iface_netmask)))
-           {
-             context->netmask = iface_netmask; 
-             if (!(is_same_net(iface_addr, context->start, iface_netmask) &&
-                   is_same_net(iface_addr, context->end, iface_netmask)))
-               {
-                  strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
-                  strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
-                  syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
-                         daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(iface_netmask));
-               }
-           }
-       }    
-        
-      /* Determine "default" default routes. These are to this server or the relay agent.
-        Also broadcast addresses, if not specified */
-      if (context->netmask.s_addr)
-       {
-         if (is_same_net(iface_addr, context->start, context->netmask))
-           {
-             if (!context->router.s_addr)
-               context->router = iface_addr;
-             if (!context->broadcast.s_addr)
-               {
-                 if (!iface_broadcast.s_addr && ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) != -1)
-                   iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
-                 if (iface_broadcast.s_addr && 
-                     is_same_net(iface_broadcast, context->start, context->netmask))
-                   context->broadcast = iface_broadcast;
-                 else 
-                   context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
-               }
-           }   
-         else if (mess->giaddr.s_addr && is_same_net(mess->giaddr, context->start, context->netmask))
-           {
-             if (!context->router.s_addr)
-               context->router = mess->giaddr;
-             /* fill in missing broadcast addresses for relayed ranges */
-             if (!context->broadcast.s_addr)
-               context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
-           }
-       }
+      struct in_addr iface_netmask, iface_broadcast;
+      
+      if (ioctl(daemon->dhcpfd, SIOCGIFNETMASK, &ifr) < 0)
+       return;
+      iface_netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+      
+      if (ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) < 0)
+       return;
+      iface_broadcast = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
+      
+      context = complete_context(daemon, iface_addr, NULL, iface_netmask, 
+                                iface_broadcast, mess->giaddr, iface_addr);
     }
-  
+
   lease_prune(NULL, now); /* lose any expired leases */
-  newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now);
+  newlen = dhcp_reply(daemon, context, ifr.ifr_name, sz, now);
   lease_update_file(0, now);
   lease_update_dns(daemon);
   
@@ -382,6 +345,73 @@ void dhcp_packet(struct daemon *daemon, time_t now)
     }
 }
 
+/* This is a complex routine: it gets called with each (address,netmask,broadcast) triple 
+   of the interface on which a DHCP packet arrives (and any relay address) and does the 
+   following things:
+   1) Fills in any netmask and broadcast addresses which have not been explicitly configured.
+   2) Fills in local (this host) and router (this host or relay) addresses.
+   3) Links contexts which are valid for hosts directly connected to the arrival interface on ->current.
+   Note that the current chain may be superceded later for configured hosts or those coming via gateways. */
+struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, struct dhcp_context *current,
+                                     struct in_addr netmask, struct in_addr broadcast, struct in_addr relay,
+                                     struct in_addr primary)
+{
+  struct dhcp_context *context;
+  
+  for (context = daemon->dhcp; context; context = context->next)
+    {
+      if (!(context->flags & CONTEXT_NETMASK) &&
+         (is_same_net(local, context->start, netmask) ||
+          is_same_net(local, context->end, netmask)))
+      { 
+       if (context->netmask.s_addr != netmask.s_addr &&
+           !(is_same_net(local, context->start, netmask) &&
+             is_same_net(local, context->end, netmask)))
+         {
+           strcpy(daemon->dhcp_buff, inet_ntoa(context->start));
+           strcpy(daemon->dhcp_buff2, inet_ntoa(context->end));
+           syslog(LOG_WARNING, "DHCP range %s -- %s is not consistent with netmask %s",
+                  daemon->dhcp_buff, daemon->dhcp_buff2, inet_ntoa(netmask));
+         }     
+       context->netmask = netmask;
+      }
+      
+      if (context->netmask.s_addr)
+       {
+         if (is_same_net(local, context->start, context->netmask) &&
+             is_same_net(local, context->end, context->netmask))
+           {
+             if (!context->current)
+               {
+                 context->router = local;
+                 context->local = local;
+                 context->current = current;
+                 current = context;
+               }
+             
+             if (!(context->flags & CONTEXT_BRDCAST))
+               {
+                 if (is_same_net(broadcast, context->start, context->netmask))
+                   context->broadcast = broadcast;
+                 else 
+                   context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
+               }
+           }   
+         else if (relay.s_addr && is_same_net(relay, context->start, context->netmask))
+           {
+             context->router = relay;
+             context->local = primary;
+             /* fill in missing broadcast addresses for relayed ranges */
+             if (!(context->flags & CONTEXT_BRDCAST))
+               context->broadcast.s_addr  = context->start.s_addr | ~context->netmask.s_addr;
+           }
+
+       }
+    }
+
+  return current;
+}
+         
 struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr taddr)
 {
   /* Check is an address is OK for this network, check all
@@ -394,7 +424,7 @@ struct dhcp_context *address_available(struct dhcp_context *context, struct in_a
       start = ntohl(context->start.s_addr);
       end = ntohl(context->end.s_addr);
 
-      if (!context->static_only &&
+      if (!(context->flags & CONTEXT_STATIC) &&
          addr >= start &&
          addr <= end)
        return context;
@@ -419,7 +449,7 @@ struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr
     return tmp;
   
   for (tmp = context; tmp; tmp = tmp->current)
-    if (tmp->static_only)
+    if (tmp->flags & CONTEXT_STATIC)
       return tmp;
 
   return context;
@@ -470,26 +500,27 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
      Try to return from contexts which mathc netis first. */
 
   struct in_addr start, addr ;
+  struct dhcp_context *c;
   unsigned int i, j;
   
-  for (; context; context = context->current)
-    if (context->static_only)
+  for (c = context; c; c = c->current)
+    if (c->flags & CONTEXT_STATIC)
       continue;
-    else if (netids && !context->filter_netid)
+    else if (netids && !(c->flags & CONTEXT_FILTER))
       continue;
-    else if (!netids && context->filter_netid)
+    else if (!netids && (c->flags & CONTEXT_FILTER))
       continue;
-    else if (netids && context->filter_netid && !match_netid(&context->netid, netids))
+    else if (netids && (c->flags & CONTEXT_FILTER) && !match_netid(&c->netid, netids))
       continue;
     else
       {
        /* pick a seed based on hwaddr then iterate until we find a free address. */
-       for (j = context->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
+       for (j = c->addr_epoch, i = 0; i < ETHER_ADDR_LEN; i++)
          j += hwaddr[i] + (hwaddr[i] << 8) + (hwaddr[i] << 16);
        
        start.s_addr = addr.s_addr = 
-         htonl(ntohl(context->start.s_addr) + 
-               (j % (1 + ntohl(context->end.s_addr) - ntohl(context->start.s_addr))));
+         htonl(ntohl(c->start.s_addr) + 
+               (j % (1 + ntohl(c->end.s_addr) - ntohl(c->start.s_addr))));
        
        do {
          if (!lease_find_by_addr(addr) && 
@@ -498,7 +529,7 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
              if (icmp_ping(daemon, addr))
                /* perturb address selection so that we are
                   less likely to try this address again. */
-               context->addr_epoch++;
+               c->addr_epoch++;
              else
                {
                  *addrp = addr;
@@ -508,8 +539,8 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
 
          addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
          
-         if (addr.s_addr == htonl(ntohl(context->end.s_addr) + 1))
-           addr = context->start;
+         if (addr.s_addr == htonl(ntohl(c->end.s_addr) + 1))
+           addr = c->start;
          
        } while (addr.s_addr != start.s_addr);
       }
@@ -523,7 +554,7 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
 static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *config)
 {
   if (!context)
-    return 1;
+    return 1; 
   if (!(config->flags & CONFIG_ADDR))
     return 1;
   if (is_same_net(config->addr, context->start, context->netmask))
@@ -532,6 +563,7 @@ static int is_addr_in_context(struct dhcp_context *context, struct dhcp_config *
   return 0;
 }
 
+
 struct dhcp_config *find_config(struct dhcp_config *configs,
                                struct dhcp_context *context,
                                unsigned char *clid, int clid_len,
@@ -539,7 +571,7 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
 {
   struct dhcp_config *config; 
   
-  if (clid_len)
+  if (clid)
     for (config = configs; config; config = config->next)
       if (config->flags & CONFIG_CLID)
        {
@@ -558,24 +590,40 @@ struct dhcp_config *find_config(struct dhcp_config *configs,
   
   for (config = configs; config; config = config->next)
     if ((config->flags & CONFIG_HWADDR) &&
+       config->wildcard_mask == 0 &&
        memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0 &&
        is_addr_in_context(context, config))
       return config;
   
-  if (hostname)
+
+  if (hostname && context)
     for (config = configs; config; config = config->next)
       if ((config->flags & CONFIG_NAME) && 
          hostname_isequal(config->hostname, hostname) &&
          is_addr_in_context(context, config))
        return config;
   
+  for (config = configs; config; config = config->next)
+    if ((config->flags & CONFIG_HWADDR) &&
+       config->wildcard_mask != 0 &&
+       is_addr_in_context(context, config))
+      {
+       int i;
+       unsigned int mask = config->wildcard_mask;
+       for (i = ETHER_ADDR_LEN - 1; i >= 0; i--, mask = mask >> 1)
+         if (mask & 1)
+           config->hwaddr[i] = hwaddr[i];
+       if (memcmp(config->hwaddr, hwaddr, ETHER_ADDR_LEN) == 0)
+         return config;
+      }
+
   return NULL;
 }
 
 void dhcp_read_ethers(struct daemon *daemon)
 {
   FILE *f = fopen(ETHERSFILE, "r");
-  unsigned int flags, e0, e1, e2, e3, e4, e5;
+  unsigned int flags;
   char *buff = daemon->namebuff;
   char *ip, *cp;
   struct in_addr addr;
@@ -603,16 +651,9 @@ void dhcp_read_ethers(struct daemon *daemon)
       if (!*ip)
        continue;
       
-      if (!sscanf(buff, "%x:%x:%x:%x:%x:%x", &e0, &e1, &e2, &e3, &e4, &e5))
+      if (parse_hex(buff, hwaddr, 6, NULL) != 6)
        continue;
       
-      hwaddr[0] = e0;
-      hwaddr[1] = e1;
-      hwaddr[2] = e2;
-      hwaddr[3] = e3;
-      hwaddr[4] = e4;
-      hwaddr[5] = e5;
-
       /* check for name or dotted-quad */
       for (cp = ip; *cp; cp++)
        if (!(*cp == '.' || (*cp >='0' && *cp <= '9')))
index f65b7e67fd937a278e1a18fb4ae3c7740db24929..9cfbf8e8d82fe465367348fd65d6b51955c1f2d0 100644 (file)
@@ -14,7 +14,7 @@
 
 #include "dnsmasq.h"
 
-static int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
+static volatile int sigterm, sighup, sigusr1, sigalarm, num_kids, in_child;
 
 static int set_dns_listeners(struct daemon *daemon, fd_set *set, int maxfd);
 static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now);
@@ -69,8 +69,9 @@ int main (int argc, char **argv)
   
   if (daemon->edns_pktsz < PACKETSZ)
     daemon->edns_pktsz = PACKETSZ;
-  daemon->packet = safe_malloc(daemon->edns_pktsz > DNSMASQ_PACKETSZ ? 
-                              daemon->edns_pktsz : DNSMASQ_PACKETSZ);
+  daemon->packet_buff_sz = daemon->edns_pktsz > DNSMASQ_PACKETSZ ? 
+    daemon->edns_pktsz : DNSMASQ_PACKETSZ;
+  daemon->packet = safe_malloc(daemon->packet_buff_sz);
   
   if (!daemon->lease_file)
     {
@@ -272,31 +273,31 @@ int main (int argc, char **argv)
   if (daemon->dhcp)
     {
       struct dhcp_context *dhcp_tmp;
+
+#ifdef HAVE_RTNETLINK
+      /* Must do this after daemonizing so that the pid is right */
+      daemon->netlinkfd =  netlink_init();
+#endif
+      
       for (dhcp_tmp = daemon->dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
        {
-         char *time = daemon->dhcp_buff2;
+         prettyprint_time(daemon->dhcp_buff2, dhcp_tmp->lease_time);
          strcpy(daemon->dhcp_buff, inet_ntoa(dhcp_tmp->start));
-         if (dhcp_tmp->lease_time == 0)
-           sprintf(time, "infinite");
-         else
-           {
-             unsigned int x, p = 0, t = (unsigned int)dhcp_tmp->lease_time;
-             if ((x = t/3600))
-               p += sprintf(&time[p], "%dh", x);
-             if ((x = (t/60)%60))
-               p += sprintf(&time[p], "%dm", x);
-             if ((x = t%60))
-               p += sprintf(&time[p], "%ds", x);
-           }
          syslog(LOG_INFO, 
-                dhcp_tmp->static_only ? 
+                (dhcp_tmp->flags & CONTEXT_STATIC) ? 
                 "DHCP, static leases only on %.0s%s, lease time %s" :
                 "DHCP, IP range %s -- %s, lease time %s",
-                daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), time);
+                daemon->dhcp_buff, inet_ntoa(dhcp_tmp->end), daemon->dhcp_buff2);
        }
  
 #ifdef HAVE_BROKEN_RTC
-      syslog(LOG_INFO, "DHCP, %s will be written every %ds", daemon->lease_file, daemon->min_leasetime/3);
+      daemon->min_leasetime = daemon->min_leasetime/3;
+      if (daemon->min_leasetime > (60 * 60 * 24))
+       daemon->min_leasetime = 60 * 60 * 24;
+      if (daemon->min_leasetime < 60)
+       daemon->min_leasetime = 60;
+      prettyprint_time(daemon->dhcp_buff2, daemon->min_leasetime);
+      syslog(LOG_INFO, "DHCP, %s will be written every %s", daemon->lease_file, daemon->dhcp_buff2);
 #endif
     }
 
@@ -341,7 +342,7 @@ int main (int argc, char **argv)
            {
              lease_update_file(1, now);
 #ifdef HAVE_BROKEN_RTC
-             alarm(daemon->min_leasetime/3);
+             alarm(daemon->min_leasetime);
 #endif
            } 
          sigalarm  = 0;
@@ -681,3 +682,5 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
   
   return gotreply;
 }
+
index 46da01829090951c62110785ea71bf678d2eaa24..5a5b104fc01406d17808eb03abb2a7c3e076bb01 100644 (file)
 #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
+/* Min buffer size: we check after adding each record, so there must be 
+   memory for the largest packet, and the largest record so the
+   min for DNS is PACKETSZ+MAXDNAME+RRFIXEDSZ which is < 1000.
+   This might be increased is EDNS packet size if greater than the minimum.
+   The buffer is also used for NETLINK, which needs to be about 2000
+   on systems with many interfaces/addresses. */
+#ifdef HAVE_RTNETLINK
+# define DNSMASQ_PACKETSZ PACKETSZ+MAXDNAME+RRFIXEDSZ
+#else
+# define DNSMASQ_PACKETSZ 2000
+#endif
 
 #define OPT_BOGUSPRIV      1
 #define OPT_FILTER         2
@@ -123,16 +131,16 @@ struct doctor {
   struct doctor *next;
 };
 
-struct mx_record {
-  char *mxname, *mxtarget;
-  int preference;
-  struct mx_record *next;
+struct mx_srv_record {
+  char *name, *target;
+  int issrv, srvport, priority, weight, offset;
+  struct mx_srv_record *next;
 };
 
-struct srv_record {
-  char *srvname, *srvtarget;
-  int srvport, priority, weight;
-  struct srv_record *next;
+struct txt_record {
+  char *name, *txt;
+  unsigned short class, len;
+  struct txt_record *next;
 };
 
 union bigname {
@@ -199,14 +207,16 @@ union mysockaddr {
 #endif
 };
 
-#define SERV_FROM_RESOLV     1  /* 1 for servers from resolv, 0 for command line. */
-#define SERV_NO_ADDR         2  /* no server, this domain is local only */
-#define SERV_LITERAL_ADDRESS 4  /* addr is the answer, not the server */ 
-#define SERV_HAS_SOURCE      8  /* source address specified */
-#define SERV_HAS_DOMAIN     16  /* server for one domain only */
-#define SERV_FOR_NODOTS     32  /* server for names with no domain part only */
+#define SERV_FROM_RESOLV       1  /* 1 for servers from resolv, 0 for command line. */
+#define SERV_NO_ADDR           2  /* no server, this domain is local only */
+#define SERV_LITERAL_ADDRESS   4  /* addr is the answer, not the server */ 
+#define SERV_HAS_SOURCE        8  /* source address specified */
+#define SERV_HAS_DOMAIN       16  /* server for one domain only */
+#define SERV_FOR_NODOTS       32  /* server for names with no domain part only */
+#define SERV_WARNED_RECURSIVE 64  /* avoid warning spam */
 #define SERV_TYPE    (SERV_HAS_DOMAIN | SERV_FOR_NODOTS)
 
+
 struct serverfd {
   int fd;
   union mysockaddr source_addr;
@@ -263,7 +273,7 @@ struct frec {
   struct server *sentto;
   unsigned int iface;
   unsigned short orig_id, new_id;
-  int fd;
+  int fd, forwardall;
   unsigned int crc;
   time_t time;
   struct frec *next;
@@ -296,18 +306,18 @@ struct dhcp_config {
   char *hostname;
   struct dhcp_netid netid;
   struct in_addr addr;
-  unsigned int lease_time;
+  unsigned int lease_time, wildcard_mask;
   struct dhcp_config *next;
 };
 
-#define CONFIG_DISABLE   1
-#define CONFIG_CLID      2
-#define CONFIG_HWADDR    4
-#define CONFIG_TIME      8
-#define CONFIG_NAME     16
-#define CONFIG_ADDR     32
-#define CONFIG_NETID    64
-#define CONFIG_NOCLID  128
+#define CONFIG_DISABLE           1
+#define CONFIG_CLID              2
+#define CONFIG_HWADDR            4
+#define CONFIG_TIME              8
+#define CONFIG_NAME             16
+#define CONFIG_ADDR             32
+#define CONFIG_NETID            64
+#define CONFIG_NOCLID          128
 
 struct dhcp_opt {
   int opt, len, is_addr;
@@ -332,13 +342,20 @@ struct dhcp_vendor {
 
 struct dhcp_context {
   unsigned int lease_time, addr_epoch;
-  struct in_addr netmask, broadcast, router;
+  struct in_addr netmask, broadcast;
+  struct in_addr local, router;
   struct in_addr start, end; /* range of available addresses */
-  int static_only, filter_netid;
+  int flags;
   struct dhcp_netid netid;
   struct dhcp_context *next, *current;
 };
 
+#define CONTEXT_STATIC    1
+#define CONTEXT_FILTER    2
+#define CONTEXT_NETMASK   4
+#define CONTEXT_BRDCAST   8
+
+
 typedef unsigned char u8;
 typedef unsigned short u16;
 typedef unsigned int u32;
@@ -370,12 +387,12 @@ struct daemon {
 
   unsigned int options;
   struct resolvc default_resolv, *resolv_files;
-  struct mx_record *mxnames;
+  struct mx_srv_record *mxnames;
+  struct txt_record *txt;
   char *mxtarget;
   char *lease_file; 
   char *username, *groupname;
   char *domain_suffix;
-  struct srv_record *srvnames;
   char *runfile; 
   struct iname *if_names, *if_addrs, *if_except;
   struct bogus_addr *bogus_addr;
@@ -397,6 +414,7 @@ struct daemon {
 
   /* globally used stuff for DNS */
   char *packet; /* packet buffer */
+  int packet_buff_sz; /* size of above */
   char *namebuff; /* MAXDNAME size buffer */
   struct serverfd *sfds;
   struct listener *listeners;
@@ -405,6 +423,9 @@ struct daemon {
   
   /* DHCP state */
   int dhcpfd, dhcp_raw_fd, dhcp_icmp_fd, lease_fd;
+#ifdef HAVE_RTNETLINK
+  int netlinkfd;
+#endif
   struct udp_dhcp_packet *dhcp_packet;
   char *dhcp_buff, *dhcp_buff2;
 };
@@ -443,7 +464,7 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
 unsigned char *find_pseudoheader(HEADER *header, unsigned int plen,
                                 unsigned int *len, unsigned char **p);
 int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
-unsigned int questions_crc(HEADER *header, unsigned int plen);
+unsigned int questions_crc(HEADER *header, unsigned int plen, char *buff);
 int resize_packet(HEADER *header, unsigned int plen, 
                  unsigned char *pheader, unsigned int hlen);
 
@@ -453,8 +474,8 @@ int legal_char(char c);
 int canonicalise(char *s);
 int atoi_check(char *a, int *res);
 void die(char *message, char *arg1);
-void complain(char *message, char *arg1);
-void *safe_malloc(int size);
+void complain(char *message, int lineno, char *file);
+void *safe_malloc(size_t size);
 char *safe_string_alloc(char *cp);
 int sa_len(union mysockaddr *addr);
 int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
@@ -462,6 +483,9 @@ int hostname_isequal(unsigned char *a, unsigned char *b);
 time_t dnsmasq_time(int fd);
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
 int retry_send(void);
+void prettyprint_time(char *buf, unsigned int t);
+int parse_hex(char *in, unsigned char *out, int maxlen, 
+             unsigned int *wildcard_mask);
 
 /* option.c */
 struct daemon *read_opts (int argc, char **argv);
@@ -501,22 +525,29 @@ void dhcp_read_ethers(struct daemon *daemon);
 struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct in_addr addr);
 char *strip_hostname(struct daemon *daemon, char *hostname);
 char *host_from_dns(struct daemon *daemon, struct in_addr addr);
+struct dhcp_context *complete_context(struct daemon *daemon, struct in_addr local, 
+                                     struct dhcp_context *current, struct in_addr netmask, 
+                                     struct in_addr broadcast, struct in_addr relay,
+                                     struct in_addr primary);
 
 /* lease.c */
 void lease_update_file(int force, time_t now);
 void lease_update_dns(struct daemon *daemon);
 void lease_init(struct daemon *daemon, time_t now);
-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);
+struct dhcp_lease *lease_allocate(unsigned char *hwaddr, unsigned char *clid,
+                                 int clid_len, struct in_addr addr);
+int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
+                     unsigned char *clid, int clid_len);
 void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix);
 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_client(unsigned char *hwaddr,
+                                       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 daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now);
+int dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, unsigned int sz, time_t now);
 
 /* dnsmasq.c */
 int icmp_ping(struct daemon *daemon, struct in_addr addr);
@@ -526,3 +557,10 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr);
 void load_dhcp(struct daemon *daemon, time_t now);
 #endif
 
+/* netlink.c */
+#ifdef HAVE_RTNETLINK
+int netlink_init(void);
+int netlink_process(struct daemon *daemon, int index, 
+                   struct in_addr relay, struct in_addr primary,
+                   struct dhcp_context **retp);
+#endif
index 542008dee8cc9ac49d26ac66f8b09481e4d4805e..065202fbfc000a9a7e3cae4259acaa488ec0a69f 100644 (file)
@@ -213,12 +213,12 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
 {
   struct frec *forward;
   char *domain = NULL;
-  int forwardall = 0, type = 0;
+  int type = 0;
   struct all_addr *addrp = NULL;
   unsigned short flags = 0;
   unsigned short gotname = extract_request(header, (unsigned int)plen, daemon->namebuff, NULL);
   struct server *start = NULL;
-  unsigned int crc = questions_crc(header,(unsigned int)plen);
+  unsigned int crc = questions_crc(header,(unsigned int)plen, daemon->namebuff);
   
   /* may be  recursion not speced or no servers available. */
   if (!header->rd || !daemon->servers)
@@ -229,7 +229,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
       domain = forward->sentto->domain;
       if (!(daemon->options & OPT_ORDER))
        {
-         forwardall = 1;
+         forward->forwardall = 1;
          daemon->last_server = NULL;
        }
       type = forward->sentto->flags & SERV_TYPE;
@@ -248,6 +248,16 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
       
       if (forward)
        {
+         forward->source = *udpaddr;
+         forward->dest = *dst_addr;
+         forward->iface = dst_iface;
+         forward->new_id = get_id();
+         forward->fd = udpfd;
+         forward->orig_id = ntohs(header->id);
+         forward->crc = crc;
+         forward->forwardall = 0;
+         header->id = htons(forward->new_id);
+
          /* In strict_order mode, or when using domain specific servers
             always try servers in the order specified in resolv.conf,
             otherwise, use the one last known to work. */
@@ -257,17 +267,8 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
          else if (!(start = daemon->last_server))
            {
              start = daemon->servers;
-             forwardall = 1;
+             forward->forwardall = 1;
            }
-         
-         forward->source = *udpaddr;
-         forward->dest = *dst_addr;
-         forward->iface = dst_iface;
-         forward->new_id = get_id();
-         forward->fd = udpfd;
-         forward->orig_id = ntohs(header->id);
-         forward->crc = crc;
-         header->id = htons(forward->new_id);
        }
     }
 
@@ -313,8 +314,9 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
 #endif 
                  forwarded = 1;
                  forward->sentto = start;
-                 if (!forwardall) 
+                 if (!forward->forwardall) 
                    break;
+                 forward->forwardall++;
                }
            } 
          
@@ -341,7 +343,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
 }
 
 static int process_reply(struct daemon *daemon, HEADER *header, time_t now, 
-                        union mysockaddr *serveraddr, unsigned int n)
+                        unsigned int query_crc, struct server *server, unsigned int n)
 {
   unsigned char *pheader, *sizep;
   unsigned int plen, munged = 0;
@@ -360,25 +362,27 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
        PUTSHORT(daemon->edns_pktsz, psave);
     }
 
+  if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
+    return n;
+  
   /* Complain loudly if the upstream server is non-recursive. */
-  if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
+  if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0 &&
+      server && !(server->flags & SERV_WARNED_RECURSIVE))
     {
       char addrbuff[ADDRSTRLEN];
 #ifdef HAVE_IPV6
-      if (serveraddr->sa.sa_family == AF_INET)
-       inet_ntop(AF_INET, &serveraddr->in.sin_addr, addrbuff, ADDRSTRLEN);
-      else if (serveraddr->sa.sa_family == AF_INET6)
-       inet_ntop(AF_INET6, &serveraddr->in6.sin6_addr, addrbuff, ADDRSTRLEN);
+      if (server->addr.sa.sa_family == AF_INET)
+       inet_ntop(AF_INET, &server->addr.in.sin_addr, addrbuff, ADDRSTRLEN);
+      else if (server->addr.sa.sa_family == AF_INET6)
+       inet_ntop(AF_INET6, &server->addr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
 #else
-      strcpy(addrbuff, inet_ntoa(serveraddr->in.sin_addr));
+      strcpy(addrbuff, inet_ntoa(server->addr.in.sin_addr));
 #endif
       syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
-      return 0;
-    }
-  
-  if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
-    return n;
-  
+      if (!(daemon->options & OPT_LOG))
+       server->flags |= SERV_WARNED_RECURSIVE;
+    }  
+    
   if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
       check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
     {
@@ -400,7 +404,11 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
          header->rcode = NOERROR;
        }
   
-      extract_addresses(header, n, daemon->namebuff, now, daemon);
+      /* If the crc of the question section doesn't match the crc we sent, then
+        someone might be attempting to insert bogus values into the cache by 
+        sending replies containing questions and bogus answers. */
+      if (query_crc == questions_crc(header, n, daemon->namebuff))
+       extract_addresses(header, n, daemon->namebuff, now, daemon);
     }
   
   /* do this after extract_addresses. Ensure NODATA reply and remove
@@ -442,28 +450,42 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
   
   if (n >= (int)sizeof(HEADER) && header->qr && forward)
     {
-      /* find good server by address if possible, otherwise assume the last one we sent to */ 
-      if ((forward->sentto->flags & SERV_TYPE) == 0)
-       {
-         struct server *last_server;
-         daemon->last_server = forward->sentto;
-         for (last_server = daemon->servers; last_server; last_server = last_server->next)
-           if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
-               sockaddr_isequal(&last_server->addr, &serveraddr))
-             {
-               daemon->last_server = last_server;
-               break;
-             }
-       }
-      
-      if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
+       struct server *server = forward->sentto;
+       
+       if ((forward->sentto->flags & SERV_TYPE) == 0)
+        {
+          if (header->rcode == SERVFAIL || header->rcode == REFUSED)
+            server = NULL;
+          else
+           {
+             /* find good server by address if possible, otherwise assume the last one we sent to */ 
+             struct server *last_server;
+             for (last_server = daemon->servers; last_server; last_server = last_server->next)
+               if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
+                   sockaddr_isequal(&last_server->addr, &serveraddr))
+                 {
+                   server = last_server;
+                   break;
+                 }
+           } 
+          daemon->last_server = server;
+        }
+       
+      if ((n = process_reply(daemon, header, now, forward->crc, server, (unsigned int)n)))
        {
          header->id = htons(forward->orig_id);
          header->ra = 1; /* recursion if available */
          send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n, 
                    &forward->source, &forward->dest, forward->iface);
-         forward->new_id = 0; /* cancel */
        }
+        
+      /* If the answer is an error, keep the forward record in place in case
+        we get a good reply from another server. Kill it when we've
+        had replies from all to avoid filling the forwarding table when
+        everything is broken */
+      if (forward->forwardall == 0 || --forward->forwardall == 1 || 
+         (header->rcode != REFUSED && header->rcode != SERVFAIL))
+       forward->new_id = 0; /* cancel */
     }
 }
 
@@ -728,7 +750,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now,
          if (!flags && last_server)
            {
              struct server *firstsendto = NULL;
-             
+             unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
+
              /* Loop round available servers until we succeed in connecting to one.
                 Note that this code subtley ensures that consecutive queries on this connection
                 which can go to the same server, do so. */
@@ -793,7 +816,7 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now,
                  /* There's no point in updating the cache, since this process will exit and
                     lose the information after one query. We make this call for the alias and 
                     bogus-nxdomain side-effects. */
-                 m = process_reply(daemon, header, now, &last_server->addr, (unsigned int)m);
+                 m = process_reply(daemon, header, now, crc, last_server, (unsigned int)m);
                  
                  break;
                }
index 2664a8a77cb2ab79c301a67edf894edd8d97bce8..96a8209282710dacfba2882d9e95133fc4c82340 100644 (file)
 
 static struct dhcp_lease *leases;
 static FILE *lease_file;
-static int dns_dirty, file_dirty, new_lease;
+static int dns_dirty;
+enum { no, yes, force } file_dirty;
 static int leases_left;
 
 void lease_init(struct daemon *daemon, time_t now)
 {
-  unsigned int e0, e1, e2, e3, e4, e5, a0, a1, a2, a3;
+  unsigned int a0, a1, a2, a3;
   unsigned long ei;
   time_t expires;
   unsigned char hwaddr[ETHER_ADDR_LEN];
@@ -29,9 +30,7 @@ void lease_init(struct daemon *daemon, time_t now)
   struct dhcp_lease *lease;
   int clid_len = 0;
   int has_old = 0;
-  char *buff = daemon->dhcp_buff;
-  char *buff2 = daemon->dhcp_buff2;
-  
+
   leases = NULL;
   leases_left = daemon->dhcp_max;
 
@@ -42,9 +41,11 @@ void lease_init(struct daemon *daemon, time_t now)
   /* a+ mode lease pointer at end. */
   rewind(lease_file);
 
-  while (fscanf(lease_file, "%lu %x:%x:%x:%x:%x:%x %d.%d.%d.%d %257s %257s",
-               &ei, &e0, &e1, &e2, &e3, &e4, &e5, &a0, &a1, &a2, &a3, 
-               buff, buff2) == 13)
+  /* client-id max length is 255 which is 255*2 digits + 254 colons 
+     borrow DNS packet buffer which is always larger than 1000 bytes */
+  while (fscanf(lease_file, "%lu %40s %d.%d.%d.%d %255s %764s",
+               &ei, daemon->dhcp_buff2, &a0, &a1, &a2, &a3, 
+               daemon->dhcp_buff, daemon->packet) == 8)
     {
 #ifdef HAVE_BROKEN_RTC
       if (ei)
@@ -63,41 +64,26 @@ void lease_init(struct daemon *daemon, time_t now)
        }
 #endif
 
-      hwaddr[0] = e0;
-      hwaddr[1] = e1;
-      hwaddr[2] = e2;
-      hwaddr[3] = e3;
-      hwaddr[4] = e4;
-      hwaddr[5] = e5;
-      
+      parse_hex(daemon->dhcp_buff2, hwaddr, ETHER_ADDR_LEN, NULL);
       addr.s_addr = htonl((a0<<24) + (a1<<16) + (a2<<8) + a3);
 
       /* decode hex in place */
-      if (strcmp(buff2, "*") == 0)
+      if (strcmp(daemon->packet, "*") == 0)
        clid_len = 0;
       else
-       { 
-         int s = (strlen(buff2)/3) + 1;
-         for (clid_len = 0; clid_len < s; clid_len++)
-         {
-           buff2[(clid_len*3)+2] = 0;
-           buff2[clid_len] = strtol(&buff2[clid_len*3], NULL, 16);
-         }
-       }
+       clid_len = parse_hex(daemon->packet, daemon->packet, 255, NULL);
       
-      if (!(lease = lease_allocate(buff2, clid_len, addr)))
+      if (!(lease = lease_allocate(hwaddr, daemon->packet, clid_len, addr)))
        die ("too many stored leases", NULL);
       
       lease->expires = expires;
-      memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
 
-      if (strcmp(buff, "*") !=  0)
-         lease_set_hostname(lease, buff, daemon->domain_suffix);
+      if (strcmp(daemon->dhcp_buff, "*") !=  0)
+         lease_set_hostname(lease, daemon->dhcp_buff, daemon->domain_suffix);
     }
   
   dns_dirty = 1;
-  file_dirty = has_old;
-  new_lease = 0;
+  file_dirty = has_old ? yes: no;
 
   daemon->lease_fd = fileno(lease_file);
 }
@@ -115,18 +101,18 @@ void lease_update_from_configs(struct dhcp_config *dhcp_configs, char *domain)
       lease_set_hostname(lease, config->hostname, domain);
 }
 
-void lease_update_file(int force, time_t now)
+void lease_update_file(int always, time_t now)
 {
   struct dhcp_lease *lease;
-  int i = force; /* avoid warning */
+  int i = always; /* avoid warning */
   unsigned long expires;
 
 #ifdef HAVE_BROKEN_RTC
-  if (force || new_lease)
+  if (always || file_dirty == force)
     {
       lease_prune(NULL, now);
 #else
-  if (file_dirty)
+  if (file_dirty != no)
     {
 #endif
       rewind(lease_file);
@@ -149,7 +135,7 @@ void lease_update_file(int force, time_t now)
                  lease->hwaddr[5], inet_ntoa(lease->addr),
                  lease->hostname && strlen(lease->hostname) != 0 ? lease->hostname : "*");
          
-         if (lease->clid_len)
+         if (lease->clid && lease->clid_len != 0)
            {
              for (i = 0; i < lease->clid_len - 1; i++)
                fprintf(lease_file, "%.2x:", lease->clid[i]);
@@ -162,8 +148,7 @@ void lease_update_file(int force, time_t now)
 
       fflush(lease_file);
       fsync(fileno(lease_file));
-      file_dirty = 0;
-      new_lease = 0;
+      file_dirty = no;
     }
 }
 
@@ -194,7 +179,7 @@ void lease_prune(struct dhcp_lease *target, time_t now)
       tmp = lease->next;
       if ((lease->expires != 0 && difftime(now, lease->expires) > 0) || lease == target)
        {
-         file_dirty = 1;
+         file_dirty = yes;
 
          *up = lease->next; /* unlink */
          if (lease->hostname)
@@ -215,23 +200,21 @@ void lease_prune(struct dhcp_lease *target, time_t now)
 } 
        
   
-struct dhcp_lease *lease_find_by_client(unsigned char *clid, int clid_len)
+struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr,
+                                       unsigned char *clid, int clid_len)
 {
   struct dhcp_lease *lease;
 
-  if (clid_len)
-    {
-      for (lease = leases; lease; lease = lease->next)
-       if (lease->clid && clid_len == lease->clid_len &&
+  if (clid)
+    for (lease = leases; lease; lease = lease->next)
+      if (lease->clid && clid_len == lease->clid_len &&
          memcmp(clid, lease->clid, clid_len) == 0)
        return lease;
-    }
-  else
-    {
-      for (lease = leases; lease; lease = lease->next) 
-       if (memcmp(clid, lease->hwaddr, ETHER_ADDR_LEN) == 0)
-         return lease;
-    }
+  
+  for (lease = leases; lease; lease = lease->next)     
+    if ((!lease->clid || !clid) && 
+       memcmp(hwaddr, lease->hwaddr, ETHER_ADDR_LEN) == 0)
+      return lease;
   
   return NULL;
 }
@@ -248,35 +231,29 @@ 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_allocate(unsigned char *hwaddr, unsigned char *clid, 
+                                 int clid_len, struct in_addr addr)
 {
   struct dhcp_lease *lease;
   if (!leases_left || !(lease = malloc(sizeof(struct dhcp_lease))))
     return NULL;
 
   lease->clid = NULL;
-  lease->clid_len = clid_len;
-  
-  if (clid_len)
-    {
-      if (!(lease->clid = malloc(clid_len)))
-       {
-        free(lease);
-        return NULL;
-       }
-      memcpy(lease->clid, clid, clid_len);
-    }
-  
-  lease->hostname = lease->fqdn = NULL;
+  lease->hostname = lease->fqdn = NULL;  
   lease->addr = addr;
   memset(lease->hwaddr, 0, ETHER_ADDR_LEN);
   lease->expires = 1;
   
+  if (!lease_set_hwaddr(lease, hwaddr, clid, clid_len))
+    {
+      free(lease);
+      return NULL;
+    }
+
   lease->next = leases;
   leases = lease;
   
-  file_dirty = 1;
-  new_lease = 1;
+  file_dirty = force;
   leases_left--;
 
   return lease;
@@ -285,18 +262,46 @@ struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_a
 void lease_set_expires(struct dhcp_lease *lease, time_t exp)
 {
   if (exp != lease->expires)
-    file_dirty = dns_dirty = 1;
-
+    {
+      file_dirty = yes;
+      dns_dirty = 1;
+    }
   lease->expires = exp;
 }
 
-void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr)
+int lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr,
+                     unsigned char *clid, int clid_len)
 {
   if (memcmp(lease->hwaddr, hwaddr, ETHER_ADDR_LEN) != 0)
     {
-      file_dirty = 1;
+      file_dirty = force;
       memcpy(lease->hwaddr, hwaddr, ETHER_ADDR_LEN);
     }
+
+  /* only update clid when one is available, stops packets
+     without a clid removing the record. Lease init uses
+     clid_len == 0 for no clid. */
+  if (clid_len != 0 && clid)
+    {
+      if (!lease->clid)
+       lease->clid_len = 0;
+
+      if (lease->clid_len != clid_len)
+       {
+         file_dirty = force;
+         if (lease->clid)
+           free(lease->clid);
+         if (!(lease->clid = malloc(clid_len)))
+           return 0;
+       }
+      else if (memcmp(lease->clid, clid, clid_len) != 0)
+       file_dirty = force;
+
+      lease->clid_len = clid_len;
+      memcpy(lease->clid, clid, clid_len);
+    }
+
+  return 1;
 }
 
 void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
@@ -347,7 +352,8 @@ void lease_set_hostname(struct dhcp_lease *lease, char *name, char *suffix)
   lease->hostname = new_name;
   lease->fqdn = new_fqdn;
   
-  file_dirty = dns_dirty = 1;
+  file_dirty = force;
+  dns_dirty = 1;
 }
 
 
diff --git a/src/netlink.c b/src/netlink.c
new file mode 100644 (file)
index 0000000..5526b47
--- /dev/null
@@ -0,0 +1,153 @@
+/* dnsmasq is Copyright (c) 2000-2005 Simon Kelley
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 dated June, 1991.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+/* Author's email: simon@thekelleys.org.uk */
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_RTNETLINK
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+int netlink_init(void)
+{
+  struct sockaddr_nl addr;
+  int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+  
+  if (sock < 0)
+    return -1; /* no kernel support */
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad = 0;
+  addr.nl_pid = getpid();
+  addr.nl_groups = 0;
+  
+  if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+    die("cannot bind netlink socket: %s", NULL);
+  
+  return sock;
+}
+
+
+/* We borrow the DNS packet buffer here. (The DHCP one already has a packet in it)
+   Since it's used only within this routine, that's fine, just remember 
+   that calling icmp_echo() will trash it */
+int netlink_process(struct daemon *daemon, int index, struct in_addr relay, 
+                   struct in_addr primary, struct dhcp_context **retp)
+{
+  struct sockaddr_nl addr;
+  struct nlmsghdr *h;
+  int len, found_primary = 0;
+  struct dhcp_context *ret = NULL;
+  static unsigned int seq = 0;
+
+  struct {
+    struct nlmsghdr nlh;
+    struct rtgenmsg g; 
+  } req;
+
+  if (daemon->netlinkfd == -1)
+    return 0;
+
+  addr.nl_family = AF_NETLINK;
+  addr.nl_pad = 0;
+  addr.nl_groups = 0;
+  addr.nl_pid = 0; /* address to kernel */
+
+  req.nlh.nlmsg_len = sizeof(req);
+  req.nlh.nlmsg_type = RTM_GETADDR;
+  req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
+  req.nlh.nlmsg_pid = 0;
+  req.nlh.nlmsg_seq = ++seq;
+  req.g.rtgen_family = AF_INET; 
+
+  /* Don't block in recvfrom if send fails */
+  while((len = sendto(daemon->netlinkfd, (void *)&req, sizeof(req), 0, 
+                     (struct sockaddr *)&addr, sizeof(addr))) == -1 && retry_send());
+
+  if (len == -1)
+    {
+      /* if RTnetlink not configured in the kernel, don't keep trying. */
+      if (errno == ECONNREFUSED)
+       {
+         close(daemon->netlinkfd);
+         daemon->netlinkfd = -1;
+       }
+      return 0;
+    }
+
+ get_next:
+  while((len = recvfrom(daemon->netlinkfd, daemon->packet, daemon->packet_buff_sz, 
+                       MSG_WAITALL, NULL, 0)) == -1 && retry_send());
+  if (len == -1)
+    return 0;
+  
+  h = (struct nlmsghdr *)daemon->packet;
+
+  while (NLMSG_OK(h, (unsigned int)len))
+    {
+          
+      if (h->nlmsg_seq != seq)
+       goto get_next;
+       
+      if (h->nlmsg_type == NLMSG_DONE)
+       break;
+
+      if (h->nlmsg_type == NLMSG_ERROR)
+       return 0;
+      
+      if (h->nlmsg_type == RTM_NEWADDR)
+       {
+         struct ifaddrmsg *ifa = NLMSG_DATA(h);
+         
+         if (ifa->ifa_index == index && ifa->ifa_family == AF_INET)
+           {
+             struct rtattr *rta = IFA_RTA(ifa);
+             unsigned int len1 = h->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa));
+             struct in_addr netmask, addr, broadcast;
+             
+             netmask.s_addr = htonl(0xffffffff << (32 - ifa->ifa_prefixlen));
+             addr.s_addr = 0;
+             broadcast.s_addr = 0;
+             
+             while (RTA_OK(rta, len1))
+               {
+                 if (rta->rta_type == IFA_LOCAL)
+                   addr = *((struct in_addr *)(rta+1));
+                 else if (rta->rta_type == IFA_BROADCAST)
+                   broadcast = *((struct in_addr *)(rta+1));
+                 
+                 rta = RTA_NEXT(rta, len1);
+               }
+             
+             if (addr.s_addr && broadcast.s_addr)
+               {
+                 ret = complete_context(daemon, addr, ret, netmask, broadcast, relay, primary);        
+                 if (addr.s_addr == primary.s_addr)
+                   found_primary = 1;
+               }
+           }
+       }
+
+      h = NLMSG_NEXT(h, len);
+    }
+
+  *retp = ret;
+
+  return found_primary;
+}
+
+#endif
+
+      
index 216d70f0265471923aba83fd62ca11144fbc147f..dc2231240bc81a0cc1037ded0acd9f75d294b5a8 100644 (file)
@@ -200,7 +200,7 @@ int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
   if ((f = fopen(IP6INTERFACES, "r")))
     {
       unsigned int plen, scope, flags, if_idx;
-      char devname[20], addrstring[32];
+      char devname[21], addrstring[33];
       
       while (fscanf(f, "%32s %x %x %x %x %20s\n",
                    addrstring, &if_idx, &plen, &scope, &flags, devname) != EOF) 
index 4844477cef541c44388c95d91d7a429061dfbffc..4d54f4db00a009a684253f6e93fdba737495c09b 100644 (file)
@@ -21,7 +21,7 @@ struct myoption {
   int val;
 };
 
-#define OPTSTRING "yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:"
+#define OPTSTRING "yZDNLERKzowefnbvhdkqr:m:p:c:l:s:i:t:u:g:a:x:S:C:A:T:H:Q:I:B:F:G:O:M:X:V:U:j:P:J:W:Y:"
 
 static struct myoption opts[] = { 
   {"version", 0, 0, 'v'},
@@ -78,6 +78,7 @@ static struct myoption opts[] = {
   {"dhcp-authoritative", 0, 0, 'K'},
   {"srv-host", 1, 0, 'W'},
   {"localise-queries", 0, 0, 'y'},
+  {"txt-record", 1, 0, 'Y'},
   {0, 0, 0, 0}
 };
 
@@ -161,15 +162,30 @@ static char *usage =
 "-U, --dhcp-vendorclass=<id>,<class> Map DHCP vendor class to option set.\n"
 "-v, --version                       Display dnsmasq version and copyright information.\n"
 "-V, --alias=addr,addr,mask          Translate IPv4 addresses from upstream servers.\n"
-"-W, --srv-host=name,port,pri,weight Specify a SRV record.\n"
+"-W, --srv-host=name,target,...      Specify a SRV record.\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"
-"-y, --localise-queries              Answer DNS queries based on the interface a query was sent to."
+"-y, --localise-queries              Answer DNS queries based on the interface a query was sent to.\n"
+"-Y  --txt-record=name,txt....       Specify TXT DNS record.\n"
 "-z, --bind-interfaces               Bind only to interfaces in use.\n"
 "-Z, --read-ethers                   Read DHCP static host information from " ETHERSFILE ".\n"
 "\n";
 
+static void add_txt(struct daemon *daemon, char *name, char *txt)
+{
+  size_t len = strlen(txt);
+  struct txt_record *r = safe_malloc(sizeof(struct txt_record));
+  
+  r->name = safe_string_alloc(name);
+  r->next = daemon->txt;
+  daemon->txt = r;
+  r->class = C_CHAOS;
+  r->txt = safe_malloc(len+1);
+  r->len = len+1;
+  *(r->txt) = len;
+  memcpy((r->txt)+1, txt, len);
+}
 
 struct daemon *read_opts (int argc, char **argv)
 {
@@ -177,7 +193,7 @@ struct daemon *read_opts (int argc, char **argv)
   char *problem = NULL, *buff = safe_malloc(MAXDNAME);
   int option = 0, i;
   FILE *file_save = NULL, *f = NULL;
-  char *comma, *file_name_save = NULL, *conffile = CONFFILE;
+  char *p, *comma, *file_name_save = NULL, *conffile = CONFFILE;
   int hosts_index = 1, conffile_set = 0;
   int line_save = 0, lineno = 0;
   opterr = 0;
@@ -197,17 +213,27 @@ struct daemon *read_opts (int argc, char **argv)
   daemon->runfile =  RUNFILE;
   daemon->dhcp_max = MAXLEASES;
   daemon->edns_pktsz = EDNS_PKTSZ;
+  add_txt(daemon, "version.bind", "dnsmasq-" VERSION );
+  add_txt(daemon, "authors.bind", "Simon Kelley");
+  add_txt(daemon, "copyright.bind", COPYRIGHT);
+
   
   while (1)
     {
       problem = NULL;
 
       if (!f)
+       {
 #ifdef HAVE_GETOPT_LONG
-       option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
+         option = getopt_long(argc, argv, OPTSTRING, (struct option *)opts, NULL);
 #else
-        option = getopt(argc, argv, OPTSTRING);
+         option = getopt(argc, argv, OPTSTRING);
 #endif
+         if (optarg)
+           for (p = optarg; *p; p++)
+             if (*p == ',')
+               *p = '\001';
+       }
       else
        { /* f non-NULL, reading from conffile. */
        reread:
@@ -228,18 +254,43 @@ struct daemon *read_opts (int argc, char **argv)
            }
          else
            {
-             char *p;
              int white;
              lineno++;
-             /* dump comments */
+             
+             /* Implement quotes, inside quotes we allow \\ \" \n and \t 
+                unquoted commas get changed to \001 also strip comments */
+             
              for (white = 1, p = buff; *p; p++)
-               if (white && *p == '#')
-                 { 
-                   *p = 0;
-                   break;
-                 }
-               else
-                 white = isspace(*p);
+               {
+                 if (*p == '"')
+                   {
+                     memmove(p, p+1, strlen(p+1)+1);
+                     for(; *p && *p != '"'; p++)
+                       if (*p == '\\' && 
+                           (p[1] == '\\' || p[1] == '"' || p[1] == 'n' || p[1] == 't'))
+                         {
+                           if (p[1] == 't')
+                             p[1] = '\t';
+                           else if (p[1] == 'n')
+                             p[1] = '\n';
+                           memmove(p, p+1, strlen(p+1)+1);
+                         }
+                     if (*p == '"') 
+                       memmove(p, p+1, strlen(p+1)+1);
+                     else
+                       complain("missing \"", lineno, conffile);
+                   }
+
+                 if (white && *p == '#')
+                   { 
+                     *p = 0;
+                     break;
+                   }
+                 white = isspace(*p); 
+                 if (*p == ',')
+                   *p = '\001';
+
+               }
              /* fgets gets end of line char too. */
              while (strlen(buff) > 0 && isspace(buff[strlen(buff)-1]))
                buff[strlen(buff)-1] = 0;
@@ -259,8 +310,7 @@ struct daemon *read_opts (int argc, char **argv)
                  option = opts[i].val;
              if (!option)
                {
-                 sprintf(buff, "bad option at line %d of %s ", lineno, conffile);
-                 complain(buff, NULL);
+                 complain("bad option", lineno, conffile);
                  continue;
                }
            }
@@ -283,16 +333,16 @@ struct daemon *read_opts (int argc, char **argv)
      
       if (!f && option == 'w')
        {
-         fprintf (stderr, usage,  CACHESIZ, EDNS_PKTSZ, MAXLEASES);
+         printf (usage,  CACHESIZ, EDNS_PKTSZ, MAXLEASES);
          exit(0);
        }
 
       if (!f && option == 'v')
         {
-          fprintf(stderr, "Dnsmasq version %s  %s\n\n", VERSION, COPYRIGHT);
-         fprintf(stderr, "This software comes with ABSOLUTELY NO WARRANTY.\n");
-         fprintf(stderr, "Dnsmasq is free software, and you are welcome to redistribute it\n");
-         fprintf(stderr, "under the terms of the GNU General Public License, version 2.\n");
+          printf("Dnsmasq version %s  %s\n\n", VERSION, COPYRIGHT);
+         printf("This software comes with ABSOLUTELY NO WARRANTY.\n");
+         printf("Dnsmasq is free software, and you are welcome to redistribute it\n");
+         printf("under the terms of the GNU General Public License, version 2.\n");
           exit(0);
         }
       
@@ -302,10 +352,7 @@ struct daemon *read_opts (int argc, char **argv)
            daemon->options |= optmap[i].flag;
            option = 0;
            if (f && optarg)
-             {
-               sprintf(buff, "extraneous parameter at line %d of %s ", lineno, conffile);
-               complain(buff, NULL);
-             }
+             complain("extraneous parameter", lineno, conffile);
            break;
          }
       
@@ -313,8 +360,7 @@ struct daemon *read_opts (int argc, char **argv)
        {
          if (f && !optarg)
            {
-             sprintf(buff, "missing parameter at line %d of %s ", lineno, conffile);
-             complain(buff, NULL);
+             complain("missing parameter", lineno, conffile);
              continue;
            }
                  
@@ -331,8 +377,7 @@ struct daemon *read_opts (int argc, char **argv)
               /* nest conffiles one deep */
               if (file_save)
                 {
-                  sprintf(buff, "nested includes not allowed at line %d of %s ", lineno, conffile);
-                  complain(buff, NULL);
+                  complain("nested includes not allowed", lineno, conffile);
                   continue;
                 }
               file_name_save = conffile;
@@ -378,13 +423,13 @@ struct daemon *read_opts (int argc, char **argv)
            case 'm':
              {
                int pref = 1;
-               struct mx_record *new;
+               struct mx_srv_record *new;
 
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  {
                    char *prefstr;
                    *(comma++) = 0;
-                   if ((prefstr=strchr(comma, ',')))
+                   if ((prefstr=strchr(comma, '\001')))
                      {
                        *(prefstr++) = 0;
                        if (!atoi_check(prefstr, &pref))
@@ -403,12 +448,13 @@ struct daemon *read_opts (int argc, char **argv)
                    break;
                  }
 
-               new = safe_malloc(sizeof(struct mx_record));
+               new = safe_malloc(sizeof(struct mx_srv_record));
                new->next = daemon->mxnames;
                daemon->mxnames = new;
-               new->mxname = safe_string_alloc(optarg);
-               new->mxtarget = safe_string_alloc(comma); /* may be NULL */
-               new->preference = pref;
+               new->issrv = 0;
+               new->name = safe_string_alloc(optarg);
+               new->target = safe_string_alloc(comma); /* may be NULL */
+               new->weight = pref;
                break;
              }
 
@@ -456,7 +502,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'i':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_names;
                daemon->if_names = new;
@@ -473,7 +519,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'I':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_except;
                daemon->if_except = new;
@@ -502,7 +548,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'a':
              do {
                struct iname *new = safe_malloc(sizeof(struct iname));
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *comma++ = 0;
                new->next = daemon->if_addrs;
 #ifdef HAVE_IPV6
@@ -769,7 +815,7 @@ struct daemon *read_opts (int argc, char **argv)
                new->broadcast.s_addr = 0;
                new->router.s_addr = 0;
                new->netid.net = NULL;
-               new->static_only = new->filter_netid = 0;
+               new->flags = 0;
                
                problem = "bad dhcp-range";
 
@@ -777,14 +823,14 @@ struct daemon *read_opts (int argc, char **argv)
                  if (!(*cp == ' ' || *cp == '.' ||  (*cp >='0' && *cp <= '9')))
                    break;
 
-               if (*cp != ',' && (comma = strchr(optarg, ',')))
+               if (*cp != '\001' && (comma = strchr(optarg, '\001')))
                  {
                    *comma = 0;
                    if (strstr(optarg, "net:") == optarg)
                      {
                        new->netid.net = safe_string_alloc(optarg+4);
                        new->netid.next = NULL;
-                       new->filter_netid = 1;
+                       new->flags |= CONTEXT_FILTER;
                      }
                    else
                      new->netid.net = safe_string_alloc(optarg);
@@ -796,7 +842,7 @@ struct daemon *read_opts (int argc, char **argv)
                
                for (k = 1; k < 5; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -806,7 +852,7 @@ struct daemon *read_opts (int argc, char **argv)
                else if (strcmp(a[1], "static") == 0)
                  {
                    new->end = new->start;
-                   new->static_only = 1;
+                   new->flags |= CONTEXT_STATIC;
                  }
                else if ((new->end.s_addr = inet_addr(a[1])) == (in_addr_t)-1)
                  option = '?';
@@ -821,6 +867,7 @@ struct daemon *read_opts (int argc, char **argv)
                if (option != '?' && k >= 3 && strchr(a[2], '.') &&  
                    ((new->netmask.s_addr = inet_addr(a[2])) != (in_addr_t)-1))
                  {
+                   new->flags |= CONTEXT_NETMASK;
                    leasepos = 3;
                    if (!is_same_net(new->start, new->end, new->netmask))
                      {
@@ -839,7 +886,10 @@ struct daemon *read_opts (int argc, char **argv)
 
                if (k >= 4 && strchr(a[3], '.') &&  
                    ((new->broadcast.s_addr = inet_addr(a[3])) != (in_addr_t)-1))
-                 leasepos = 4;
+                 {
+                   new->flags |= CONTEXT_BRDCAST;
+                   leasepos = 4;
+                 }
 
                if (k >= leasepos+1)
                  {
@@ -866,6 +916,10 @@ struct daemon *read_opts (int argc, char **argv)
                              }
                            
                            new->lease_time = atoi(a[leasepos]) * fac;
+                           /* Leases of a minute or less confuse
+                              some clients, notably Apple's */
+                           if (new->lease_time < 120)
+                             new->lease_time = 120;
                          }
                      }
                  }
@@ -879,7 +933,6 @@ struct daemon *read_opts (int argc, char **argv)
              {
                int j, k;
                char *a[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
-               unsigned int e0, e1, e2, e3, e4, e5;
                struct dhcp_config *new = safe_malloc(sizeof(struct dhcp_config));
                struct in_addr in;
 
@@ -890,7 +943,7 @@ struct daemon *read_opts (int argc, char **argv)
                a[0] = optarg;
                for (k = 1; k < 6; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -899,6 +952,7 @@ struct daemon *read_opts (int argc, char **argv)
                  if (strchr(a[j], ':')) /* ethernet address, netid or binary CLID */
                    {
                      char *arg = a[j];
+                     
                      if ((arg[0] == 'i' || arg[0] == 'I') &&
                          (arg[1] == 'd' || arg[1] == 'D') &&
                          arg[2] == ':')
@@ -910,32 +964,9 @@ struct daemon *read_opts (int argc, char **argv)
                              int len;
                              arg += 3; /* dump id: */
                              if (strchr(arg, ':'))
-                               {
-                                 /* decode hex in place */
-                                 char *p = arg, *q = arg, *r;
-                                 while (*p)
-                                   {
-                                     for (r = p; *r && *r != ':'; r++);
-                                     if (*r)
-                                       {
-                                         if (r != p)
-                                           {
-                                             *r = 0;
-                                             *(q++) = strtol(p, NULL, 16);
-                                           }
-                                         p = r+1;
-                                       }
-                                     else
-                                       {
-                                         if (*p)
-                                           *(q++) = strtol(p, NULL, 16);
-                                         break;
-                                       }
-                                   }
-                                 len = q - arg;
-                               }
+                               len = parse_hex(arg, arg, -1, NULL);
                              else
-                               len = strlen(arg);
+                               len = (int) strlen(arg);
                              
                              new->flags |= CONFIG_CLID;
                              new->clid_len = len;
@@ -948,17 +979,8 @@ struct daemon *read_opts (int argc, char **argv)
                          new->flags |= CONFIG_NETID;
                          new->netid.net = safe_string_alloc(arg+4);
                        }
-                     else if (sscanf(a[j], "%x:%x:%x:%x:%x:%x",
-                                     &e0, &e1, &e2, &e3, &e4, &e5) == 6)
-                       {
+                     else if (parse_hex(a[j],  new->hwaddr, 6, &new->wildcard_mask) == 6)
                          new->flags |= CONFIG_HWADDR;
-                         new->hwaddr[0] = e0;
-                         new->hwaddr[1] = e1;
-                         new->hwaddr[2] = e2;
-                         new->hwaddr[3] = e3;
-                         new->hwaddr[4] = e4;
-                         new->hwaddr[5] = e5;
-                       }
                      else
                        option = '?';
                    }
@@ -1016,6 +1038,10 @@ struct daemon *read_opts (int argc, char **argv)
                      else
                        {
                          new->lease_time = atoi(a[j]) * fac; 
+                         /* Leases of a minute or less confuse
+                            some clients, notably Apple's */
+                         if (new->lease_time < 120)
+                           new->lease_time = 120;
                          new->flags |= CONFIG_TIME;
                        }
                    }
@@ -1052,7 +1078,7 @@ struct daemon *read_opts (int argc, char **argv)
                new->netid = NULL;
                new->val = NULL;
                                
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  {
                    struct dhcp_netid *np = NULL;
                    *comma++ = 0;
@@ -1069,7 +1095,7 @@ struct daemon *read_opts (int argc, char **argv)
                      new->netid->next = np;
                      np = new->netid;
                      optarg = comma;
-                     if ((comma = strchr(optarg, ',')))
+                     if ((comma = strchr(optarg, '\001')))
                        *comma++ = 0;
                    } while (optarg);
                  }
@@ -1084,10 +1110,10 @@ struct daemon *read_opts (int argc, char **argv)
                    /* dns search, RFC 3397 */
                    unsigned char *q, *r, *tail;
                    unsigned char *p = NULL;
-                   int newlen, len = 0;
+                   size_t newlen, len = 0;
                    
                    optarg = comma;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *(comma++) = 0;
 
                    while (optarg && *optarg)
@@ -1099,10 +1125,8 @@ struct daemon *read_opts (int argc, char **argv)
                            break;
                          }
                        
-                       if (!(r = realloc(p, len + strlen(optarg) + 2)))
+                       if (!(p = realloc(p, len + strlen(optarg) + 2)))
                          die("could not get memory", NULL);
-                       p = memmove(r, p, len);
-                       
                        q = p + len;
                        
                        /* add string on the end in RFC1035 format */
@@ -1121,7 +1145,7 @@ struct daemon *read_opts (int argc, char **argv)
                        /* Now tail-compress using earlier names. */
                        newlen = q - p;
                        for (tail = p + len; *tail; tail += (*tail) + 1)
-                         for (r = p; r - p < len; r += (*r) + 1)
+                         for (r = p; r - p < (int)len; r += (*r) + 1)
                            if (strcmp(r, tail) == 0)
                              {
                                PUTSHORT((r - p) | 0xc000, tail); 
@@ -1132,11 +1156,11 @@ struct daemon *read_opts (int argc, char **argv)
                        len = newlen;
                    
                        optarg = comma;
-                       if (optarg && (comma = strchr(optarg, ',')))
+                       if (optarg && (comma = strchr(optarg, '\001')))
                          *(comma++) = 0;
                      }
 
-                   new->len = len;
+                   new->len = (int) len;
                    new->val = p;
                  }
                else if (comma)
@@ -1146,7 +1170,7 @@ struct daemon *read_opts (int argc, char **argv)
                    is_addr = is_hex = is_dec = 1;
                    addrs = digs = 1;
                    for (cp = comma; *cp; cp++)
-                     if (*cp == ',')
+                     if (*cp == '\001')
                        {
                          addrs++;
                          is_dec = is_hex = 0;
@@ -1168,28 +1192,9 @@ struct daemon *read_opts (int argc, char **argv)
                
                    if (is_hex && digs > 1)
                      {
-                       char *p = comma, *q, *r;
                        new->len = digs;
-                       q = new->val = safe_malloc(new->len);
-                       while (*p)
-                         {
-                           for (r = p; *r && *r != ':'; r++);
-                           if (*r)
-                             {
-                               if (r != p)
-                                 {
-                                   *r = 0;
-                                   *(q++) = strtol(p, NULL, 16);
-                                 }
-                               p = r+1;
-                             }
-                           else
-                             {
-                               if (*p)
-                                 *(q++) = strtol(p, NULL, 16);
-                               break;
-                             }
-                         }
+                       new->val = safe_malloc(new->len);
+                       parse_hex(comma, new->val, digs, NULL);
                      }
                    else if (is_dec)
                      {
@@ -1222,7 +1227,7 @@ struct daemon *read_opts (int argc, char **argv)
                        while (addrs--) 
                          {
                            cp = comma;
-                           if ((comma = strchr(cp, ',')))
+                           if ((comma = strchr(cp, '\001')))
                              *comma++ = 0;
                            in.s_addr = inet_addr(cp);
                            memcpy(op, &in, INADDRSZ);
@@ -1238,7 +1243,7 @@ struct daemon *read_opts (int argc, char **argv)
                      }
                  }
 
-               if (new->len > 256)
+               if (new->len > 255)
                  {
                    option = '?';
                    problem = "dhcp-option too long";
@@ -1266,7 +1271,7 @@ struct daemon *read_opts (int argc, char **argv)
                    struct dhcp_netid *newid = safe_malloc(sizeof(struct dhcp_netid));
                    newid->next = id;
                    id = newid;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *comma++ = 0;
                    newid->net = safe_string_alloc(optarg+4);
                    optarg = comma;
@@ -1278,14 +1283,14 @@ struct daemon *read_opts (int argc, char **argv)
                  {
                    char *dhcp_file, *dhcp_sname = NULL;
                    struct in_addr dhcp_next_server;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *comma++ = 0;
                    dhcp_file = safe_string_alloc(optarg);
                    dhcp_next_server.s_addr = 0;
                    if (comma)
                      {
                        optarg = comma;
-                       if ((comma = strchr(optarg, ',')))
+                       if ((comma = strchr(optarg, '\001')))
                          *comma++ = 0;
                        dhcp_sname = safe_string_alloc(optarg);
                        if (comma && (dhcp_next_server.s_addr = inet_addr(comma)) == (in_addr_t)-1)
@@ -1318,7 +1323,7 @@ struct daemon *read_opts (int argc, char **argv)
            case 'U':
            case 'j':
              {
-               if (!(comma = strchr(optarg, ',')))
+               if (!(comma = strchr(optarg, '\001')))
                  option = '?';
                else
                  {
@@ -1343,7 +1348,7 @@ struct daemon *read_opts (int argc, char **argv)
                daemon->dhcp_ignore = new;
                do {
                  struct dhcp_netid *member = safe_malloc(sizeof(struct dhcp_netid));
-                 if ((comma = strchr(optarg, ',')))
+                 if ((comma = strchr(optarg, '\001')))
                    *comma++ = 0;
                  member->next = list;
                  list = member;
@@ -1367,7 +1372,7 @@ struct daemon *read_opts (int argc, char **argv)
                a[0] = optarg;
                for (k = 1; k < 4; k++)
                  {
-                   if (!(a[k] = strchr(a[k-1], ',')))
+                   if (!(a[k] = strchr(a[k-1], '\001')))
                      break;
                    *(a[k]++) = 0;
                  }
@@ -1393,13 +1398,80 @@ struct daemon *read_opts (int argc, char **argv)
                break;
              }
 
+           case 'Y':
+             {
+               struct txt_record *new;
+               unsigned char *p, *q;
+
+               if ((comma = strchr(optarg, '\001')))
+                 *(comma) = 0;
+
+               if (!canonicalise(optarg))
+                 {
+                   option = '?';
+                   problem = "bad TXT record";
+                   break;
+                 }
+                                 
+               if ((q = comma))
+                 while (1)
+                   {
+                     size_t len;
+                     if ((p = strchr(q+1, '\001')))
+                       {
+                         if ((len = p - q - 1) > 255)
+                           { 
+                             option = '?';
+                             break;
+                           }
+                         *q = len;
+                         q = p;
+                       }
+                     else
+                       {
+                         if ((len = strlen(q+1)) > 255)
+                           option = '?';
+                         *q = len;
+                         break;
+                       }
+                   }
+               
+               if (option == '?')
+                 {
+                   problem = "TXT record string too long";
+                   break;
+                 }
+
+               new = safe_malloc(sizeof(struct txt_record));
+               new->next = daemon->txt;
+               daemon->txt = new;
+               new->class = C_IN;
+               if (comma)
+                 {
+                   new->len = q - ((unsigned char *)comma) + *q + 1;
+                   new->txt = safe_malloc(new->len);
+                   memcpy(new->txt, comma, new->len);
+                 }
+               else
+                 {
+                   static char empty[] = "";
+                   new->len = 1;
+                   new->txt = empty;
+                 }
+               
+               if (comma)
+                 *comma = 0;
+               new->name = safe_string_alloc(optarg);
+               break;
+             }
+
            case 'W':
              {
                int port = 1, priority = 0, weight = 0;
                char *name, *target = NULL;
-               struct srv_record *new;
+               struct mx_srv_record *new;
                
-               if ((comma = strchr(optarg, ',')))
+               if ((comma = strchr(optarg, '\001')))
                  *(comma++) = 0;
 
                if (!canonicalise(optarg))
@@ -1413,7 +1485,7 @@ struct daemon *read_opts (int argc, char **argv)
                if (comma)
                  {
                    optarg = comma;
-                   if ((comma = strchr(optarg, ',')))
+                   if ((comma = strchr(optarg, '\001')))
                      *(comma++) = 0;
                    if (!canonicalise(optarg))
                      {
@@ -1425,7 +1497,7 @@ struct daemon *read_opts (int argc, char **argv)
                    if (comma)
                      {
                        optarg = comma;
-                       if ((comma = strchr(optarg, ',')))
+                       if ((comma = strchr(optarg, '\001')))
                          *(comma++) = 0;
                        if (!atoi_check(optarg, &port))
                          {
@@ -1436,7 +1508,7 @@ struct daemon *read_opts (int argc, char **argv)
                        if (comma)
                          {
                            optarg = comma;
-                           if ((comma = strchr(optarg, ',')))
+                           if ((comma = strchr(optarg, '\001')))
                              *(comma++) = 0;
                            if (!atoi_check(optarg, &priority))
                              {
@@ -1447,7 +1519,7 @@ struct daemon *read_opts (int argc, char **argv)
                            if (comma)
                              {
                                optarg = comma;
-                               if ((comma = strchr(optarg, ',')))
+                               if ((comma = strchr(optarg, '\001')))
                                  *(comma++) = 0;
                                if (!atoi_check(optarg, &weight))
                                  {
@@ -1460,11 +1532,12 @@ struct daemon *read_opts (int argc, char **argv)
                      }
                  }
                
-               new = safe_malloc(sizeof(struct srv_record));
-               new->next = daemon->srvnames;
-               daemon->srvnames = new;
-               new->srvname = name;
-               new->srvtarget = target;
+               new = safe_malloc(sizeof(struct mx_srv_record));
+               new->next = daemon->mxnames;
+               daemon->mxnames = new;
+               new->issrv = 1;
+               new->name = name;
+               new->target = target;
                new->srvport = port;
                new->priority = priority;
                new->weight = weight;
@@ -1476,11 +1549,7 @@ struct daemon *read_opts (int argc, char **argv)
       if (option == '?')
        {
          if (f)
-           {
-             sprintf(buff, "%s at line %d of %s ", 
-                     problem ? problem : "error", lineno, conffile);
-             complain(buff, NULL);
-           }
+           complain( problem ? problem : "error", lineno, conffile);
          else
 #ifdef HAVE_GETOPT_LONG
            die("bad command line options: %s.", problem ? problem : "try --help");
@@ -1521,34 +1590,47 @@ struct daemon *read_opts (int argc, char **argv)
   /* only one of these need be specified: the other defaults to the host-name */
   if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
     {
+      struct mx_srv_record *mx;
+      
       if (gethostname(buff, MAXDNAME) == -1)
        die("cannot get host-name: %s", NULL);
       
-      if (!daemon->mxnames)
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+       if (!mx->issrv && hostname_isequal(mx->name, buff))
+         break;
+      
+      if ((daemon->mxtarget || (daemon->options & OPT_LOCALMX)) && !mx)
        {
-         daemon->mxnames = safe_malloc(sizeof(struct mx_record));
-         daemon->mxnames->next = NULL;
-         daemon->mxnames->mxtarget = NULL;
-         daemon->mxnames->mxname = safe_string_alloc(buff);
+         daemon->mxnames = safe_malloc(sizeof(struct mx_srv_record));
+         daemon->mxnames->next = daemon->mxnames;
+         daemon->mxnames->issrv = 0;
+         daemon->mxnames->target = NULL;
+         daemon->mxnames->name = safe_string_alloc(buff);
        }
       
       if (!daemon->mxtarget)
        daemon->mxtarget = safe_string_alloc(buff);
+
+      for (mx = daemon->mxnames; mx; mx = mx->next)
+       if (!mx->issrv && !mx->target)
+         mx->target = daemon->mxtarget;
     }
 
   if (daemon->domain_suffix)
     {
        /* add domain for any srv record without one. */
-      struct srv_record *srv;
+      struct mx_srv_record *srv;
       
-      for (srv = daemon->srvnames; srv; srv = srv->next)
-       if (strchr(srv->srvname, '.') && strchr(srv->srvname, '.') == strrchr(srv->srvname, '.'))
+      for (srv = daemon->mxnames; srv; srv = srv->next)
+       if (srv->issrv &&
+           strchr(srv->name, '.') && 
+           strchr(srv->name, '.') == strrchr(srv->name, '.'))
          {
-           strcpy(buff, srv->srvname);
+           strcpy(buff, srv->name);
            strcat(buff, ".");
            strcat(buff, daemon->domain_suffix);
-           free(srv->srvname);
-           srv->srvname = safe_string_alloc(buff);
+           free(srv->name);
+           srv->name = safe_string_alloc(buff);
          }
     }
   
index f1a0a4c4152267dc72c31dd064bf7e7fe42b88a8..ec29018f3836e047fd55e798fc64e0c6a0222eae 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000 Simon Kelley
+/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -333,21 +333,48 @@ static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *heade
   return ansp;
 }
 
-/* CRC all the bytes of the question section. This is used to
-   safely detect query retransmision. */
-unsigned int questions_crc(HEADER *header, unsigned int plen)
+/* CRC the question section. This is used to safely detect query 
+   retransmision and to detect answers to questions we didn't ask, which 
+   might be poisoning attacks. Note that we decode the name rather 
+   than CRC the raw bytes, since replies might be compressed differently. 
+   We ignore case in the names for the same reason. */
+unsigned int questions_crc(HEADER *header, unsigned int plen, char *name)
 {
-  unsigned char *start, *end = skip_questions(header, plen);
-  unsigned int crc = 0xffffffff;
+  unsigned int q, crc = 0xffffffff;
+  unsigned char *p1, *p = (unsigned char *)(header+1);
+
+  for (q = 0; q < ntohs(header->qdcount); q++) 
+    {
+      if (!extract_name(header, plen, &p, name, 1))
+       return crc; /* bad packet */
+      
+      for (p1 = name; *p1; p1++)
+       {
+         int i = 8;
+         char c = *p1;
+
+         if (c >= 'A' && c <= 'Z')
+           c += 'a' - 'A';
+
+         crc ^= c << 24;
+         while (i--)
+           crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
+       }
+      
+      /* CRC the class and type as well */
+      for (p1 = p; p1 < p+4; p1++)
+       {
+         int i = 8;
+         crc ^= *p1 << 24;
+         while (i--)
+           crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
+       }
+
+      p += 4;
+      if ((unsigned int)(p - (unsigned char *)header) > plen)
+       return crc; /* bad packet */
+    }
 
-  if (end)
-    for (start = (unsigned char *)(header+1); start < end; start++)
-      {
-       int i = 8;
-       crc ^= *start << 24;
-       while (i--)
-         crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
-      }
   return crc;
 }
 
@@ -437,14 +464,12 @@ static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *ad
        addr->s_addr &= ~doctor->mask.s_addr;
        addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
        /* Since we munged the data, the server it came from is no longer authoritative */
-       header->nscount = htons(0);
-       header->arcount = htons(0);
        header->aa = 0;
        break;
       }
 }
 
-static int find_soa(HEADER *header, unsigned int qlen)
+static int find_soa(HEADER *header, struct doctor *doctor, unsigned int qlen)
 {
   unsigned char *p;
   int qtype, qclass, rdlen;
@@ -491,6 +516,26 @@ static int find_soa(HEADER *header, unsigned int qlen)
        return 0; /* bad packet */
     }
  
+  if (doctor)
+    for (i=0; i<ntohs(header->arcount); i++)
+      {
+       if (!(p = skip_name(p, header, qlen)))
+         return 0; /* bad packet */
+      
+       GETSHORT(qtype, p); 
+       GETSHORT(qclass, p);
+       GETLONG(ttl, p);
+       GETSHORT(rdlen, p);
+       
+       if ((qclass == C_IN) && (qtype == T_A))
+         dns_doctor(header, doctor, (struct in_addr *)p);
+       
+       p += rdlen;
+       
+       if ((unsigned int)(p - (unsigned char *)header) > qlen)
+         return 0; /* bad packet */
+      }
   return found_soa ? minttl : 0;
 }
 
@@ -501,8 +546,16 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
 {
   unsigned char *p, *p1, *endrr;
   int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
-   
+  unsigned long ttl = 0;
+
   cache_start_insert();
+
+  /* find_soa is needed for dns_doctor side-effects, so don't call it lazily if there are any. */
+  if (daemon->doctors)
+    {
+      searched_soa = 1;
+      ttl = find_soa(header, daemon->doctors, qlen);
+    }
   
   /* go through the questions. */
   p = (unsigned char *)(header+1);
@@ -512,7 +565,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
       int found = 0, cname_count = 5;
       struct crec *cpp = NULL;
       int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
-      unsigned long cttl = ULONG_MAX, attl, ttl = 0;
+      unsigned long cttl = ULONG_MAX, attl;
       
       if (!extract_name(header, qlen, &p, name, 1))
        return; /* bad packet */
@@ -581,7 +634,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, qlen);
+                 ttl = find_soa(header, NULL, qlen);
                }
              if (ttl)
                cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); 
@@ -665,7 +718,7 @@ void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now
              if (!searched_soa)
                {
                  searched_soa = 1;
-                 ttl = find_soa(header, qlen);
+                 ttl = find_soa(header, NULL, qlen);
                }
              /* If there's no SOA to get the TTL from, but there is a CNAME 
                 pointing at this, inherit it's TTL */
@@ -705,11 +758,11 @@ unsigned short extract_request(HEADER *header,unsigned int qlen, char *name, uns
   GETSHORT(qtype, p); 
   GETSHORT(qclass, p);
 
+  if (typep)
+    *typep = qtype;
+
   if (qclass == C_IN)
     {
-      if (typep)
-       *typep = qtype;
-
       if (qtype == T_A)
        return F_IPV4;
       if (qtype == T_AAAA)
@@ -766,21 +819,21 @@ int setup_reply(HEADER *header, unsigned int qlen,
 int check_for_local_domain(char *name, time_t now, struct daemon *daemon)
 {
   struct crec *crecp;
-  struct mx_record *mx;
-  struct srv_record *srv;
-  
+  struct mx_srv_record *mx;
+  struct txt_record *txt;
+    
   if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
       (crecp->flags & (F_HOSTS | F_DHCP)))
     return 1;
   
   for (mx = daemon->mxnames; mx; mx = mx->next)
-    if (hostname_isequal(name, mx->mxname))
+    if (hostname_isequal(name, mx->name))
       return 1;
 
-  for (srv = daemon->srvnames; srv; srv = srv->next)
-    if (hostname_isequal(name, srv->srvname))
+  for (txt = daemon->txt; txt; txt = txt->next)
+    if (hostname_isequal(name, txt->name))
       return 1;
-
+  
   return 0;
 }
 
@@ -880,6 +933,8 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
        
       case 'd':
        /* get domain-name answer arg and store it in RDATA field */
+       if (offset)
+         *offset = p - (unsigned char *)header;
        sval = va_arg(ap, char *);
        while (sval && *sval)
          {
@@ -894,11 +949,10 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
        break;
 
       case 't':
+       usval = va_arg(ap, int);
        sval = va_arg(ap, char *);
-       j = strlen(sval);
-       *p++ = j;
-       memcpy(p, sval, j);
-       p += j;
+       memcpy(p, sval, usval);
+       p += usval;
        break;
       }
 
@@ -907,9 +961,6 @@ static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigne
   j = p - sav - 2;
   PUTSHORT(j, sav);     /* Now, store real RDLength */
   
-  if (offset)
-    *offset = sav - (unsigned char *)header;
-  
   /* check for overflow of buffer */
   if (limit && ((unsigned char *)limit - p) < 0)
     {
@@ -933,11 +984,12 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
   unsigned int nameoffset;
   unsigned short flag;
   int qdcount = ntohs(header->qdcount); 
-  int q, ans, anscount = 0;
+  int q, ans, anscount = 0, addncount = 0;
   int dryrun = 0, sec_reqd = 0;
   struct crec *crecp;
   int nxdomain = 0, auth = 1, trunc = 0;
-
+  struct mx_srv_record *rec;
   if (!qdcount || header->opcode != QUERY )
     return 0;
 
@@ -968,6 +1020,9 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
       dryrun = 1;
     }
 
+  for (rec = daemon->mxnames; rec; rec = rec->next)
+    rec->offset = 0;
+  
  rerun:
   /* determine end of question section (we put answers there) */
   if (!(ansp = skip_questions(header, qlen)))
@@ -994,27 +1049,24 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
 
       ans = 0; /* have we answered this question */
       
-      if (qclass == C_CHAOS && qtype == T_TXT)
-       /* special query to get version. */
+      if (qtype == T_TXT || qtype == T_ANY)
        {
-         ans = 1;
-         if (!dryrun)
+         struct txt_record *t;
+         for(t = daemon->txt; t ; t = t->next)
            {
-             if (hostname_isequal(name, "version.bind"))
-               sprintf(name, "dnsmasq-%s", VERSION);
-             else if (hostname_isequal(name, "authors.bind"))
-               sprintf(name, "Simon Kelley");
-             else if (hostname_isequal(name, "copyright.bind"))
-               sprintf(name, COPYRIGHT);
-             else
-               *name = 0;
-             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL,
-                                      T_TXT, C_CHAOS, "t", name))
-               anscount++;
+             if (t->class == qclass && hostname_isequal(name, t->name))
+               {
+                 ans = 1;
+                 log_query(F_CNAME | F_FORWARD | F_CONFIG | F_NXDOMAIN, name, NULL, 0, NULL, 0);
+                 if (!dryrun &&
+                     add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL,
+                                         T_TXT, t->class, "t", t->len, t->txt))
+                   anscount++;
+               }
            }
-         }
+       }
 
-      else if (qclass == C_IN)
+      if (qclass == C_IN)
        {
          if (qtype == T_PTR || qtype == T_ANY)
            {
@@ -1172,18 +1224,21 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
          if (qtype == T_MX || qtype == T_ANY)
            {
              int found = 0;
-             struct mx_record *mx;
-             for (mx = daemon->mxnames; mx; mx = mx->next)
-               if (hostname_isequal(name, mx->mxname))
+             for (rec = daemon->mxnames; rec; rec = rec->next)
+               if (!rec->issrv && hostname_isequal(name, rec->name))
                  {
                  ans = found = 1;
                  if (!dryrun)
                    {
+                     int offset;
                      log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
-                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, 
-                                             T_MX, C_IN, "sd", mx->preference, 
-                                             mx->mxtarget ? mx->mxtarget : daemon->mxtarget))
-                       anscount++;
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl,
+                                             &offset, T_MX, C_IN, "sd", rec->weight, rec->target))
+                       {
+                         anscount++;
+                         if (rec->target)
+                           rec->offset = offset;
+                       }
                    }
                  }
              
@@ -1205,19 +1260,23 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
          if (qtype == T_SRV || qtype == T_ANY)
            {
              int found = 0;
-             struct srv_record *srv;
              
-             for (srv = daemon->srvnames; srv; srv = srv->next)
-               if (hostname_isequal(name, srv->srvname))
+             for (rec = daemon->mxnames; rec; rec = rec->next)
+               if (rec->issrv && hostname_isequal(name, rec->name))
                  {
                    found = ans = 1;
                    if (!dryrun)
                      {
+                       int offset;
                        log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV6, name, NULL, 0, NULL, 0);
                        if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, 
-                                               NULL, T_SRV, C_IN, "sssd", 
-                                               srv->priority, srv->weight, srv->srvport, srv->srvtarget))
-                         anscount++;
+                                               &offset, T_SRV, C_IN, "sssd", 
+                                               rec->priority, rec->weight, rec->srvport, rec->target))
+                         {
+                           anscount++;
+                           if (rec->target)
+                             rec->offset = offset;
+                         }
                      }
                  }
              
@@ -1250,6 +1309,39 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
       goto rerun;
     }
   
+  /* create an additional data section, for stuff in SRV and MX record replies. */
+  for (rec = daemon->mxnames; rec; rec = rec->next)
+    if (rec->offset != 0)
+      {
+       /* squash dupes */
+       struct mx_srv_record *tmp;
+       for (tmp = rec->next; tmp; tmp = tmp->next)
+         if (tmp->offset != 0 && hostname_isequal(rec->target, tmp->target))
+           tmp->offset = 0;
+       
+       crecp = NULL;
+       while ((crecp = cache_find_by_name(crecp, rec->target, now, F_IPV4 | F_IPV6)))
+         {
+           unsigned long ttl;
+#ifdef HAVE_IPV6
+           int type =  crecp->flags & F_IPV4 ? T_A : T_AAAA;
+#else
+           int type = T_A;
+#endif
+           if (crecp->flags & F_NEG)
+             continue;
+
+           if  (crecp->flags & (F_IMMORTAL | F_DHCP))
+             ttl = daemon->local_ttl;
+           else
+             ttl = crecp->ttd - now;
+           
+           if (add_resource_record(header, limit, NULL, rec->offset, &ansp, ttl, NULL, type, C_IN, 
+                                   crecp->flags & F_IPV4 ? "4" : "6", &crecp->addr))
+             addncount++;
+         }
+      }
+  
   /* done all questions, set up header and return length of result */
   header->qr = 1; /* response */
   header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
@@ -1261,7 +1353,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
     header->rcode = NOERROR; /* no error */
   header->ancount = htons(anscount);
   header->nscount = htons(0);
-  header->arcount = htons(0);
+  header->arcount = htons(addncount);
   return ansp - (unsigned char *)header;
 }
 
index c0a00abf21a125333c7bd71880dae2ec6f9ffdac..6745cb086714c986fabadd4e36d95219fc9fecab 100644 (file)
@@ -54,6 +54,8 @@
 #define DHCPRELEASE              7
 #define DHCPINFORM               8
 
+#define have_config(config, mask) ((config) && ((config)->flags & (mask))) 
+
 static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val);
 static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dhcp_packet *start);
 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string);
@@ -70,20 +72,14 @@ static unsigned char *do_req_options(struct dhcp_context *context,
                                     unsigned char *req_options, 
                                     struct daemon *daemon,
                                     char *hostname,
-                                    struct in_addr iface_addr,
                                     struct dhcp_netid *netid,
                                     struct in_addr subnet_addr);
 
-static int have_config(struct dhcp_config *config, unsigned int mask)
-{
-  return config && (config->flags & mask);
-}
-
-int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_name, unsigned int sz, time_t now)
+int dhcp_reply(struct daemon *daemon, struct dhcp_context *context, char *iface_name, unsigned int sz, time_t now)
 {
-  struct dhcp_context *context, *context_tmp;
+  struct dhcp_context *context_tmp;
   unsigned char *opt, *clid = NULL;
-  struct dhcp_lease *lease, *ltmp;
+  struct dhcp_lease *ltmp, *lease = NULL;
   struct dhcp_vendor *vendor;
   struct dhcp_netid_list *id_list;
   int clid_len = 0, ignore = 0;
@@ -148,35 +144,46 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          clid_len = option_len(opt);
          clid = option_ptr(opt);
        }
-      else
-       clid =  mess->chaddr;
+      
+      /* do we have a lease in store? */
+      lease = lease_find_by_client(mess->chaddr, clid, clid_len);
+
+      /* If this request is missing a clid, but we've seen one before, 
+        use it again for option matching etc. */
+      if (lease && !clid && lease->clid)
+       {
+         clid_len = lease->clid_len;
+         clid = lease->clid;
+       }
     }
   
-  /* Determine network for this packet. If the machine has an address already, and we don't have
-     have a giaddr or explicit subnet selector, use the ciaddr. This is necessary because a 
-     machine which got a lease via a relay won't use the relay to renew. */
-  addr = 
-    subnet_addr.s_addr ? subnet_addr : 
-    (mess->giaddr.s_addr ? mess->giaddr : 
-     (mess->ciaddr.s_addr ? mess->ciaddr : iface_addr));
-
-  /* More than one context may match, we build a chain of them all on ->current
-     Note that if netmasks, netid or lease times don't match, odd things may happen. */
-    
-  for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
-    if (context_tmp->netmask.s_addr  && 
-       is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
-       is_same_net(addr, context_tmp->end, context_tmp->netmask))
-      {
-       context_tmp->current = context;
-       context = context_tmp;
-      }
+  /* Determine network for this packet. Our caller will have already linked all the 
+     contexts which match the addresses of the receiving interface but if the 
+     machine has an address already, or came via a relay, or we have a subnet selector, 
+     we search again. If we don't have have a giaddr or explicit subnet selector, 
+     use the ciaddr. This is necessary because a  machine which got a lease via a 
+     relay won't use the relay to renew. */
+  if (mess->giaddr.s_addr || subnet_addr.s_addr || mess->ciaddr.s_addr)
+    {
+      addr = 
+       subnet_addr.s_addr ? subnet_addr : 
+       (mess->giaddr.s_addr ? mess->giaddr : mess->ciaddr);
+      
+      for (context = NULL, context_tmp = daemon->dhcp; context_tmp; context_tmp = context_tmp->next)
+       if (context_tmp->netmask.s_addr  && 
+           is_same_net(addr, context_tmp->start, context_tmp->netmask) &&
+           is_same_net(addr, context_tmp->end, context_tmp->netmask))
+         {
+           context_tmp->current = context;
+           context = context_tmp;
+         }
+    }
   
   if (!context)
     {
       syslog(LOG_WARNING, "no address range available for DHCP request %s %s", 
-            subnet_addr.s_addr ? "with subnet selector" : "via",
-            subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
+             subnet_addr.s_addr ? "with subnet selector" : "via",
+             subnet_addr.s_addr ? inet_ntoa(subnet_addr) : (mess->giaddr.s_addr ? inet_ntoa(mess->giaddr) : iface_name));
       return 0;
     }
   
@@ -210,7 +217,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
       if (have_config(config, CONFIG_NAME))
        hostname = config->hostname;
       
-      if (context->netid.net && !context->filter_netid)
+      if (context->netid.net && !(context->flags & CONTEXT_FILTER))
        {
          context->netid.next = netid;
          netid = &context->netid;
@@ -236,9 +243,9 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          message = "disabled";
       
       p = do_req_options(context, p, end, NULL, daemon, 
-                        hostname, iface_addr, netid, subnet_addr);
+                        hostname, netid, subnet_addr);
       /* must do this after do_req_options since it overwrites filename field. */
-      mess->siaddr = iface_addr;
+      mess->siaddr = context->local;
       bootp_option_put(mess, daemon->boot_config, netid);
       p = option_end(p, end, mess);
       log_packet(NULL, logaddr, mess->chaddr, iface_name, message);
@@ -320,14 +327,8 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
   
   /* Can have setting to ignore the client ID for a particular MAC address or hostname */
   if (have_config(config, CONFIG_NOCLID))
-    {
-      clid =  mess->chaddr;
-      clid_len = 0;
-    }
-    
-  /* do we have a lease in store? */
-  lease = lease_find_by_client(clid, clid_len);
-  
+    clid = NULL;
+          
   if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
     {
       req_options = daemon->dhcp_buff2;
@@ -339,7 +340,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
     {
     case DHCPDECLINE:
       if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
-         (iface_addr.s_addr != option_addr(opt).s_addr))
+         (context->local.s_addr != option_addr(opt).s_addr))
        return 0;
 
       /* sanitise any message. Paranoid? Moi? */
@@ -381,7 +382,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
 
     case DHCPRELEASE:
       if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
-         (iface_addr.s_addr != option_addr(opt).s_addr))
+         (context->local.s_addr != option_addr(opt).s_addr))
        return 0;
       
       if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
@@ -414,7 +415,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
        return 0;
       
       context = narrow_context(context, mess->yiaddr);
-      if (context->netid.net && !context->filter_netid)
+      if (context->netid.net && !(context->flags & CONTEXT_FILTER))
        {
          context->netid.next = netid;
          netid = &context->netid;
@@ -430,10 +431,10 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
       else if (lease && lease->expires != 0)
        time = (unsigned int)difftime(lease->expires, now);
 
-      mess->siaddr = iface_addr;
+      mess->siaddr = context->local;
       bootp_option_put(mess, daemon->boot_config, netid);
       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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
       p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
       /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
       if (time != 0xffffffff)
@@ -442,7 +443,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          p = option_put(p, end, OPTION_T2, 4, (time*7)/8);
        }
       p = do_req_options(context, p, end, req_options, daemon, 
-                        NULL, iface_addr, netid, subnet_addr);
+                        NULL, netid, subnet_addr);
       p = option_end(p, end, mess);
       
       log_packet("OFFER" , &mess->yiaddr, mess->chaddr, iface_name, NULL);
@@ -459,7 +460,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)))
            {
              /* SELECTING */
-             if (iface_addr.s_addr != option_addr(opt).s_addr)
+             if (context->local.s_addr != option_addr(opt).s_addr)
                return 0;
              
              /* If a lease exists for this host and another address, squash it. */
@@ -517,12 +518,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
 
          /* Check to see if the address is reserved as a static address for another host */
          else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
-           message ="address reserved";
+           message = "address reserved";
 
          else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
            message = "address in use";
          
-         else if (!lease && !(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
+         else if (!lease && !(lease = lease_allocate(mess->chaddr, clid, clid_len, mess->yiaddr)))
            message = "no leases left";
        }
       
@@ -541,7 +542,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
       
          context = narrow_context(context, mess->yiaddr);
-         if (context->netid.net && !context->filter_netid)
+         if (context->netid.net && !(context->flags & CONTEXT_FILTER))
            {
              context->netid.next = netid;
              netid = &context->netid;
@@ -555,17 +556,17 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
                time = req_time;
            }
          
-         lease_set_hwaddr(lease, mess->chaddr);
+         lease_set_hwaddr(lease, mess->chaddr, clid, clid_len);
          if (!hostname)
            hostname = host_from_dns(daemon, mess->yiaddr);
          if (hostname)
            lease_set_hostname(lease, hostname, daemon->domain_suffix);
          lease_set_expires(lease, time == 0xffffffff ? 0 : now + (time_t)time);
-         
-         mess->siaddr = iface_addr;
+                 
+         mess->siaddr = context->local;
          bootp_option_put(mess, daemon->boot_config, netid);
          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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
          p = option_put(p, end, OPTION_LEASE_TIME, 4, time);
          if (time != 0xffffffff)
            {
@@ -575,7 +576,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
              p = option_put(p, end, OPTION_T2, 4, ((time * 7)/8) - fuzz);
            }
          p = do_req_options(context, p, end, req_options, daemon, 
-                            hostname, iface_addr, netid, subnet_addr);
+                            hostname, netid, subnet_addr);
        }
 
       p = option_end(p, end, mess);
@@ -597,14 +598,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          netid = &context->netid;
        }
        
-      mess->siaddr = iface_addr;
+      mess->siaddr = context->local;
       bootp_option_put(mess, daemon->boot_config, netid);
       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_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->local.s_addr));
       if (!hostname)
        hostname = host_from_dns(daemon, mess->yiaddr);
       p = do_req_options(context, p, end, req_options, daemon, 
-                        hostname, iface_addr, netid, subnet_addr);
+                        hostname, netid, subnet_addr);
       p = option_end(p, end, mess);
       
       log_packet("ACK", &mess->ciaddr, mess->chaddr, iface_name, hostname);
@@ -719,7 +720,7 @@ static unsigned char *option_end(unsigned char *p, unsigned char *end, struct dh
 
 static unsigned char *option_put_string(unsigned char *p, unsigned char *end, int opt, char *string)
 {
-  int len = strlen(string);
+  size_t len = strlen(string);
 
   if (p + len + 3 < end)
     {
@@ -828,7 +829,6 @@ static unsigned char *do_req_options(struct dhcp_context *context,
                                     unsigned char *req_options,
                                     struct daemon *daemon,
                                     char *hostname,
-                                    struct in_addr iface_addr,
                                     struct dhcp_netid *netid,
                                     struct in_addr subnet_addr)
 {
@@ -861,7 +861,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
 
   if (in_list(req_options, OPTION_DNSSERVER) &&
       !option_find2(netid, config_opts, OPTION_DNSSERVER))
-    p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(iface_addr.s_addr));
+    p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->local.s_addr));
   
   if (daemon->domain_suffix && in_list(req_options, OPTION_DOMAINNAME) && 
       !option_find2(netid, config_opts, OPTION_DOMAINNAME))
@@ -904,7 +904,7 @@ static unsigned char *do_req_options(struct dhcp_context *context,
            {
              /* zero means "self" */
              if (a->s_addr == 0)
-               memcpy(p, &iface_addr, INADDRSZ);
+               memcpy(p, &context->local, INADDRSZ);
              else
                memcpy(p, a, INADDRSZ);
              p += INADDRSZ;
index 332386b6509141a77559d8faf118c2958762128e..d71c1ec921e08b653f04ecf800886c6f733ef16a 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000 Simon Kelley
+/* dnsmasq is Copyright (c) 2000 - 2005 Simon Kelley
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -114,7 +114,7 @@ int canonicalise(char *s)
 {
   /* check for legal chars and remove trailing . 
      also fail empty string and label > 63 chars */
-  int dotgap = 0, l = strlen(s);
+  size_t dotgap = 0, l = strlen(s);
   char c;
 
   if (l == 0 || l > MAXDNAME) return 0;
@@ -135,7 +135,7 @@ int canonicalise(char *s)
 }
 
 /* for use during startup */
-void *safe_malloc(int size)
+void *safe_malloc(size_t size)
 {
   void *ret = malloc(size);
   
@@ -158,7 +158,7 @@ char *safe_string_alloc(char *cp)
   return ret;
 }
 
-void complain(char *message, char *arg1)
+static void log_err(char *message, char *arg1)
 {
   char *errmess = strerror(errno);
   
@@ -172,9 +172,17 @@ void complain(char *message, char *arg1)
   syslog(LOG_CRIT, message, arg1, errmess);
 }
 
+void complain(char *message, int lineno, char *file)
+{
+  char buff[256];
+  
+  sprintf(buff, "%s at line %d of %s", message, lineno, file);
+  log_err(buff, NULL);
+}
+
 void die(char *message, char *arg1)
 {
-  complain(message, arg1);
+  log_err(message, arg1);
   syslog(LOG_CRIT, "FAILED to start up");
   exit(1);
 }
@@ -270,3 +278,50 @@ int retry_send(void)
 
    return 0;
 }
+
+void prettyprint_time(char *buf, unsigned int t)
+{
+  if (t == 0xffffffff)
+    sprintf(buf, "infinite");
+  else
+    {
+      unsigned int x, p = 0;
+      if ((x = t/3600))
+       p += sprintf(&buf[p], "%dh", x);
+      if ((x = (t/60)%60))
+       p += sprintf(&buf[p], "%dm", x);
+      if ((x = t%60))
+       p += sprintf(&buf[p], "%ds", x);
+    }
+}
+
+
+/* in may equal out, when maxlen may be -1 (No max len). */
+int parse_hex(char *in, unsigned char *out, int maxlen, unsigned int *wildcard_mask)
+{
+  int mask = 0, i = 0;
+  char *r;
+
+  while (maxlen == -1 || i < maxlen)
+    {
+      for (r = in; *r != 0 && *r != ':' && *r != '-'; r++);
+      if (*r == 0)
+       maxlen = i;
+      if (r != in )
+       {
+         *r = 0;
+         mask = mask << 1;
+         if (strcmp(in, "*") == 0)
+           mask |= 1;
+         else
+           out[i] = strtol(in, NULL, 16);
+         i++;
+       }
+      in = r+1;
+    }
+  
+  if (wildcard_mask)
+    *wildcard_mask = mask;
+
+  return i;
+}