]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
import of dnsmasq-2.20.tar.gz
authorSimon Kelley <simon@thekelleys.org.uk>
Sun, 23 Jan 2005 12:06:08 +0000 (12:06 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Thu, 5 Jan 2012 17:31:11 +0000 (17:31 +0000)
19 files changed:
CHANGELOG
dnsmasq-rh.spec
dnsmasq-suse.spec
dnsmasq.8
dnsmasq.conf.example
rpm/dnsmasq-SuSE.patch
src/cache.c
src/config.h
src/dhcp.c
src/dnsmasq.c
src/dnsmasq.h
src/forward.c
src/isc.c
src/lease.c
src/network.c
src/option.c
src/rfc1035.c
src/rfc2131.c
src/util.c

index 9cc262ff214367a1f7ac7229309529f75a492764..0904b3c257bd72b141ed422372b31bc5c8383f43 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1267,7 +1267,7 @@ version 2.17
            already a fatal error when bind-interfaces is set.)
 
            Allow the --interface and --except-interface options to
-           take a comma-seperated list of interfaces.
+           take a comma-separated list of interfaces.
 
            Tweak --dhcp-userclass matching code to work with the
            ISC dhclient which violates RFC3004 unless its
@@ -1324,7 +1324,50 @@ version 2.19
            option, send the /etc/hosts name as the hostname in 
            the DHCP lease. Thanks to Will Murname for the suggestion.
 
-               
+version 2.20
+           Allow more than one instance of dnsmasq to run on a
+           machine, each providing DHCP service on a different
+           interface, provided that --bind-interfaces is set. This
+           configuration used to work, but regressed in version 2.14
 
+           Fix compilation on Mac OS X. Thanks to Kevin Bullock.
+       
+           Protect against overlong names and overlong
+           labels in configuration and from DHCP.
+
+           Fix interesting corner case in CNAME handling. This occurs
+           when a CNAME has a target which "shadowed" by a name in
+           /etc/hosts or from DHCP. Resolving the CNAME would sneak
+           the upstream value of the CNAME's target into the cache,
+           alongside the local value. Now that doesn't happen, though
+           resolving the CNAME still gives the unshadowed value. This
+           is arguably wrong but rather difficult to fix. The main
+           thing is to avoid getting strange results for the target
+           due to the cache pollution when resolving the
+           CNAME. Thanks to Pierre Habouzit for exploring the corner
+           and submitting a very clear bug report.
+
+           Fix subtle bug in the DNS packet parsing code. It's almost
+           impossible to describe this succinctly, but the one known
+           manifestation is the inability to cache the A record for
+           www.apple.com. Thanks to Bob Alexander for spotting that.
+
+           Support SRV records. Thanks to Robert Kean for the patches
+           for this.
+
+           Fixed sign confusion in the vendor-id matching code which
+           could cause crashes sometimes. (Credit to Mark Wiater for
+           help finding this.)
+
+           Added the ability to match the netid tag in a
+           dhcp-range. Combined with the ability to have multiple
+           ranges in a single subnet, this provides a means to
+           segregate hosts on different address ranges based on
+           vendorclass or userclass. Thanks to Mark Wiater for
+           prompting this enhancement.    
+
+           Added preference values for MX records.
+
+           Added the --localise-queries option.
+           
            
index 87f3d59e26ec880e609cb7fea51de04bdbeec72b..c5fbb215daaa4496ab334f77d6823a16d3027ea8 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.19
+Version: 2.20
 Release: 1
 Copyright: GPL
 Group: System Environment/Daemons
index 8fec85ac81eb2e494107ea359396674d4949229e..b52242a36d450e1d7b650bd0bb980c1a60497cf2 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.19
+Version: 2.20
 Release: 1
 Copyright: GPL
 Group: Productivity/Networking/DNS/Servers
index e80fd3755d8e6ca96c6c9f9143557ee263616ac0..091145e0b9d142e7260323823ad174d4094d668d 100644 (file)
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -143,11 +143,21 @@ requests that it shouldn't reply to. This has the advantage of
 working even when interfaces come and go and change address. This
 option forces dnsmasq to really bind only the interfaces it is
 listening on. About the only time when this is useful is when 
-running another nameserver on the same machine or using IP
+running another nameserver (or another instance of dnsmasq) on the
+same machine or when using IP
 alias. Specifying interfaces with IP alias automatically turns this
-option on. Note that this only applies to the DNS part of dnsmasq, the
-DHCP server always binds the wildcard address in order to receive
-broadcast packets.
+option on. Setting this option also enables multiple instances of
+dnsmasq which provide DHCP service to run in the same machine.
+.TP
+.B \-y, --localise-queries
+Return answers to DNS queries from /etc/hosts which depend on the interface over which the query was
+recieved. If a name in /etc/hosts has more than one address associated with
+it, and at least one of those addresses is on the same subnet as the
+interface to which the query was sent, then return only the
+address(es) on that subnet. This allows for a server  to have multiple
+addresses in /etc/hosts corresponding to each of its interfaces, and
+hosts will get the correct address based on which network they are
+attached to. Currently this facility is limited to IPv4.
 .TP
 .B \-b, --bogus-priv
 Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
@@ -214,7 +224,7 @@ and they are queried only using the specified server. This is
 intended for private nameservers: if you have a nameserver on your
 network which deals with names of the form
 xxx.internal.thekelleys.org.uk at 192.168.1.1 then giving  the flag 
-.B -S /internal.thekelleys.org.uk/192.168.1.1 
+.B -S /.internal.thekelleys.org.uk/192.168.1.1 
 will send all queries for
 internal machines to that nameserver, everything else will go to the
 servers in /etc/resolv.conf. An empty domain specification,
@@ -258,18 +268,20 @@ additional facility that /#/ matches any domain. Thus
 answered from /etc/hosts or DHCP and not sent to an upstream
 nameserver by a more specific --server directive.
 .TP
-.B \-m, --mx-host=<mx name>[,<hostname>]
+.B \-m, --mx-host=<mx name>[[,<hostname>],<preference>]
 Return an MX record named <mx name> pointing to the given hostname (if
 given), or
 the host specified in the --mx-target switch
 or, if that switch is not given, the host on which dnsmasq 
-is running. This is useful for directing mail from systems on a LAN
-to a central server.
+is running. The default is useful for directing mail from systems on a LAN
+to a central server. The preference value is optional, and defaults to
+1 if not given. More than one MX record may be given for a host.
 .TP 
 .B \-t, --mx-target=<hostname>
-Specify target for the MX record returned by dnsmasq. See --mx-host. Note that to turn on the MX function, 
-at least one of --mx-host and --mx-target must be set. If only one of --mx-host and --mx-target 
-is set, the other defaults to the hostname of the machine on which dnsmasq is running.
+Specify the default target for the MX record returned by dnsmasq. See
+--mx-host.  If --mx-target is given, but not --mx-host, then dnsmasq
+returns a MX record containing the MX target for MX queries on the 
+hostname of the machine on which dnsmasq is running.
 .TP
 .B \-e, --selfmx
 Return an MX record pointing to itself for each local
@@ -281,6 +293,23 @@ machine on which dnsmasq is running) for each
 local machine. Local machines are those in /etc/hosts or with DHCP
 leases.
 .TP
+.B \-W, --srv-host=<_service>.<_prot>.[<domain>],[<target>[,<port>[,<priority>[,<weight>]]]]
+Return a SRV DNS record. See RFC2782 for details. If not supplied, the
+domain defaults to that given by
+.B --domain.
+The default for the target domain is empty, and the default for port
+is one and the defaults for 
+weight and priority are zero. Be careful if transposing data from BIND
+zone files: the port, weight and priority numbers are in a different
+order. More than one SRV record for a given service/domain is allowed,
+all that match are returned. Specifying at least one 
+.B --srv-host
+option also turns on replies to SOA queries for the
+domain given by the
+.B --domain 
+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 \-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
@@ -307,7 +336,8 @@ always optional. On some broken systems, dnsmasq can listen on only
 one interface when using DHCP, and the name of that interface must be
 given using the
 .B interface
-option. This limitation currently affects OpenBSD. The optional
+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 
@@ -380,8 +410,8 @@ specfied in RFC2132. For example, to set the default route option to
 and to set the time-server address to 192.168.0.4, do
 .B --dhcp-option=42,192.168.0.4
 The special address 0.0.0.0 is taken to mean "the address of the
-machine running dnsmasq". Data types allowed are comma seperated
-dotted-quad IP addresses, a decimal number, colon-seperated hex digits
+machine running dnsmasq". Data types allowed are comma separated
+dotted-quad IP addresses, a decimal number, colon-separated hex digits
 and a text string. If the optional network-ids are given then
 this option is only sent when all the network-ids are matched.
 Be careful: no checking is done that the correct type of data for the
@@ -544,6 +574,17 @@ and run dnsmasq with the
 .B \-r /etc/resolv.dnsmasq
 option. This second technique allows for dynamic update of the server
 addresses by PPP or DHCP.
+.PP
+Addresses in /etc/hosts will "shadow" different addresses for the same
+names in the upstream DNS, so "mycompany.com 1.2.3.4" in /etc/hosts will ensure that
+queries for "mycompany.com" always return 1.2.3.4 even if queries in
+the upstream DNS would otherwise return a different address. There is
+one exception to this: if the upstream DNS contains a CNAME which
+points to a shadowed name, then looking up the CNAME through dnsmasq
+will result in the unshadowed address associated with the target of
+the CNAME. To work around this, add the CNAME to /etc/hosts so that
+the CNAME is shadowed too.
+
 .PP
 The network-id system works as follows: For each DHCP request, dnsmasq
 collects a set of valid network-id tags, one from the 
@@ -560,6 +601,14 @@ set collected as described above. The prefix '#' on a tag means 'not'
 so --dhcp=option=#purple,3,1.2.3.4 sends the option when the
 network-id tag purple is not in the set of valid tags.
 .PP
+If the network-id in a
+.B dhcp-range 
+is prefixed with 'net:' then its meaning changes from setting a
+tag to matching it. Thus if there is more than dhcp-range on a subnet,
+and one is tagged with a network-id which is set (for instance
+from a vendorclass option) then hosts which set the netid tag will be 
+allocated addresses in the tagged range.
+.PP 
 The DHCP server in dnsmasq will function as a BOOTP server also,
 provided that the MAC address and IP address for clients are given,
 either using 
index d82159e993128bd9cf73b915d040d5e497d17bef..cd1e4baddbb3484e07558694022c57d9db003a3d 100644 (file)
@@ -4,14 +4,6 @@
 # as the long options legal on the command line. See
 # "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
 
-# Change these lines if you want dnsmasq to serve MX records.
-# Only one of mx-host and mx-target need be set, the other defaults
-# to the name of the host  running dnsmasq.
-#mx-host=
-#mx-target=
-#selfmx
-#localmx
-
 # The following two options make you a better netizen, since they 
 # tell dnsmasq to filter out queries which the public DNS cannot
 # answer, and which load the servers (especially the root servers) 
@@ -28,6 +20,8 @@ bogus-priv
 # which can trigger dial-on-demand links needlessly.
 # Note that (amongst other things) this blocks all SRV requests, 
 # so don't use it if you use eg Kerberos.
+# This option only affects forwarding, SRV records originating for
+# dnsmasq (via srv-host= lines) are not suppressed by it.
 #filterwin2k
 
 # Change this line if you want dns to get its upstream servers from
@@ -63,9 +57,8 @@ bogus-priv
 # webserver.
 #address=/doubleclick.net/127.0.0.1
 
-# You no longer (as of version 1.7) need to set these to enable 
-# dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
-# "dip" group to achieve this.
+# If you want dnsmasq to change uid and gid to something other
+# than the default, edit the following lines.
 #user=
 #group=
 
@@ -292,6 +285,50 @@ bogus-priv
 # and this maps 1.2.3.x to 5.6.7.x
 #alias=1.2.3.0,5.6.7.0,255.255.255.0
 
+
+# Change these lines if you want dnsmasq to serve MX records.
+
+# Return an MX record named "maildomain.com" with target
+# servermachine.com and preference 50
+#mx-host=maildomain.com,servermachine.com,50
+
+# Set the default target for MX records created using the localmx option.
+#mx-target=servermachine.com
+
+# Return an MX record pointing to the mx-target for all local
+# machines.
+#localmx
+
+# Return an MX record pointing to itself for all local machines.
+#selfmx
+
+# Change the following lines if you want dnsmasq to serve SRV 
+# records.  These are useful if you want to serve ldap requests for
+# Active Directory and other windows-originated DNS requests.
+# See RFC 2782.
+# You may add multiple srv-host lines. 
+# 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.
+
+# A SRV record sending LDAP for the example.com domain to
+# ldapserver.example.com port 289
+#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389
+
+# A SRV record sending LDAP for the example.com domain to
+# ldapserver.example.com port 289 (using domain=)
+#domain=example.com
+#srv-host=_ldap._tcp,ldapserver.example.com,389
+
+# Two SRV records for LDAP, each with different priorities
+#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,1
+#srv-host=_ldap._tcp.example.com,ldapserver.example.com,389,2
+
+# A SRV record indicating that there is no LDAP server for the domain
+# example.com 
+#srv-host=_ldap._tcp.example.com
+
 # For debugging purposes, log each DNS query as it passes through
 # dnsmasq.
 #log-queries
index c32a7d80e62ac634dfb5bad830cca38a8634156f..08bc38e0e197bb8e71a7b46228a8b86d5fe6777f 100644 (file)
@@ -9,17 +9,6 @@
  /etc/ppp/resolv.conf which is not normally world readable.
  .TP
  .B \-v, --version
---- dnsmasq.conf.example       2004-08-08 21:18:26.000000000 +0200
-+++ dnsmasq.conf.example       2004-08-12 00:40:01.000000000 +0200
-@@ -65,7 +65,7 @@
- # You no longer (as of version 1.7) need to set these to enable 
- # dnsmasq to read /etc/ppp/resolv.conf since dnsmasq now uses the
--# "dip" group to achieve this.
-+# "dialout" group to achieve this.
- #user=
- #group=
 --- src/config.h       2004-08-11 11:39:18.000000000 +0200
 +++ src/config.h       2004-08-12 00:40:01.000000000 +0200
 @@ -44,7 +44,7 @@
index c50dbe7e66c5b5e2874f2d531379b5555b60f21a..b3e7d76824a73ebda270a6d395deb8f813676fbe 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
@@ -156,32 +156,52 @@ static int is_outdated_cname_pointer(struct crec *crecp)
   return 1;
 }
 
-static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
+static int is_expired(time_t now, struct crec *crecp)
+{
+  if (crecp->flags & F_IMMORTAL)
+    return 0;
+
+  if (difftime(now, crecp->ttd) < 0)
+    return 0;
+
+  return 1;
+}
+
+static int cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
 {
   /* Scan and remove old entries.
      If (flags & F_FORWARD) then remove any forward entries for name and any expired
      entries but only in the same hash bucket as name.
      If (flags & F_REVERSE) then remove any reverse entries for addr and any expired
      entries in the whole cache.
-     If (flags == 0) remove any expired entries in the whole cache. */
+     If (flags == 0) remove any expired entries in the whole cache. 
+
+     In the flags & F_FORWARD case, the return code is valid, and returns zero if the
+     name exists in the cache as a HOSTS or DHCP entry (these are never deleted) */
   
-#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6 | F_CNAME)
   struct crec *crecp, **up;
-  flags &= (F_FORWARD | F_REVERSE | F_IPV6 | F_IPV4 | F_CNAME);
 
   if (flags & F_FORWARD)
     {
       for (up = hash_bucket(name), crecp = *up; crecp; crecp = crecp->hash_next)
-       if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
-           is_outdated_cname_pointer(crecp) || 
-           ((flags == (crecp->flags & F_CACHESTATUS)) && hostname_isequal(cache_get_name(crecp), name)))
-         {
+       if (is_expired(now, crecp) || is_outdated_cname_pointer(crecp))
+         { 
            *up = crecp->hash_next;
            if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-             { 
+             {
                cache_unlink(crecp);
                cache_free(crecp);
              }
+         } 
+       else if ((crecp->flags & F_FORWARD) && 
+                ((flags & crecp->flags & (F_IPV4 | F_IPV6)) || (crecp->flags & F_CNAME)) &&
+                hostname_isequal(cache_get_name(crecp), name))
+         {
+           if (crecp->flags & (F_HOSTS | F_DHCP))
+             return 0;
+           *up = crecp->hash_next;
+           cache_unlink(crecp);
+           cache_free(crecp);
          }
        else
          up = &crecp->hash_next;
@@ -196,8 +216,7 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
 #endif 
       for (i = 0; i < hash_size; i++)
        for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
-         if ((!(crecp->flags & F_IMMORTAL) && difftime(now, crecp->ttd) > 0) ||
-             ((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
+         if (is_expired(now, crecp))
            {
              *up = crecp->hash_next;
              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -206,9 +225,20 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
                  cache_free(crecp);
                }
            }
+         else if (!(crecp->flags & (F_HOSTS | F_DHCP)) &&
+                  (flags & crecp->flags & F_REVERSE) && 
+                  (flags & crecp->flags & (F_IPV4 | F_IPV6)) &&
+                  memcmp(&crecp->addr.addr, addr, addrlen) == 0)
+           {
+             *up = crecp->hash_next;
+             cache_unlink(crecp);
+             cache_free(crecp);
+           }
          else
            up = &crecp->hash_next;
     }
+  
+  return 1;
 }
 
 /* Note: The normal calling sequence is
@@ -260,8 +290,13 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
     return NULL;
 
   /* First remove any expired entries and entries for the name/address we
-     are currently inserting. */
-  cache_scan_free(name, addr, now, flags);
+     are currently inserting. Fail is we attempt to delete a name from
+     /etc/hosts or DHCP. */
+  if (!cache_scan_free(name, addr, now, flags))
+    {
+      insert_error = 1;
+      return NULL;
+    }
   
   /* Now get a cache entry from the end of the LRU list */
   while (1) {
@@ -376,8 +411,7 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
        {
          next = crecp->hash_next;
          
-         if (!is_outdated_cname_pointer(crecp) &&
-             ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
+         if (!is_expired(now, crecp) && !is_outdated_cname_pointer(crecp))
            {
              if ((crecp->flags & F_FORWARD) && 
                  (crecp->flags & prot) &&
@@ -458,7 +492,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
        
        for(i=0; i<hash_size; i++)
         for (crecp = hash_table[i], up = &hash_table[i]; crecp; crecp = crecp->hash_next)
-          if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
+          if (!is_expired(now, crecp))
             {      
               if ((crecp->flags & F_REVERSE) && 
                   (crecp->flags & prot) &&
@@ -835,7 +869,15 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr,
        strcat(addrbuff, "-IPv6");
     }
   else if (flags & F_CNAME)
-    strcpy(addrbuff, "<CNAME>");
+    {
+      /* nasty abuse of IPV4 and IPV6 flags */
+      if (flags & F_IPV4)
+       strcpy(addrbuff, "<MX>");
+      else if (flags & F_IPV6)
+       strcpy(addrbuff, "<SRV>");
+      else
+       strcpy(addrbuff, "<CNAME>");
+    }
   else
 #ifdef HAVE_IPV6
     inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
index 03747a59513c68366c4e9a051c850288320c4daf..997213ca7c123d97ec6dc1c9aa7fcf48e613d745 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2004 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
@@ -12,7 +12,7 @@
 
 /* Author's email: simon@thekelleys.org.uk */
 
-#define VERSION "2.19"
+#define VERSION "2.20"
 
 #define FTABSIZ 150 /* max number of outstanding requests */
 #define MAX_PROCS 20 /* max no children for TCP requests */
@@ -278,7 +278,6 @@ typedef unsigned long in_addr_t;
 #define HAVE_SOCKADDR_SA_LEN
 #undef HAVE_PSELECT
 #define HAVE_BPF
-#define BIND_8_COMPAT
 /* Define before sys/socket.h is included so we get socklen_t */
 #define _BSD_SOCKLEN_T_
 /* This is not defined in Mac OS X arpa/nameserv.h */
index 195be30fa2aca3101301f7fa8a01d611b4d62084..26ca7ac0d7a3f338b72acc572d49174c2bb6a846 100644 (file)
@@ -34,6 +34,13 @@ void dhcp_init(struct daemon *daemon)
       setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &oneopt, sizeof(oneopt)) == -1)  
     die("failed to set options on DHCP socket: %s", NULL);
   
+  /* When bind-interfaces is set, there might be more than one dnmsasq
+     instance binding port 67. That's Ok if they serve different networks.
+     Need to set REUSEADDR to make this posible. */
+  if ((daemon->options & OPT_NOWILD) &&
+      setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt)) == -1)
+    die("failed to set SO_REUSEADDR on DHCP socket: %s", NULL);
+  
   saddr.sin_family = AF_INET;
   saddr.sin_port = htons(DHCP_SERVER_PORT);
   saddr.sin_addr.s_addr = INADDR_ANY;
@@ -429,17 +436,52 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
   return NULL;
 }
 
+/* Is every member of check matched by a member of pool? */
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
+{
+  struct dhcp_netid *tmp1;
+  
+  if (!check)
+    return 0;
+
+  for (; check; check = check->next)
+    {
+      if (check->net[0] != '#')
+       {
+         for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+           if (strcmp(check->net, tmp1->net) == 0)
+             break;
+         if (!tmp1)
+           return 0;
+       }
+      else
+       for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
+         if (strcmp((check->net)+1, tmp1->net) == 0)
+           return 0;
+    }
+  return 1;
+}
+
 int address_allocate(struct dhcp_context *context, struct daemon *daemon,
-                    struct in_addr *addrp, unsigned char *hwaddr)   
+                    struct in_addr *addrp, unsigned char *hwaddr, struct dhcp_netid *netids)   
 {
   /* Find a free address: exclude anything in use and anything allocated to
-     a particular hwaddr/clientid/hostname in our configuration */
+     a particular hwaddr/clientid/hostname in our configuration.
+     Try to return from contexts which mathc netis first. */
 
   struct in_addr start, addr ;
   unsigned int i, j;
   
   for (; context; context = context->current)
-    if (!context->static_only)
+    if (context->static_only)
+      continue;
+    else if (netids && !context->filter_netid)
+      continue;
+    else if (!netids && context->filter_netid)
+      continue;
+    else if (netids && context->filter_netid && !match_netid(&context->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++)
@@ -471,6 +513,10 @@ int address_allocate(struct dhcp_context *context, struct daemon *daemon,
          
        } while (addr.s_addr != start.s_addr);
       }
+
+  if (netids)
+    return address_allocate(context, daemon, addrp, hwaddr, NULL);
+
   return 0;
 }
 
index 7333bde1aa83fcf8857a89cb3f6369bcf22b3f99..f65b7e67fd937a278e1a18fb4ae3c7740db24929 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000-2004 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
@@ -82,8 +82,9 @@ int main (int argc, char **argv)
     die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
 #endif
   
-  interfaces = enumerate_interfaces(daemon);
-    
+  if (!enumerate_interfaces(daemon, &interfaces, NULL, NULL))
+    die("failed to find list of interfaces: %s", NULL);
+
   if (!(daemon->options & OPT_NOWILD) && 
       !(daemon->listeners = create_wildcard_listeners(daemon->port)))
     {
@@ -509,45 +510,25 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
        if (FD_ISSET(listener->tcpfd, set))
         {
           int confd;
+          struct in_addr netmask, dst_addr_4;
           
           while((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
              
           if (confd != -1)
             {
-              int match = 1;
-              if (!(daemon->options & OPT_NOWILD)) 
-                {
-                  /* Check for allowed interfaces when binding the wildcard address */
-                  /* Don't know how to get interface of a connection, so we have to
-                     check by address. This will break when interfaces change address */
-                  union mysockaddr tcp_addr;
-                  socklen_t tcp_len = sizeof(union mysockaddr);
-                  struct iname *tmp;
-                  
-                  if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) != -1)
-                    {
-#ifdef HAVE_IPV6
-                      if (tcp_addr.sa.sa_family == AF_INET6)
-                        tcp_addr.in6.sin6_flowinfo =  htonl(0);
-#endif
-                      for (match = 1, tmp = daemon->if_except; tmp; tmp = tmp->next)
-                        if (sockaddr_isequal(&tmp->addr, &tcp_addr))
-                          match = 0;
-                      
-                      if (match && (daemon->if_names || daemon->if_addrs))
-                        {
-                          match = 0;
-                          for (tmp = daemon->if_names; tmp; tmp = tmp->next)
-                            if (sockaddr_isequal(&tmp->addr, &tcp_addr))
-                              match = 1;
-                          for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
-                            if (sockaddr_isequal(&tmp->addr, &tcp_addr))
-                              match = 1;  
-                        }
-                    }
-                }                        
-              
-              if (!match || (num_kids >= MAX_PROCS))
+              union mysockaddr tcp_addr;
+              socklen_t tcp_len = sizeof(union mysockaddr);
+                      
+              /* Check for allowed interfaces when binding the wildcard address:
+                 we do this by looking for an interface with the same address as 
+                 the local address of the TCP connection, then looking to see if that's
+                 an allowed interface. As a side effect, we get the netmask of the
+                 interface too, for localisation. */
+
+              if ((num_kids >= MAX_PROCS) ||
+                  (!(daemon->options & OPT_NOWILD) &&
+                   (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1 ||
+                    !enumerate_interfaces(daemon, NULL, &tcp_addr, &netmask)))) 
                 close(confd);
 #ifndef NO_FORK
               else if (!(daemon->options & OPT_DEBUG) && fork())
@@ -584,7 +565,21 @@ static void check_dns_listeners(struct daemon *daemon, fd_set *set, time_t now)
                   if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
                     fcntl(confd, F_SETFL, flags & ~O_NONBLOCK);
                   
-                  buff = tcp_request(daemon, confd, now);
+                  if (listener->family == AF_INET)
+                    {
+                      if (daemon->options & OPT_NOWILD)
+                        {
+                          netmask = listener->iface->netmask;
+                          dst_addr_4 = listener->iface->addr.in.sin_addr;
+                        }
+                      else
+                        /* netmask already set by enumerate_interfaces */
+                        dst_addr_4 = tcp_addr.in.sin_addr;
+                    }
+                  else
+                    dst_addr_4.s_addr = 0;
+                  
+                  buff = tcp_request(daemon, confd, now, dst_addr_4, netmask);
                   
                   if (!(daemon->options & OPT_DEBUG))
                     exit(0);
index 7f163a236480b4c2efce5bc9994e18ed2a005919..46da01829090951c62110785ea71bf678d2eaa24 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
 
 /* Author's email: simon@thekelleys.org.uk */
 
-#define COPYRIGHT "Copyright (C) 2000-2004 Simon Kelley" 
+#define COPYRIGHT "Copyright (C) 2000-2005 Simon Kelley" 
 
 #ifdef __linux__
 /* for pselect.... */
-#define _XOPEN_SOURCE 600 
+#  define _XOPEN_SOURCE 600 
 /* but then DNS headers don't compile without.... */
 #define _BSD_SOURCE
 #endif
 
 /* get this before config.h too. */
 #include <syslog.h>
+#ifdef __APPLE__
+/* need this before arpa/nameser.h */
+#  define BIND_8_COMPAT
+#endif
 #include <arpa/nameser.h>
 
 /* and this. */
@@ -57,6 +61,7 @@
 #include <errno.h>
 #include <pwd.h>
 #include <grp.h>
+#include <stdarg.h>
 #if defined(__OpenBSD__) || defined(__NetBSD__)
 #  include <netinet/if_ether.h>
 #else
 #define OPT_RESOLV_DOMAIN  32768
 #define OPT_NO_FORK        65536
 #define OPT_AUTHORITATIVE  131072
+#define OPT_LOCALISE       262144
 
 struct all_addr {
   union {
@@ -119,9 +125,16 @@ struct doctor {
 
 struct mx_record {
   char *mxname, *mxtarget;
+  int preference;
   struct mx_record *next;
 };
 
+struct srv_record {
+  char *srvname, *srvtarget;
+  int srvport, priority, weight;
+  struct srv_record *next;
+};
+
 union bigname {
   char name[MAXDNAME];
   union bigname *next; /* freelist */
@@ -211,11 +224,13 @@ struct server {
 
 struct irec {
   union mysockaddr addr;
+  struct in_addr netmask; /* only valid for IPv4 */
   struct irec *next;
 };
 
 struct listener {
   int fd, tcpfd, family;
+  struct irec *iface; /* only valid for non-wildcard */
   struct listener *next;
 };
 
@@ -319,7 +334,7 @@ struct dhcp_context {
   unsigned int lease_time, addr_epoch;
   struct in_addr netmask, broadcast, router;
   struct in_addr start, end; /* range of available addresses */
-  int static_only;
+  int static_only, filter_netid;
   struct dhcp_netid netid;
   struct dhcp_context *next, *current;
 };
@@ -360,6 +375,7 @@ struct daemon {
   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;
@@ -420,12 +436,13 @@ int setup_reply(HEADER *header, unsigned int qlen,
                unsigned long local_ttl);
 void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, 
                       time_t now, struct daemon *daemon);
-int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now);
+int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, 
+                  struct in_addr local_addr, struct in_addr local_netmask, time_t now);
 int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name, 
                             struct bogus_addr *addr, time_t now);
 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 mx_record *mx);
+int check_for_local_domain(char *name, time_t now, struct daemon *daemon);
 unsigned int questions_crc(HEADER *header, unsigned int plen);
 int resize_packet(HEADER *header, unsigned int plen, 
                  unsigned char *pheader, unsigned int hlen);
@@ -453,13 +470,15 @@ struct daemon *read_opts (int argc, char **argv);
 void forward_init(int first);
 void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now);
 void receive_query(struct listener *listen, struct daemon *daemon, time_t now);
-char *tcp_request(struct daemon *daemon, int confd, time_t now);
+char *tcp_request(struct daemon *daemon, int confd, time_t now,
+                 struct in_addr local_addr, struct in_addr netmask);
 
 /* network.c */
 struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds);
 void reload_servers(char *fname, struct daemon *daemon);
 void check_servers(struct daemon *daemon, struct irec *interfaces);
-struct irec *enumerate_interfaces(struct daemon *daemon);
+int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
+                        union mysockaddr *test_addrp, struct in_addr *netmaskp);
 struct listener *create_wildcard_listeners(int port);
 struct listener *create_bound_listeners(struct irec *interfaces, int port);
 
@@ -469,8 +488,10 @@ void dhcp_packet(struct daemon *daemon, time_t now);
 
 struct dhcp_context *address_available(struct dhcp_context *context, struct in_addr addr);
 struct dhcp_context *narrow_context(struct dhcp_context *context, struct in_addr taddr);
+int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
 int address_allocate(struct dhcp_context *context, struct daemon *daemon,
-                    struct in_addr *addrp, unsigned char *hwaddr);
+                    struct in_addr *addrp, unsigned char *hwaddr,
+                    struct dhcp_netid *netids);
 struct dhcp_config *find_config(struct dhcp_config *configs,
                                struct dhcp_context *context,
                                unsigned char *clid, int clid_len,
index 9332bb0b30a6da3cb40eb89666c1e1f7264716bf..542008dee8cc9ac49d26ac66f8b09481e4d4805e 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
@@ -197,7 +197,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
   else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
     flags = F_NXDOMAIN;
     
-  if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon->mxnames))
+  if (flags == F_NXDOMAIN && check_for_local_domain(qdomain, now, daemon))
     flags = F_NOERR;
 
   if (flags == F_NXDOMAIN || flags == F_NOERR)
@@ -390,7 +390,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
     {
       if (header->rcode == NXDOMAIN && 
          extract_request(header, n, daemon->namebuff, NULL) &&
-         check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
+         check_for_local_domain(daemon->namebuff, now, daemon))
        {
          /* if we forwarded a query for a locally known name (because it was for 
             an unknown type) and the answer is NXDOMAIN, convert that to NODATA,
@@ -474,6 +474,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
   unsigned short type;
   struct iname *tmp;
   struct all_addr dst_addr;
+  struct in_addr netmask, dst_addr_4;
   int m, n, if_index = 0;
   struct iovec iov[1];
   struct msghdr msg;
@@ -491,6 +492,14 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
 #endif
   } control_u;
   
+  if (listen->family == AF_INET && (daemon->options & OPT_NOWILD))
+    {
+      dst_addr_4 = listen->iface->addr.in.sin_addr;
+      netmask = listen->iface->netmask;
+    }
+  else
+    dst_addr_4.s_addr = 0;
+
   iov[0].iov_base = daemon->packet;
   iov[0].iov_len = daemon->edns_pktsz;
     
@@ -526,7 +535,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
        for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
          if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO)
            {
-             dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
+             dst_addr_4 = dst_addr.addr.addr4 = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_spec_dst;
              if_index = ((struct in_pktinfo *)CMSG_DATA(cmptr))->ipi_ifindex;
            }
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
@@ -534,7 +543,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
        {
          for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
            if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR)
-             dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
+             dst_addr_4 = dst_addr.addr.addr4 = *((struct in_addr *)CMSG_DATA(cmptr));
            else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF)
              if_index = ((struct sockaddr_dl *)CMSG_DATA(cmptr))->sdl_index;
        }
@@ -557,7 +566,7 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
       if (if_index == 0)
        return;
       
-      if (daemon->if_except || daemon->if_names)
+      if (daemon->if_except || daemon->if_names || (daemon->options & OPT_LOCALISE))
        {
 #ifdef SIOCGIFNAME
          ifr.ifr_ifindex = if_index;
@@ -567,6 +576,13 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
          if (!if_indextoname(if_index, ifr.ifr_name))
            return;
 #endif
+
+         if (listen->family == AF_INET &&
+             (daemon->options & OPT_LOCALISE) &&
+             ioctl(listen->fd, SIOCGIFNETMASK, &ifr) == -1)
+           return;
+
+         netmask = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
        }
 
       for (tmp = daemon->if_except; tmp; tmp = tmp->next)
@@ -610,7 +626,8 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
 #endif
     }
 
-  m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, now);
+  m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n, daemon, 
+                     dst_addr_4, netmask, now);
   if (m >= 1)
     send_from(listen->fd, daemon->options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr, if_index);
   else
@@ -647,7 +664,8 @@ static int read_write(int fd, char *packet, int size, int rw)
    blocking as neccessary, and then return. Note, need to be a bit careful
    about resources for debug mode, when the fork is suppressed: that's
    done by the caller. */
-char *tcp_request(struct daemon *daemon, int confd, time_t now)
+char *tcp_request(struct daemon *daemon, int confd, time_t now,
+                 struct in_addr local_addr, struct in_addr netmask)
 {
   int size = 0, m;
   unsigned short qtype, gotname;
@@ -689,7 +707,8 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
        }
       
       /* m > 0 if answered from cache */
-      m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, now);
+      m = answer_request(header, ((char *) header) + 65536, (unsigned int)size, daemon, 
+                        local_addr, netmask, now);
       
       if (m == 0)
        {
index 0f21c3bee8787190b4ff6582e32b1b760e4836bc..82016da09527172f455b4ad9f23504d98778fc0e 100644 (file)
--- a/src/isc.c
+++ b/src/isc.c
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000 - 2004 by Simon Kelley
+/* dnsmasq is Copyright (c) 2000 - 2005 by 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
index e0d2948d760b3841e55a3071555f028bd12a4e37..2664a8a77cb2ab79c301a67edf894edd8d97bce8 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
index 52503712824e4736345aff3e30b1a2de257e0727..216d70f0265471923aba83fd62ca11144fbc147f 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
 
 #include "dnsmasq.h"
 
-static struct irec *add_iface(struct daemon *daemon, struct irec *list
-                             char *name, int is_loopback, union mysockaddr *addr) 
+static int iface_allowed(struct daemon *daemon, struct irec *iface
+                        char *name, int is_loopback, union mysockaddr *addr) 
 {
-  struct irec *iface;
   struct iname *tmp;
   
   /* If we are restricting the set of interfaces to use, make
@@ -45,12 +44,8 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
   if (daemon->if_except)
     for (tmp = daemon->if_except; tmp; tmp = tmp->next)
       if (tmp->name && strcmp(tmp->name, name) == 0)
-       {
-         /* record address of named interfaces, for TCP access control */
-         tmp->addr = *addr;
-         return list;
-       }
-
+       return 0;
+       
   /* we may need to check the whitelist */
   if (daemon->if_names || daemon->if_addrs)
     { 
@@ -58,36 +53,39 @@ static struct irec *add_iface(struct daemon *daemon, struct irec *list,
 
       for (tmp = daemon->if_names; tmp; tmp = tmp->next)
        if (tmp->name && (strcmp(tmp->name, name) == 0))
-         {
-           tmp->addr = *addr;
-           found = tmp->used = 1;
-         }
-
+         found = tmp->used = 1;
+         
       for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
        if (sockaddr_isequal(&tmp->addr, addr))
          found = tmp->used = 1;
       
       if (!found) 
-       return list;
+       return 0;
     }
   
   /* check whether the interface IP has been added already 
      it is possible to have multiple interfaces with the same address */
-  for (iface = list; iface; iface = iface->next) 
+  for (; iface; iface = iface->next) 
     if (sockaddr_isequal(&iface->addr, addr))
       break;
   if (iface)
-    return list;
+    return 0;
   
-  /* If OK, add it to the head of the list */
-  iface = safe_malloc(sizeof(struct irec));
-  iface->addr = *addr;
-  iface->next = list;
-  return iface;
+  return 1;
 }
 
+/* This does two different jobs: if chainp is non-NULL, it puts
+   a list of all the interfaces allowed by config into *chainp.
+   If chainp is NULL, it returns 1 if addr is  an address of an interface
+   allowed by config and if that address is IPv4, it fills in the
+   netmask of the interface. 
+   
+   If chainp is non-NULL, a zero return indicates a fatal error.
 
-struct irec *enumerate_interfaces(struct daemon *daemon)
+   If chainp is NULL, errors result in a match failure and zero return.
+*/
+int enumerate_interfaces(struct daemon *daemon, struct irec **chainp,
+                        union mysockaddr *test_addrp, struct in_addr *netmaskp)
 {
 #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
   FILE *f;
@@ -100,9 +98,16 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
   int lastlen = 0;
   int len = 20 * sizeof(struct ifreq);
   int fd = socket(PF_INET, SOCK_DGRAM, 0);
+  struct in_addr netmask;
+  int ret = 0;
 
   if (fd == -1)
-    die ("cannot create socket to enumerate interfaces: %s", NULL);
+    return 0;
+
+#ifdef HAVE_IPV6
+  if (test_addrp && test_addrp->sa.sa_family == AF_INET6)
+    test_addrp->in6.sin6_flowinfo = htonl(0);
+#endif
         
   while (1)
      {
@@ -113,7 +118,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        if (ioctl(fd, SIOCGIFCONF, &ifc) < 0)
         {
           if (errno != EINVAL || lastlen != 0)
-            die ("ioctl error while enumerating interfaces: %s", NULL);
+            goto exit;
         }
        else
         {
@@ -133,7 +138,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
         unaligned accesses. */
       int ifr_len = ((struct ifreq *)ptr)->ifr_addr.sa_len + IF_NAMESIZE;
       if (!(ifr = realloc(ifr, ifr_len)))
-       die("cannot allocate buffer", NULL);
+       goto exit;
       
       memcpy(ifr, ptr, ifr_len);
       ptr += ifr_len;
@@ -147,6 +152,9 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        {
          addr.in = *((struct sockaddr_in *) &ifr->ifr_addr);
          addr.in.sin_port = htons(daemon->port);
+         if (ioctl(fd, SIOCGIFNETMASK, ifr) == -1)
+           goto exit;
+         netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr;
        }
 #ifdef HAVE_IPV6
       else if (ifr->ifr_addr.sa_family == AF_INET6)
@@ -164,9 +172,25 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
        continue; /* unknown address family */
       
       if (ioctl(fd, SIOCGIFFLAGS, ifr) < 0)
-       die("ioctl error getting interface flags: %m", NULL);
+       goto exit;
 
-      iface = add_iface(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr);
+      if (iface_allowed(daemon, iface, ifr->ifr_name, ifr->ifr_flags & IFF_LOOPBACK, &addr))
+       {
+         if (chainp)
+           {
+             struct irec *new = safe_malloc(sizeof(struct irec));
+             new->addr = addr;
+             new->netmask = netmask;
+             new->next = iface;
+             iface = new;
+           }
+         else if (sockaddr_isequal(&addr, test_addrp))
+           {
+             *netmaskp = netmask;
+             ret = 1;
+             goto exit;
+           }
+       }
     }
 
 #if defined(HAVE_LINUX_IPV6_PROC) && defined(HAVE_IPV6)
@@ -198,13 +222,35 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
          
          strncpy(sifr.ifr_name, devname, IF_NAMESIZE);
          if (ioctl(fd, SIOCGIFFLAGS, &sifr) < 0)
-           die("ioctl error getting interface flags: %m", NULL);
-         iface = add_iface(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr);
+           goto exit;
+         
+         if (iface_allowed(daemon, iface, sifr.ifr_name, sifr.ifr_flags & IFF_LOOPBACK, &addr))
+           {
+             if (chainp)
+               {
+                 struct irec *new = safe_malloc(sizeof(struct irec));
+                 new->addr = addr;
+                 new->next = iface;
+                 iface = new;
+               }
+             else if (sockaddr_isequal(&addr, test_addrp))
+               {
+                 ret = 1;
+                 goto exit;
+               }
+           }
        }           
       fclose(f);
     }
 #endif /* LINUX */
-   
+  
+  if (chainp)
+    {
+      *chainp = iface;
+      ret = 1;
+    }
+ exit:  
   if (buf)
     free(buf);
 #ifdef HAVE_SOCKADDR_SA_LEN
@@ -213,7 +259,7 @@ struct irec *enumerate_interfaces(struct daemon *daemon)
 #endif
   close(fd);
 
-  return iface;
+  return ret;
 }
 
 #ifdef HAVE_IPV6
@@ -354,6 +400,7 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
     {
       struct listener *new = safe_malloc(sizeof(struct listener));
       new->family = iface->addr.sa.sa_family;
+      new->iface = iface;
       new->next = listeners;
       if ((new->tcpfd = socket(iface->addr.sa.sa_family, SOCK_STREAM, 0)) == -1 ||
          (new->fd = socket(iface->addr.sa.sa_family, SOCK_DGRAM, 0)) == -1 ||
index 1fc2b8d8ddc6e4ebde2996c8c78f055033d6b472..4844477cef541c44388c95d91d7a429061dfbffc 100644 (file)
@@ -1,4 +1,4 @@
-/* dnsmasq is Copyright (c) 2000 - 2004 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
@@ -21,7 +21,7 @@ struct myoption {
   int val;
 };
 
-#define OPTSTRING "ZDNLERKzowefnbvhdkqr: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:"
+#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:"
 
 static struct myoption opts[] = { 
   {"version", 0, 0, 'v'},
@@ -76,6 +76,8 @@ static struct myoption opts[] = {
   {"edns-packet-max", 1, 0, 'P'},
   {"keep-in-foreground", 0, 0, 'k'},
   {"dhcp-authoritative", 0, 0, 'K'},
+  {"srv-host", 1, 0, 'W'},
+  {"localise-queries", 0, 0, 'y'},
   {0, 0, 0, 0}
 };
 
@@ -102,6 +104,7 @@ static struct optflags optmap[] = {
   { 'D', OPT_NODOTS_LOCAL },
   { 'z', OPT_NOWILD },
   { 'Z', OPT_ETHERS },
+  { 'y', OPT_LOCALISE },
   { 'v', 0},
   { 'w', 0},
   { 0, 0 }
@@ -137,7 +140,7 @@ static char *usage =
 "-K, --dhcp-authoritative            Assume we are the only DHCP server on the local network.\n"
 "-l, --dhcp-leasefile=path           Specify where to store DHCP leases (defaults to " LEASEFILE ").\n"
 "-L, --localmx                       Return MX records for local hosts.\n"
-"-m, --mx-host=host_name             Specify the MX name to reply to.\n"
+"-m, --mx-host=host_name,target,pref Specify an MX record.\n"
 "-M, --dhcp-boot=<bootp opts>        Specify BOOTP options to DHCP server.\n"
 "-n, --no-poll                       Do NOT poll " RESOLVFILE " file, reload only on SIGHUP.\n"
 "-N, --no-negcache                   Do NOT cache failed search results.\n"
@@ -152,15 +155,17 @@ static char *usage =
 "-S, --server=/domain/ipaddr         Specify address(es) of upstream servers with optional domains.\n"
 "    --local=/domain/                Never forward queries to specified domains.\n"
 "-s, --domain=domain                 Specify the domain to be assigned in DHCP leases.\n"
-"-t, --mx-target=host_name           Specify the host in an MX reply.\n"
+"-t, --mx-target=host_name           Specify default target in an MX record.\n"
 "-T, --local-ttl=time                Specify time-to-live in seconds for replies from /etc/hosts.\n"
 "-u, --user=username                 Change to this user after startup. (defaults to " CHUSER ").\n" 
 "-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, --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."
 "-z, --bind-interfaces               Bind only to interfaces in use.\n"
 "-Z, --read-ethers                   Read DHCP static host information from " ETHERSFILE ".\n"
 "\n";
@@ -372,24 +377,41 @@ struct daemon *read_opts (int argc, char **argv)
 
            case 'm':
              {
+               int pref = 1;
+               struct mx_record *new;
+
                if ((comma = strchr(optarg, ',')))
-                 *(comma++) = 0;
+                 {
+                   char *prefstr;
+                   *(comma++) = 0;
+                   if ((prefstr=strchr(comma, ',')))
+                     {
+                       *(prefstr++) = 0;
+                       if (!atoi_check(prefstr, &pref))
+                         {
+                           option = '?';
+                           problem = "bad MX preference";
+                           break;
+                         }
+                     }
+                 }
+
                if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
                  {
                    option = '?';
                    problem = "bad MX name";
+                   break;
                  }
-               else 
-                 {
-                   struct mx_record *new = safe_malloc(sizeof(struct mx_record));
-                   new->next = daemon->mxnames;
-                   daemon->mxnames = new;
-                   new->mxname = safe_string_alloc(optarg);
-                   new->mxtarget = safe_string_alloc(comma); /* may be NULL */
-                 }
+
+               new = safe_malloc(sizeof(struct mx_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;
                break;
              }
-             
+
            case 't':
              if (!canonicalise(optarg))
                {
@@ -747,7 +769,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 = 0;
+               new->static_only = new->filter_netid = 0;
                
                problem = "bad dhcp-range";
 
@@ -758,7 +780,14 @@ struct daemon *read_opts (int argc, char **argv)
                if (*cp != ',' && (comma = strchr(optarg, ',')))
                  {
                    *comma = 0;
-                   new->netid.net = safe_string_alloc(optarg);
+                   if (strstr(optarg, "net:") == optarg)
+                     {
+                       new->netid.net = safe_string_alloc(optarg+4);
+                       new->netid.next = NULL;
+                       new->filter_netid = 1;
+                     }
+                   else
+                     new->netid.net = safe_string_alloc(optarg);
                    a[0] = comma + 1;
                  }
                else
@@ -1363,6 +1392,84 @@ struct daemon *read_opts (int argc, char **argv)
                
                break;
              }
+
+           case 'W':
+             {
+               int port = 1, priority = 0, weight = 0;
+               char *name, *target = NULL;
+               struct srv_record *new;
+               
+               if ((comma = strchr(optarg, ',')))
+                 *(comma++) = 0;
+
+               if (!canonicalise(optarg))
+                 {
+                   option = '?';
+                   problem = "bad SRV record";
+                   break;
+                 }
+               name = safe_string_alloc(optarg);
+               
+               if (comma)
+                 {
+                   optarg = comma;
+                   if ((comma = strchr(optarg, ',')))
+                     *(comma++) = 0;
+                   if (!canonicalise(optarg))
+                     {
+                       option = '?';
+                       problem = "bad SRV target";
+                       break;
+                     }
+                   target = safe_string_alloc(optarg);
+                   if (comma)
+                     {
+                       optarg = comma;
+                       if ((comma = strchr(optarg, ',')))
+                         *(comma++) = 0;
+                       if (!atoi_check(optarg, &port))
+                         {
+                           option = '?';
+                           problem = "invalid port number";
+                           break;
+                         }
+                       if (comma)
+                         {
+                           optarg = comma;
+                           if ((comma = strchr(optarg, ',')))
+                             *(comma++) = 0;
+                           if (!atoi_check(optarg, &priority))
+                             {
+                               option = '?';
+                               problem = "invalid priority";
+                               break;
+                             }
+                           if (comma)
+                             {
+                               optarg = comma;
+                               if ((comma = strchr(optarg, ',')))
+                                 *(comma++) = 0;
+                               if (!atoi_check(optarg, &weight))
+                                 {
+                                   option = '?';
+                                   problem = "invalid weight";
+                                   break;
+                                 }
+                             }
+                         }
+                     }
+                 }
+               
+               new = safe_malloc(sizeof(struct srv_record));
+               new->next = daemon->srvnames;
+               daemon->srvnames = new;
+               new->srvname = name;
+               new->srvtarget = target;
+               new->srvport = port;
+               new->priority = priority;
+               new->weight = weight;
+               break;
+             }
            }
        }
       
@@ -1411,24 +1518,39 @@ struct daemon *read_opts (int argc, char **argv)
 #endif /* IPv6 */
     }
                      
-  /* only one of these need be specified: the other defaults to the
-     host-name */
+  /* only one of these need be specified: the other defaults to the host-name */
   if ((daemon->options & OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
     {
       if (gethostname(buff, MAXDNAME) == -1)
        die("cannot get host-name: %s", NULL);
-             
+      
       if (!daemon->mxnames)
        {
          daemon->mxnames = safe_malloc(sizeof(struct mx_record));
          daemon->mxnames->next = NULL;
          daemon->mxnames->mxtarget = NULL;
          daemon->mxnames->mxname = safe_string_alloc(buff);
-}
+       }
       
       if (!daemon->mxtarget)
        daemon->mxtarget = safe_string_alloc(buff);
     }
+
+  if (daemon->domain_suffix)
+    {
+       /* add domain for any srv record without one. */
+      struct srv_record *srv;
+      
+      for (srv = daemon->srvnames; srv; srv = srv->next)
+       if (strchr(srv->srvname, '.') && strchr(srv->srvname, '.') == strrchr(srv->srvname, '.'))
+         {
+           strcpy(buff, srv->srvname);
+           strcat(buff, ".");
+           strcat(buff, daemon->domain_suffix);
+           free(srv->srvname);
+           srv->srvname = safe_string_alloc(buff);
+         }
+    }
   
   if (daemon->options & OPT_NO_RESOLV)
     daemon->resolv_files = 0;
index 3715ec44529ff2dc501c728f534233b4495e006e..f1a0a4c4152267dc72c31dd064bf7e7fe42b88a8 100644 (file)
 
 #include "dnsmasq.h"
 
+static int add_resource_record(HEADER *header, char *limit, int *truncp, 
+                              unsigned int nameoffset, unsigned char **pp, 
+                              unsigned long ttl, int *offset, unsigned short type, 
+                              unsigned short class, char *format, ...);
+
 static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp, 
                        unsigned char *name, int isExtract)
 {
@@ -19,6 +24,9 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
   unsigned int j, l, hops = 0;
   int retvalue = 1;
   
+  if (isExtract)
+    *cp = 0;
+
   while ((l = *p++))
     {
       unsigned int label_type = l & 0xc0;
@@ -117,9 +125,8 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
          
          if (isExtract)
            *cp++ = '.';
-         else
-           if (*cp != 0 && *cp++ != '.')
-             retvalue = 2;
+         else if (*cp != 0 && *cp++ != '.')
+           retvalue = 2;
        }
       
       if ((unsigned int)(p - (unsigned char *)header) >= plen)
@@ -128,7 +135,9 @@ static int extract_name(HEADER *header, unsigned int plen, unsigned char **pp,
 
   if (isExtract)
     *--cp = 0; /* terminate: lose final period */
-  
+  else if (*cp != 0)
+    retvalue = 2;
+    
   if (p1) /* we jumped via compression */
     *pp = p1;
   else
@@ -420,43 +429,6 @@ static int private_net(struct all_addr *addrp)
     return 0;
 }
  
-static unsigned char *add_text_record(HEADER *header, unsigned int nameoffset, unsigned char *p, 
-                                     unsigned long ttl, unsigned short pref, 
-                                     unsigned short type, char *name, int *offset)
-{
-  unsigned char *sav, *cp;
-  int j;
-  
-  PUTSHORT(nameoffset | 0xc000, p); 
-  PUTSHORT(type, p);
-  PUTSHORT(C_IN, p);
-  PUTLONG(ttl, p); /* TTL */
-  
-  sav = p;
-  PUTSHORT(0, p); /* dummy RDLENGTH */
-
-  if (pref)
-    PUTSHORT(pref, p);
-
-  while (*name) 
-    {
-      cp = p++;
-      for (j=0; *name && (*name != '.'); name++, j++)
-       *p++ = *name;
-      *cp = j;
-      if (*name)
-       name++;
-    }
-  *p++ = 0;
-  j = p - sav - 2;
-  PUTSHORT(j, sav); /* Real RDLENGTH */
-  
-  if (offset)
-    *offset = sav - (unsigned char *)header;
-
-  return p;
-}
-
 static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
 {
   for (; doctor; doctor = doctor->next)
@@ -773,13 +745,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
       header->rcode = NOERROR;
       header->ancount = htons(1);
       header->aa = 1;
-      PUTSHORT (sizeof(HEADER) | 0xc000, p);
-      PUTSHORT(T_A, p);
-      PUTSHORT(C_IN, p);
-      PUTLONG(ttl, p); /* TTL */
-      PUTSHORT(INADDRSZ, p);
-      memcpy(p, addrp, INADDRSZ);
-      p += INADDRSZ;
+      add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_A, C_IN, "4", addrp);
     }
 #ifdef HAVE_IPV6
   else if (p && flags == F_IPV6)
@@ -787,13 +753,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
       header->rcode = NOERROR;
       header->ancount = htons(1);
       header->aa = 1;
-      PUTSHORT (sizeof(HEADER) | 0xc000, p);
-      PUTSHORT(T_AAAA, p);
-      PUTSHORT(C_IN, p);
-      PUTLONG(ttl, p); /* TTL */
-      PUTSHORT(IN6ADDRSZ, p);
-      memcpy(p, addrp, IN6ADDRSZ);
-      p += IN6ADDRSZ;
+      add_resource_record(header, NULL, NULL, sizeof(HEADER), &p, ttl, NULL, T_AAAA, C_IN, "6", addrp);
     }
 #endif
   else /* nowhere to forward to */
@@ -803,18 +763,24 @@ int setup_reply(HEADER *header, unsigned int qlen,
 }
 
 /* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
-int check_for_local_domain(char *name, time_t now, struct mx_record *mx)
+int check_for_local_domain(char *name, time_t now, struct daemon *daemon)
 {
   struct crec *crecp;
+  struct mx_record *mx;
+  struct srv_record *srv;
   
   if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6)) &&
       (crecp->flags & (F_HOSTS | F_DHCP)))
     return 1;
   
-  for (; mx; mx = mx->next)
+  for (mx = daemon->mxnames; mx; mx = mx->next)
     if (hostname_isequal(name, mx->mxname))
       return 1;
-  
+
+  for (srv = daemon->srvnames; srv; srv = srv->next)
+    if (hostname_isequal(name, srv->srvname))
+      return 1;
+
   return 0;
 }
 
@@ -862,8 +828,103 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
   return 0;
 }
 
+static int add_resource_record(HEADER *header, char *limit, int *truncp, unsigned int nameoffset, unsigned char **pp, 
+                              unsigned long ttl, int *offset, unsigned short type, unsigned short class, char *format, ...)
+{
+  va_list ap;
+  unsigned char *sav, *p = *pp;
+  int j;
+  unsigned short usval;
+  long lval;
+  char *sval;
+
+  if (truncp && *truncp)
+    return 0;
+
+  PUTSHORT(nameoffset | 0xc000, p);
+  PUTSHORT(type, p);
+  PUTSHORT(class, p);
+  PUTLONG(ttl, p);      /* TTL */
+
+  sav = p;              /* Save pointer to RDLength field */
+  PUTSHORT(0, p);       /* Placeholder RDLength */
+
+  va_start(ap, format);   /* make ap point to 1st unamed argument */
+  
+  for (; *format; format++)
+    switch (*format)
+      {
+#ifdef HAVE_IPV6
+      case '6':
+       sval = va_arg(ap, char *); 
+       memcpy(p, sval, IN6ADDRSZ);
+       p += IN6ADDRSZ;
+       break;
+#endif
+       
+      case '4':
+       sval = va_arg(ap, char *); 
+       memcpy(p, sval, INADDRSZ);
+       p += INADDRSZ;
+       break;
+       
+      case 's':
+       usval = va_arg(ap, int);
+       PUTSHORT(usval, p);
+       break;
+       
+      case 'l':
+       lval = va_arg(ap, long);
+       PUTLONG(lval, p);
+       break;
+       
+      case 'd':
+       /* get domain-name answer arg and store it in RDATA field */
+       sval = va_arg(ap, char *);
+       while (sval && *sval)
+         {
+           unsigned char *cp = p++;
+           for (j = 0; *sval && (*sval != '.'); sval++, j++)
+             *p++ = *sval;
+           *cp  = j;
+           if (*sval)
+             sval++;
+         }
+       *p++ = 0;
+       break;
+
+      case 't':
+       sval = va_arg(ap, char *);
+       j = strlen(sval);
+       *p++ = j;
+       memcpy(p, sval, j);
+       p += j;
+       break;
+      }
+
+  va_end(ap);  /* clean up variable argument pointer */
+  
+  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)
+    {
+      if (truncp)
+       *truncp = 1;
+      return 0;
+    }
+  
+  *pp = p;
+  return 1;
+}
+
 /* return zero if we can't answer from cache, or packet size if we can */
-int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, time_t now) 
+int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, 
+                  struct in_addr local_addr, struct in_addr local_netmask, time_t now) 
 {
   char *name = daemon->namebuff;
   unsigned char *p, *ansp, *pheader;
@@ -872,10 +933,10 @@ 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;
+  int q, ans, anscount = 0;
   int dryrun = 0, sec_reqd = 0;
   struct crec *crecp;
-  int nxdomain, auth;
+  int nxdomain = 0, auth = 1, trunc = 0;
 
   if (!qdcount || header->opcode != QUERY )
     return 0;
@@ -914,7 +975,6 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
    
   /* now process each question, answers go in RRs after the question */
   p = (unsigned char *)(header+1);
-  nxdomain = 0, auth = 1, anscount = 0;
 
   for (q=0; q<qdcount; q++)
     {
@@ -940,7 +1000,6 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
          ans = 1;
          if (!dryrun)
            {
-             int len;
              if (hostname_isequal(name, "version.bind"))
                sprintf(name, "dnsmasq-%s", VERSION);
              else if (hostname_isequal(name, "authors.bind"))
@@ -949,112 +1008,104 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                sprintf(name, COPYRIGHT);
              else
                *name = 0;
-             len = strlen(name);
-             PUTSHORT(nameoffset | 0xc000, ansp); 
-             PUTSHORT(T_TXT, ansp);
-             PUTSHORT(C_CHAOS, ansp);
-             PUTLONG(0, ansp);
-             PUTSHORT(len+1, ansp);
-             *ansp++ = len;
-             memcpy(ansp, name, len);
-             ansp += len;
-             anscount++;
+             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 0, NULL,
+                                      T_TXT, C_CHAOS, "t", name))
+               anscount++;
            }
          }
+
       else if (qclass == C_IN)
        {
-         if ((daemon->options & OPT_FILTER) && 
-             (qtype == T_SOA || qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
+         if (qtype == T_PTR || qtype == T_ANY)
            {
-             ans = 1;
-             log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
-           }
-         else 
-           {
-             if (qtype == T_PTR || qtype == T_ANY)
-               {
-                 if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
-                   { 
-                     if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
-                       {
-                         /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
-                         ans = 1;
-                         if (!dryrun)
-                           {
-                             log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
-                             nxdomain = 1;
-                           }
-                       }
+             if (!(crecp = cache_find_by_addr(NULL, &addr, now, is_arpa)))
+               { 
+                 if (is_arpa == F_IPV4 && (daemon->options & OPT_BOGUSPRIV) && private_net(&addr))
+                   {
+                     /* if not in cache, enabled and private IPV4 address, return NXDOMAIN */
+                     ans = 1;
+                     nxdomain = 1;
+                     if (!dryrun)
+                       log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
                    }
-                 else do 
-                   { 
-                     /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
-                     if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
-                       continue;
-                     
-                     if (crecp->flags & F_NEG)
-                       {
-                         ans = 1;
-                         if (!dryrun)
-                           {
-                             log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
-                             auth = 0;
-                             if (crecp->flags & F_NXDOMAIN)
-                               nxdomain = 1;
-                           }
-                       }
-                     else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
-                       {
-                         ans = 1;
-                         if (!dryrun)
-                           {
-                             unsigned long ttl;
-                             /* Return 0 ttl for DHCP entries, which might change
-                                before the lease expires. */
-                             if  (crecp->flags & (F_IMMORTAL | F_DHCP))
-                               ttl = daemon->local_ttl;
-                             else
-                               ttl = crecp->ttd - now;
-                             
-                             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-                               auth = 0;
-                             
-                             ansp = add_text_record(header, nameoffset, ansp, ttl, 0, T_PTR, 
-                                                    cache_get_name(crecp), NULL);
-                             
-                             log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
-                                       0, daemon->addn_hosts, crecp->uid);
-                             anscount++;
-                             
-                             /* if last answer exceeded packet size, give up */
-                             if (((unsigned char *)limit - ansp) < 0)
-                               return 0;
-                           }
-                       }
-                   } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
                }
-             
-             for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
-               {
-                 unsigned short type = T_A;
-                 int addrsz = INADDRSZ;
-                                 
-                 if (flag == F_IPV6)
+             else do 
+               { 
+                 /* don't answer wildcard queries with data not from /etc/hosts or dhcp leases */
+                 if (qtype == T_ANY && !(crecp->flags & (F_HOSTS | F_DHCP)))
+                   continue;
+                 
+                 if (crecp->flags & F_NEG)
                    {
+                     ans = 1;
+                     auth = 0;
+                     if (crecp->flags & F_NXDOMAIN)
+                       nxdomain = 1;
+                     if (!dryrun)
+                       log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
+                   }
+                 else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+                   {
+                     ans = 1;
+                     if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+                       auth = 0;
+                     if (!dryrun)
+                       {
+                         unsigned long ttl;
+                         /* Return 0 ttl for DHCP entries, which might change
+                            before the lease expires. */
+                         if  (crecp->flags & (F_IMMORTAL | F_DHCP))
+                           ttl = daemon->local_ttl;
+                         else
+                           ttl = crecp->ttd - now;
+                         
+                         log_query(crecp->flags & ~F_FORWARD, cache_get_name(crecp), &addr,
+                                   0, daemon->addn_hosts, crecp->uid);
+
+                         if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL,
+                                                 T_PTR, C_IN, "d", cache_get_name(crecp)))
+                           anscount++;
+                       }
+                   }
+               } while ((crecp = cache_find_by_addr(crecp, &addr, now, is_arpa)));
+           }
+
+         for (flag = F_IPV4; flag; flag = (flag == F_IPV4) ? F_IPV6 : 0)
+           {
+             unsigned short type = T_A;
+                     
+             if (flag == F_IPV6)
 #ifdef HAVE_IPV6
-                     type = T_AAAA;
-                     addrsz = IN6ADDRSZ;
+                 type = T_AAAA;
 #else
-                     break;
+                 break;
 #endif
-                   }
+             
+             if (qtype != type && qtype != T_ANY)
+               continue;
+             
+           cname_restart:
+             if ((crecp = cache_find_by_name(NULL, name, now, flag | F_CNAME)))
+               {
+                 int localise = 0;
                  
-                 if (qtype != type && qtype != T_ANY)
-                   continue;
-
-               cname_restart:
-                 crecp = NULL;
-                 while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)))
+                 /* See if a putative address is on the network from which we recieved
+                    the query, is so we'll filter other answers. */
+                 if (local_addr.s_addr != 0 && (daemon->options & OPT_LOCALISE) && flag == F_IPV4)
+                   {
+                     struct crec *save = crecp;
+                     do {
+                       if ((crecp->flags & F_HOSTS) &&
+                           is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
+                         {
+                           localise = 1;
+                           break;
+                         } 
+                       } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
+                     crecp = save;
+                   }
+                         
+                 do
                    { 
                      /* don't answer wildcard queries with data not from /etc/hosts
                         or DHCP leases */
@@ -1065,29 +1116,37 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                        {
                          if (!dryrun)
                            {
-                             ansp = add_text_record(header, nameoffset, ansp, crecp->ttd - now, 0, T_CNAME, 
-                                                    cache_get_name(crecp->addr.cname.cache), &nameoffset);
-                             anscount++;
                              log_query(crecp->flags, name, NULL, 0, daemon->addn_hosts, crecp->uid);
+                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, crecp->ttd - now, &nameoffset,
+                                                     T_CNAME, C_IN, "d", cache_get_name(crecp->addr.cname.cache)))
+                               anscount++;
                            }
-                                                 
+                         
                          strcpy(name, cache_get_name(crecp->addr.cname.cache));
                          goto cname_restart;
                        }
-
+                     
                      if (crecp->flags & F_NEG)
                        {
                          ans = 1;
+                         auth = 0;
+                         if (crecp->flags & F_NXDOMAIN)
+                           nxdomain = 1;
                          if (!dryrun)
-                           {
-                             log_query(crecp->flags, name, NULL, 0, NULL, 0);
-                             auth = 0;
-                             if (crecp->flags & F_NXDOMAIN)
-                               nxdomain = 1;
-                           }
+                           log_query(crecp->flags, name, NULL, 0, NULL, 0);
                        }
                      else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
                        {
+                         /* If we are returning local answers depending on network,
+                            filter here. */
+                         if (localise && 
+                             (crecp->flags & F_HOSTS) &&
+                             !is_same_net(*((struct in_addr *)&crecp->addr), local_addr, local_netmask))
+                           continue;
+       
+                         if (!(crecp->flags & (F_HOSTS | F_DHCP)))
+                           auth = 0;
+                         
                          ans = 1;
                          if (!dryrun)
                            {
@@ -1098,68 +1157,93 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                              else
                                ttl = crecp->ttd - now;
                              
-                             if (!(crecp->flags & (F_HOSTS | F_DHCP)))
-                               auth = 0;
                              log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr.addr,
                                        0, daemon->addn_hosts, crecp->uid);
                              
-                             /* copy question as first part of answer (use compression) */
-                             PUTSHORT(nameoffset | 0xc000, ansp); 
-                             PUTSHORT(type, ansp);
-                             PUTSHORT(C_IN, ansp);
-                             PUTLONG(ttl, ansp); /* TTL */
-                             
-                             PUTSHORT(addrsz, ansp);
-                             memcpy(ansp, &crecp->addr, addrsz);
-                             ansp += addrsz;
-                             anscount++;
-                             
-                             if (((unsigned char *)limit - ansp) < 0)
-                               return 0;
+                             if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, ttl, NULL, type, C_IN, 
+                                                     type == T_A ? "4" : "6", &crecp->addr))
+                               anscount++;
                            }
                        }
-                   }
+                   } while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)));
                }
-             
-             if (qtype == T_MX || qtype == T_ANY)
-               {
-                 struct mx_record *mx;
-                 for (mx = daemon->mxnames; mx; mx = mx->next)
-                   if (hostname_isequal(name, mx->mxname))
-                     break;
-                 if (mx)
+           }
+         
+         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))
+                 {
+                 ans = found = 1;
+                 if (!dryrun)
                    {
-                     ans = 1;
-                     if (!dryrun)
-                       {
-                         ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX, 
-                                                mx->mxtarget ? mx->mxtarget : daemon->mxtarget, NULL);
-                         anscount++;
-                       }
+                     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++;
                    }
-                 else if ((daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && 
-                          cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
-                   { 
-                     ans = 1;
-                     if (!dryrun)
-                       {
-                         ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX,  
-                                                (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget, NULL);
-                         anscount++;
-                       }
+                 }
+             
+             if (!found && (daemon->options & (OPT_SELFMX | OPT_LOCALMX)) && 
+                 cache_find_by_name(NULL, name, now, F_HOSTS | F_DHCP))
+               { 
+                 ans = 1;
+                 if (!dryrun)
+                   {
+                     log_query(F_CNAME | F_FORWARD | F_CONFIG | F_IPV4, name, NULL, 0, NULL, 0);
+                     if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->local_ttl, NULL, 
+                                             T_MX, C_IN, "sd", 1, 
+                                             (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget))
+                       anscount++;
                    }
                }
+           }
+                 
+         if (qtype == T_SRV || qtype == T_ANY)
+           {
+             int found = 0;
+             struct srv_record *srv;
              
-             if (qtype == T_MAILB)
-               ans = 1, nxdomain = 1;
+             for (srv = daemon->srvnames; srv; srv = srv->next)
+               if (hostname_isequal(name, srv->srvname))
+                 {
+                   found = ans = 1;
+                   if (!dryrun)
+                     {
+                       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++;
+                     }
+                 }
              
+             if (!found && (daemon->options & OPT_FILTER) &&  (qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
+               {
+                 ans = 1;
+                 if (!dryrun)
+                   log_query(F_CONFIG | F_NEG, name, NULL, 0, NULL, 0);
+               }
+           }
+         
+         if (qtype == T_MAILB)
+           ans = 1, nxdomain = 1;
+
+         if (qtype == T_SOA && (daemon->options & OPT_FILTER))
+           {
+             ans = 1; 
+             if (!dryrun)
+               log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
            }
        }
-      
-      if (!ans || ((unsigned char *)limit - ansp) < 0)
+
+      if (!ans)
        return 0; /* failed to answer a question */
     }
-
+  
   if (dryrun)
     {
       dryrun = 0;
@@ -1170,7 +1254,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
   header->qr = 1; /* response */
   header->aa = auth; /* authoritive - only hosts and DHCP derived names. */
   header->ra = 1; /* recursion if available */
-  header->tc = 0; /* truncation */
+  header->tc = trunc; /* truncation */
   if (anscount == 0 && nxdomain)
     header->rcode = NXDOMAIN;
   else
index 0c333247748fcff8a7d3511141c2f1b4748fe17a..c0a00abf21a125333c7bd71880dae2ec6f9ffdac 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
@@ -59,13 +59,12 @@ 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);
 static void bootp_option_put(struct dhcp_packet *mess, 
                             struct dhcp_boot *boot_opts, struct dhcp_netid *netids);
-static unsigned int option_len(unsigned char *opt);
+static int option_len(unsigned char *opt);
 static void *option_ptr(unsigned char *opt);
 static struct in_addr option_addr(unsigned char *opt);
 static unsigned int option_uint(unsigned char *opt, int size);
 static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface, char *string);
-static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool);
-static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize);
+static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize);
 static unsigned char *do_req_options(struct dhcp_context *context,
                                     unsigned char *p, unsigned char *end, 
                                     unsigned char *req_options, 
@@ -211,7 +210,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)
+      if (context->netid.net && !context->filter_netid)
        {
          context->netid.next = netid;
          netid = &context->netid;
@@ -255,7 +254,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
     hostname = config->hostname;
   else if ((opt = option_find(mess, sz, OPTION_HOSTNAME, 1)))
     {
-      unsigned int len = option_len(opt);
+      int len = option_len(opt);
       hostname = daemon->dhcp_buff;
       memcpy(hostname, option_ptr(opt), len);
       /* May not be zero terminated */
@@ -291,7 +290,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
   if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
     {
       unsigned char *ucp = option_ptr(opt);
-      unsigned int tmp, j;
+      int tmp, j;
       for (j = 0; j < option_len(opt); j += ucp[j] + 1);
       if (j == option_len(opt))
        for (j = 0; j < option_len(opt); j = tmp)
@@ -304,7 +303,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
   for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
     if ((opt = option_find(mess, sz, vendor->is_vendor ? OPTION_VENDOR_ID : OPTION_USER_CLASS, 1)))
       {
-       unsigned int i;
+       int i;
        for (i = 0; i <= (option_len(opt) - vendor->len); i++)
          if (memcmp(vendor->data, option_ptr(opt)+i, vendor->len) == 0)
            {
@@ -407,7 +406,7 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
       else if (opt && address_available(context, addr) && !lease_find_by_addr(addr) && 
               !config_find_by_address(daemon->dhcp_conf, addr))
        mess->yiaddr = addr;
-      else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr))
+      else if (!address_allocate(context, daemon, &mess->yiaddr, mess->chaddr, netid))
        message = "no address available";      
       log_packet("DISCOVER", opt ? &addr : NULL, mess->chaddr, iface_name, message);          
       
@@ -415,7 +414,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)
+      if (context->netid.net && !context->filter_netid)
        {
          context->netid.next = netid;
          netid = &context->netid;
@@ -542,7 +541,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)
+         if (context->netid.net && !context->filter_netid)
            {
              context->netid.next = netid;
              netid = &context->netid;
@@ -628,7 +627,7 @@ static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr,
         string ? string : "");
 }
 
-static unsigned int option_len(unsigned char *opt)
+static int option_len(unsigned char *opt)
 {
   return opt[1];
 }
@@ -769,7 +768,7 @@ static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt
   return NULL;
 }
  
-static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, unsigned int minsize)
+static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type, int minsize)
 {
   int overload = 0; 
   unsigned char *ret;
@@ -805,32 +804,6 @@ static int in_list(unsigned char *list, int opt)
   return 0;
 }
 
-/* Is every member of check matched by a member of pool? */
-static int match_netid(struct dhcp_netid *check, struct dhcp_netid *pool)
-{
-  struct dhcp_netid *tmp1;
-  
-  if (!check)
-    return 0;
-
-  for (; check; check = check->next)
-    {
-      if (check->net[0] != '#')
-       {
-         for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
-           if (strcmp(check->net, tmp1->net) == 0)
-             break;
-         if (!tmp1)
-           return 0;
-       }
-      else
-       for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
-         if (strcmp((check->net)+1, tmp1->net) == 0)
-           return 0;
-    }
-  return 1;
-}
-
 static struct dhcp_opt *option_find2(struct dhcp_netid *netid, struct dhcp_opt *opts, int opt)
 {
   struct dhcp_opt *tmp;
index dc90eafa65ed991394a8229390154bb2ff3cc615..332386b6509141a77559d8faf118c2958762128e 100644 (file)
@@ -113,20 +113,22 @@ int legal_char(char c)
 int canonicalise(char *s)
 {
   /* check for legal chars and remove trailing . 
-     also fail empty string. */
-  int l = strlen(s);
+     also fail empty string and label > 63 chars */
+  int dotgap = 0, l = strlen(s);
   char c;
 
-  if (l == 0) return 0;
+  if (l == 0 || l > MAXDNAME) return 0;
 
   if (s[l-1] == '.')
     {
       if (l == 1) return 0;
       s[l-1] = 0;
     }
-
+  
   while ((c = *s++))
-    if (c != '.' && !legal_char(c))
+    if (c == '.')
+      dotgap = 0;
+    else if (!legal_char(c) || (++dotgap > MAXLABEL))
       return 0;
   
   return 1;