]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
import of dnsmasq-2.16.tar.gz
authorSimon Kelley <simon@thekelleys.org.uk>
Thu, 21 Oct 2004 19:24:00 +0000 (20:24 +0100)
committerSimon Kelley <simon@thekelleys.org.uk>
Thu, 5 Jan 2012 17:31:11 +0000 (17:31 +0000)
21 files changed:
CHANGELOG
FAQ
contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl [new file with mode: 0755]
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 e4304ffd04ae26923a887b5df1b985c0bc17d6d4..26cfcf6d0b026c0ff6f1ab01cf90d055a80b8d2b 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1213,4 +1213,43 @@ version 2.15
            Improve error messages when there are problems with
            configuration.
 
+version 2.16
+           Fixed typo in OpenBSD-only code which stopped compilation
+            under that OS. Chris Weinhaupl gets credit for reporting 
+            this.
+
+           Added dhcp-authoritative option which restores non-RFC 
+           compliant but desirable behaviour of pre-2.14 versions and
+            avoids long timeouts while DHCP clients try to renew leases
+            which are unknown to dnsmasq. Thanks to John Mastwijk for 
+           help with this.      
+
+           Added support to the DHCP option code to allow RFC-3397 
+           domain search DHCP option (119) to be sent.
+
+            Set NONBLOCK on all listening sockets to workaround non-POSIX
+            compliance in Linux 2.4 and 2.6. This fixes rare hangs which
+            occured when corrupted packets were received. Thanks to
+           Joris van Rantwijk for chasing that down.
  
+           Updated config.h for NetBSD. Thanks to Martin Lambers.
+
+            Do a better job of distinguishing between retransmissions
+           and new queries when forwarding. This fixes a bug
+           triggered by the polipo web cache which sends A and AAAA
+           queries both with the same transaction-ID. Thanks to 
+           Joachim Berdal Haga and Juliusz Chroboczek for help with this.
+
+           Rewrote cache code to store CNAMES, rather then chasing
+           them before storage. This eliminates bad situations when
+           clients get inconsistent views depending on if data comes
+           from the cache.
+
+           Allow for more than one --addn-hosts flag.
+
+           Clarify logged message when a DHCP lease clashes with an
+           /etc/hosts entry. Thanks to Mat Swift for the suggestion.
+
+           Added dynamic-dnsmasq from Peter Willis to the contrib
+           section.
+
diff --git a/FAQ b/FAQ
index 67d3889f46b4aa5ea8a2f7f46d2b8cd83680d1dc..dceb8d19df74bc183ac1bf2018e35a37e8c22d47 100644 (file)
--- a/FAQ
+++ b/FAQ
@@ -284,6 +284,22 @@ A: Yes, new releases of dnsmasq are always announced through
    freshmeat.net, and they allow you to subcribe to email alerts when
    new versions of particular projects are released.
 
+Q: What does the dhcp-authoritative option do? 
+
+A: See http://www.isc.org/index.pl?/sw/dhcp/authoritative.php - that's
+   for the ISC daemon, but the same applies to dnsmasq.
+
+Q: Why does my Gentoo box pause for a minute before getting a new
+   lease?
+
+A: Because when a Gentoo box shuts down, it releases its lease with
+   the server but remembers it on the client; this seems to be a 
+   Gentoo-specific patch to dhcpcd. On restart it tries to renew
+   a lease which is long gone, as far as dnsmasq is concerned, and
+   dnsmasq ignores it until is times out and restarts the process.
+   To fix this, set the dhcp-authoritative flag in dnsmasq.
+
+
 
 
              
diff --git a/contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl b/contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl
new file mode 100755 (executable)
index 0000000..3c4a1f1
--- /dev/null
@@ -0,0 +1,249 @@
+#!/usr/bin/perl
+# dynamic-dnsmasq.pl - update dnsmasq's internal dns entries dynamically
+# Copyright (C) 2004  Peter Willis
+# 
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+# the purpose of this script is to be able to update dnsmasq's dns
+# records from a remote dynamic dns client.
+# 
+# basic use of this script:
+# dynamic-dnsmasq.pl add testaccount 1234 testaccount.mydomain.com
+# dynamic-dnsmasq.pl listen &
+# 
+# this script tries to emulate DynDNS.org's dynamic dns service, so
+# technically you should be able to use any DynDNS.org client to
+# update the records here. tested and confirmed to work with ddnsu
+# 1.3.1. just point the client's host to the IP of this machine,
+# port 9020, and include the hostname, user and pass, and it should
+# work.
+# 
+# make sure "addn-hosts=/etc/dyndns-hosts" is in your /etc/dnsmasq.conf
+# file and "nopoll" is commented out.
+
+use strict;
+use IO::Socket;
+use MIME::Base64;
+use DB_File;
+use Fcntl;
+
+my $accountdb = "accounts.db";
+my $recordfile = "/etc/dyndns-hosts";
+my $dnsmasqpidfile = "/var/run/dnsmasq.pid"; # if this doesn't exist, will look for process in /proc
+my $listenaddress = "0.0.0.0";
+my $listenport = 9020;
+
+# no editing past this point should be necessary
+
+if ( @ARGV < 1 ) {
+       die "Usage: $0 ADD|DEL|LISTUSERS|WRITEHOSTSFILE|LISTEN\n";
+} elsif ( lc $ARGV[0] eq "add" ) {
+       die "Usage: $0 ADD USER PASS HOSTNAME\n" unless @ARGV == 4;
+       add_acct($ARGV[1], $ARGV[2], $ARGV[3]);
+} elsif ( lc $ARGV[0] eq "del" ) {
+       die "Usage: $0 DEL USER\n" unless @ARGV == 2;
+       print "Are you sure you want to delete user \"$ARGV[1]\"? [N/y] ";
+       my $resp = <STDIN>;
+       chomp $resp;
+       if ( lc substr($resp,0,1) eq "y" ) {
+               del_acct($ARGV[1]);
+       }
+} elsif ( lc $ARGV[0] eq "listusers" or lc $ARGV[0] eq "writehostsfile" ) {
+       my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
+       my $fh;
+       if ( lc $ARGV[0] eq "writehostsfile" ) {
+               open($fh, ">$recordfile") || die "Couldn't open recordfile \"$recordfile\": $!\n";
+               flock($fh, 2);
+               seek($fh, 0, 0);
+               truncate($fh, 0);
+        }
+       while ( my ($key, $val) = each %h ) {
+               my ($pass, $domain, $ip) = split("\t",$val);
+               if ( lc $ARGV[0] eq "listusers" ) {
+                       print "user $key, hostname $domain, ip $ip\n";
+               } else {
+                       if ( defined $ip ) {
+                               print $fh "$ip\t$domain\n";
+                       }
+               }
+       }
+       if ( lc $ARGV[0] eq "writehostsfile" ) {
+               flock($fh, 8);
+               close($fh);
+               dnsmasq_rescan_configs();
+       }
+       undef $X;
+       untie %h;
+} elsif ( lc $ARGV[0] eq "listen" ) {
+       listen_for_updates();
+}
+
+sub listen_for_updates {
+       my $sock = IO::Socket::INET->new(Listen    => 5,
+               LocalAddr => $listenaddress, LocalPort => $listenport,
+               Proto     => 'tcp', ReuseAddr => 1,
+               MultiHomed => 1) || die "Could not open listening socket: $!\n";
+       $SIG{'CHLD'} = 'IGNORE';
+       while ( my $client = $sock->accept() ) {
+               my $p = fork();
+               if ( $p != 0 ) {
+                       next;
+               }
+               $SIG{'CHLD'} = 'DEFAULT';
+               my @headers;
+               my %cgi;
+               while ( <$client> ) {
+                       s/(\r|\n)//g;
+                       last if $_ eq "";
+                       push @headers, $_;
+               }
+               foreach my $header (@headers) {
+                       if ( $header =~ /^GET \/nic\/update\?([^\s].+) HTTP\/1\.[01]$/ ) {
+                               foreach my $element (split('&', $1)) {
+                                       $cgi{(split '=', $element)[0]} = (split '=', $element)[1];
+                               }
+                       } elsif ( $header =~ /^Authorization: basic (.+)$/ ) {
+                               unless ( defined $cgi{'hostname'} ) {
+                                       print_http_response($client, undef, "badsys");
+                                       exit(1);
+                               }
+                               if ( !exists $cgi{'myip'} ) {
+                                       $cgi{'myip'} = $client->peerhost();
+                               }
+                               my ($user,$pass) = split ":", MIME::Base64::decode($1);
+                               if ( authorize($user, $pass, $cgi{'hostname'}, $cgi{'myip'}) == 0 ) {
+                                       print_http_response($client, $cgi{'myip'}, "good");
+                                       update_dns(\%cgi);
+                               } else {
+                                       print_http_response($client, undef, "badauth");
+                                       exit(1);
+                               }
+                               last;
+                       }
+               }
+               exit(0);
+       }
+       return(0);
+}
+
+sub add_acct {
+       my ($user, $pass, $hostname) = @_;
+       my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
+       $X->put($user, join("\t", ($pass, $hostname)));
+       undef $X;
+       untie %h;
+}
+
+sub del_acct {
+        my ($user, $pass, $hostname) = @_;
+        my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
+        $X->del($user);
+        undef $X;
+        untie %h;
+}
+
+
+sub authorize {
+       my $user = shift;
+       my $pass = shift;
+       my $hostname = shift;
+       my $ip = shift;;
+       my $X = tie my %h, "DB_File", $accountdb, O_RDWR|O_CREAT, 0600, $DB_HASH;
+       my ($spass, $shost) = split("\t", $h{$user});
+       if ( defined $h{$user} and ($spass eq $pass) and ($shost eq $hostname) ) {
+               $X->put($user, join("\t", $spass, $shost, $ip));
+               undef $X;
+               untie %h;
+               return(0);
+       }
+       undef $X;
+       untie %h;
+       return(1);
+}
+
+sub print_http_response {
+       my $sock = shift;
+       my $ip = shift;
+       my $response = shift;
+       print $sock "HTTP/1.0 200 OK\n";
+       my @tmp = split /\s+/, scalar gmtime();
+       print $sock "Date: $tmp[0], $tmp[2] $tmp[1] $tmp[4] $tmp[3] GMT\n";
+       print $sock "Server: Peter's Fake DynDNS.org Server/1.0\n";
+       print $sock "Content-Type: text/plain; charset=ISO-8859-1\n";
+       print $sock "Connection: close\n";
+       print $sock "Transfer-Encoding: chunked\n";
+       print $sock "\n";
+       #print $sock "12\n"; # this was part of the dyndns response but i'm not sure what it is
+       print $sock "$response", defined($ip)? " $ip" : "" . "\n";
+}
+
+sub update_dns {
+       my $hashref = shift;
+       my @records;
+       my $found = 0;
+       # update the addn-hosts file
+       open(FILE, "+<$recordfile") || die "Couldn't open recordfile \"$recordfile\": $!\n";
+       flock(FILE, 2);
+       while ( <FILE> ) {
+               if ( /^(\d+\.\d+\.\d+\.\d+)\s+$$hashref{'hostname'}\n$/si ) {
+                       if ( $1 ne $$hashref{'myip'} ) {
+                               push @records, "$$hashref{'myip'}\t$$hashref{'hostname'}\n";
+                               $found = 1;
+                       }
+               } else {
+                       push @records, $_;
+               }
+       }
+       unless ( $found ) {
+               push @records, "$$hashref{'myip'}\t$$hashref{'hostname'}\n";
+       }
+       sysseek(FILE, 0, 0);
+       truncate(FILE, 0);
+       syswrite(FILE, join("", @records));
+       flock(FILE, 8);
+       close(FILE);
+       dnsmasq_rescan_configs();
+       return(0);
+}
+
+sub dnsmasq_rescan_configs {
+       # send the HUP signal to dnsmasq
+       if ( -r $dnsmasqpidfile ) {
+               open(PID,"<$dnsmasqpidfile") || die "Could not open PID file \"$dnsmasqpidfile\": $!\n";
+               my $pid = <PID>;
+               close(PID);
+               chomp $pid;
+               if ( kill(0, $pid) ) {
+                       kill(1, $pid);
+               } else {
+                       goto LOOKFORDNSMASQ;
+               }
+       } else {
+               LOOKFORDNSMASQ:
+               opendir(DIR,"/proc") || die "Couldn't opendir /proc: $!\n";
+               my @dirs = grep(/^\d+$/, readdir(DIR));
+               closedir(DIR);
+               foreach my $process (@dirs) {
+                       if ( open(FILE,"</proc/$process/cmdline") ) {
+                               my $cmdline = <FILE>;
+                               close(FILE);
+                               if ( (split(/\0/,$cmdline))[0] =~ /dnsmasq/ ) {
+                                       kill(1, $process);
+                               }
+                       }
+               }
+       }
+       return(0);
+}
index 3594e3f9ae0baa8eae7d59253db102c8c132fb8d..3652a243c54595e673c9b8b375a3c870edca9cc0 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.15
+Version: 2.16
 Release: 1
 Copyright: GPL
 Group: System Environment/Daemons
index b379e0ac3ead1f831321fafdc97c06abeaf77f0c..c1ca8a0ba9137cb03a5346f206402a8fa5f2237f 100644 (file)
@@ -5,7 +5,7 @@
 ###############################################################################
 
 Name: dnsmasq
-Version: 2.15
+Version: 2.16
 Release: 1
 Copyright: GPL
 Group: Productivity/Networking/DNS/Servers
index 907b3ff680f60f3797e6f77731d58a88c231f996..b2bec2ee384981f4aa03d40032b87095fa102238 100644 (file)
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -34,8 +34,8 @@ Don't read the hostnames in /etc/hosts.
 .TP
 .B \-H, --addn-hosts=<file>
 Additional hosts file. Read the specified file as well as /etc/hosts. If -h is given, read
-only the specified file. At most one additional hosts file may be
-given.
+only the specified file. This option may be repeated for more than one
+additional hosts file.
 .TP
 .B \-T, --local-ttl=<time>
 When replying with information from /etc/hosts or the DHCP leases
@@ -423,6 +423,12 @@ default is 150. This limit is to prevent DoS attacks from hosts which
 create thousands of leases and use lots of memory in the dnsmasq
 process.
 .TP
+.B \-K, --dhcp-authoritative
+Should be set when dnsmasq is definatively the only DHCP server on a network.
+It changes the behaviour from strict RFC compliance so that DHCP requests on
+unknown leases from unknown hosts are not ignored. This allows new hosts
+to get a lease without a tedious timeout under all circumstances.
+.TP
 .B \-l, --dhcp-leasefile=<path>
 Use the specified file to store DHCP lease information. If this option
 is given but no dhcp-range option is given then dnsmasq version 1
index 7fdd24a9326ca1acd711a9b1790c94478bb5e10f..d82159e993128bd9cf73b915d040d5e497d17bef 100644 (file)
@@ -237,7 +237,10 @@ bogus-priv
 #dhcp-option=45,0.0.0.0     # netbios datagram distribution server
 #dhcp-option=46,8           # netbios node type
 #dhcp-option=47             # empty netbios scope.
+
+# Send RFC-3397 DNS domain search DHCP option. WARNING: Your DHCP client
+# probably doesn't support this......
+#dhcp-option=119,eng.apple.com,marketing.apple.com 
 
 # Set the boot filename and tftpd server name and address
 # for BOOTP. You will only need this is you want to
@@ -252,6 +255,16 @@ bogus-priv
 # the line below.
 #dhcp-leasefile=/var/lib/misc/dnsmasq.leases
 
+# Set the DHCP server to authoritative mode. In this mode it will barge in 
+# and take over the lease for any client which broadcasts on the network, 
+# whether it has a record of the lease or not. This avoids long timeouts
+# when a machine wakes up on a new network. DO NOT enable this if there's 
+# the slighest chance that you might end up accidentally configuring a DHCP
+# server for your campus/company accidentally. The ISC server uses the same
+# the same option, and this URL provides more information:
+# http://www.isc.org/index.pl?/sw/dhcp/authoritative.php
+#dhcp-authoritative
+
 # Set the cachesize here.
 #cache-size=150
 
index 9982808691182288e51885e40078c2e7d333007e..c32a7d80e62ac634dfb5bad830cca38a8634156f 100644 (file)
@@ -1,6 +1,6 @@
 --- dnsmasq.8  2004-08-08 20:57:56.000000000 +0200
 +++ dnsmasq.8  2004-08-12 00:40:01.000000000 +0200
-@@ -63,7 +63,7 @@
+@@ -69,7 +69,7 @@
  .TP
  .B \-g, --group=<groupname> 
  Specify the group which dnsmasq will run
@@ -31,7 +31,7 @@
  #define IP6INTERFACES "/proc/net/if_inet6"
  #define UPTIME "/proc/uptime"
  #define DHCP_SERVER_PORT 67
-@@ -176,7 +176,7 @@
+@@ -187,7 +187,7 @@
  
  /* platform independent options. */
  #undef HAVE_BROKEN_RTC
index 1419631198fb83734cda7874941a9c7abcd65b85..64f24af547801063a4adccab87bcd9a84d58751a 100644 (file)
@@ -17,11 +17,12 @@ static struct crec *dhcp_inuse, *dhcp_spare, *new_chain;
 static int cache_inserted, cache_live_freed, insert_error;
 static union bigname *big_free;
 static int bignames_left, log_queries, cache_size, hash_size;
-static char *addn_file;
+static int index;
 
 static void cache_free(struct crec *crecp);
 static void cache_unlink(struct crec *crecp);
 static void cache_link(struct crec *crecp);
+static char *record_source(struct hostsfile *add_hosts, int index);
 
 void cache_init(int size, int logq)
 {
@@ -35,7 +36,7 @@ void cache_init(int size, int logq)
   cache_size = size;
   big_free = NULL;
   bignames_left = size/10;
-  addn_file = NULL;
+  index = 0;
 
   cache_inserted = cache_live_freed = 0;
 
@@ -47,6 +48,7 @@ void cache_init(int size, int logq)
        {
          cache_link(crecp);
          crecp->flags = 0;
+         crecp->uid = index++;
        }
     }
   
@@ -83,7 +85,8 @@ static void cache_free(struct crec *crecp)
 {
   crecp->flags &= ~F_FORWARD;
   crecp->flags &= ~F_REVERSE;
-
+  crecp->uid = index++; /* invalidate CNAMES pointing to this. */
+  
   if (cache_tail)
     cache_tail->next = crecp;
   else
@@ -137,6 +140,22 @@ char *cache_get_name(struct crec *crecp)
   return crecp->name.sname;
 }
 
+static int is_outdated_cname_pointer(struct crec *crecp)
+{
+  struct crec *target = crecp->addr.cname.cache;
+
+  if (!(crecp->flags & F_CNAME))
+    return 0;
+
+  if (!target)
+    return 1;
+
+  if (crecp->addr.cname.uid == target->uid)
+    return 0;
+
+  return 1;
+}
+
 static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsigned short flags)
 {
   /* Scan and remove old entries.
@@ -146,14 +165,15 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
      entries in the whole cache.
      If (flags == 0) remove any expired entries in the whole cache. */
   
-#define F_CACHESTATUS (F_HOSTS | F_DHCP | F_FORWARD | F_REVERSE | F_IPV4 | F_IPV6)
+#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);
+  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)))
          {
            *up = crecp->hash_next;
@@ -177,7 +197,7 @@ static void cache_scan_free(char *name, struct all_addr *addr, time_t now, unsig
       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, addrlen) == 0))
+             ((flags == (crecp->flags & F_CACHESTATUS)) && memcmp(&crecp->addr.addr, addr, addrlen) == 0))
            {
              *up = crecp->hash_next;
              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -214,8 +234,8 @@ void cache_start_insert(void)
   insert_error = 0;
 }
  
-void cache_insert(char *name, struct all_addr *addr, 
-                 time_t now,  unsigned long ttl, unsigned short flags)
+struct crec *cache_insert(char *name, struct all_addr *addr, 
+                         time_t now,  unsigned long ttl, unsigned short flags)
 {
 #ifdef HAVE_IPV6
   int addrlen = (flags & F_IPV6) ? IN6ADDRSZ : INADDRSZ;
@@ -226,7 +246,7 @@ void cache_insert(char *name, struct all_addr *addr,
   union bigname *big_name = NULL;
   int freed_all = flags & F_REVERSE;
 
-  log_query(flags | F_UPSTREAM, name, addr, 0);
+  log_query(flags | F_UPSTREAM, name, addr, 0, NULL, 0);
 
   /* name is needed as workspace by log_query in this case */
   if ((flags & F_NEG) && (flags & F_REVERSE))
@@ -237,7 +257,7 @@ void cache_insert(char *name, struct all_addr *addr,
 
   /* if previous insertion failed give up now. */
   if (insert_error)
-    return;
+    return NULL;
 
   /* First remove any expired entries and entries for the name/address we
      are currently inserting. */
@@ -248,7 +268,7 @@ void cache_insert(char *name, struct all_addr *addr,
     if (!(new = cache_tail)) /* no entries left - cache is too small, bail */
       {
        insert_error = 1;
-       return;
+       return NULL;
       }
     
     /* End of LRU list is still in use: if we didn't scan all the hash
@@ -259,7 +279,7 @@ void cache_insert(char *name, struct all_addr *addr,
       { 
        if (freed_all)
          {
-           cache_scan_free(cache_get_name(new), &new->addr, now, new->flags);
+           cache_scan_free(cache_get_name(new), &new->addr.addr, now, new->flags);
            cache_live_freed++;
          }
        else
@@ -283,7 +303,7 @@ void cache_insert(char *name, struct all_addr *addr,
                 !(big_name = (union bigname *)malloc(sizeof(union bigname))))
          {
            insert_error = 1;
-           return;
+           return NULL;
          }
        else
          bignames_left--;
@@ -306,10 +326,15 @@ void cache_insert(char *name, struct all_addr *addr,
   else
     *cache_get_name(new) = 0;
   if (addr)
-    memcpy(&new->addr, addr, addrlen);
+    memcpy(&new->addr.addr, addr, addrlen);
+  else
+    new->addr.cname.cache = NULL;
+  
   new->ttd = now + (time_t)ttl;
   new->next = new_chain;
   new_chain = new;
+
+  return new;
 }
 
 /* after end of insertion, commit the new entries */
@@ -321,10 +346,16 @@ void cache_end_insert(void)
   while (new_chain)
     { 
       struct crec *tmp = new_chain->next;
-      cache_hash(new_chain);
-      cache_link(new_chain);
+      /* drop CNAMEs which didn't find a target. */
+      if (is_outdated_cname_pointer(new_chain))
+       cache_free(new_chain);
+      else
+       {
+         cache_hash(new_chain);
+         cache_link(new_chain);
+         cache_inserted++;
+       }
       new_chain = tmp;
-      cache_inserted++;
     }
   new_chain = NULL;
 }
@@ -345,7 +376,8 @@ struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsi
        {
          next = crecp->hash_next;
          
-         if ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0)
+         if (!is_outdated_cname_pointer(crecp) &&
+             ((crecp->flags & F_IMMORTAL) || difftime(now, crecp->ttd) < 0))
            {
              if ((crecp->flags & F_FORWARD) && 
                  (crecp->flags & prot) &&
@@ -430,7 +462,7 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
             {      
               if ((crecp->flags & F_REVERSE) && 
                   (crecp->flags & prot) &&
-                  memcmp(&crecp->addr, addr, addrlen) == 0)
+                  memcmp(&crecp->addr.addr, addr, addrlen) == 0)
                 {          
                   if (crecp->flags & (F_HOSTS | F_DHCP))
                     {
@@ -461,19 +493,20 @@ struct crec *cache_find_by_addr(struct crec *crecp, struct all_addr *addr,
   if (ans && 
       (ans->flags & F_REVERSE) &&
       (ans->flags & prot) &&
-      memcmp(&ans->addr, addr, addrlen) == 0)
+      memcmp(&ans->addr.addr, addr, addrlen) == 0)
     return ans;
   
   return NULL;
 }
 
-static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, unsigned short flags)
+static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrlen, 
+                           unsigned short flags, int index)
 {
   struct crec *lookup = cache_find_by_name(NULL, cache->name.sname, 0, flags & (F_IPV4 | F_IPV6));
 
   /* Remove duplicates in hosts files. */
   if (lookup && (lookup->flags & F_HOSTS) &&
-      memcmp(&lookup->addr, addr, addrlen) == 0)
+      memcmp(&lookup->addr.addr, addr, addrlen) == 0)
     free(cache);
   else
     {
@@ -481,12 +514,13 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
       if (cache_find_by_addr(NULL, addr, 0, flags & (F_IPV4 | F_IPV6)))
        flags &= ~F_REVERSE;
       cache->flags = flags;
-      memcpy(&cache->addr, addr, addrlen);
+      cache->uid = index;
+      memcpy(&cache->addr.addr, addr, addrlen);
       cache_hash(cache);
     }
 }
 
-static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int is_addn)
+static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int index)
 {  
   FILE *f = fopen(filename, "r");
   char *line;
@@ -531,9 +565,6 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
       else
        continue;
 
-     if (is_addn)
-        flags |= F_ADDN;
-
      while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
        {
         struct crec *cache;
@@ -548,12 +579,12 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
                 strcpy(cache->name.sname, token);
                 strcat(cache->name.sname, ".");
                 strcat(cache->name.sname, domain_suffix);
-                add_hosts_entry(cache, &addr, addrlen, flags);
+                add_hosts_entry(cache, &addr, addrlen, flags, index);
               }
             if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
               {
                 strcpy(cache->name.sname, token);
-                add_hosts_entry(cache, &addr, addrlen, flags);
+                add_hosts_entry(cache, &addr, addrlen, flags, index);
               }
           }
         else
@@ -566,7 +597,7 @@ static void read_hostsfile(char *filename, int opts, char *buff, char *domain_su
   syslog(LOG_INFO, "read %s - %d addresses", filename, count);
 }
            
-void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
+void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile *addn_hosts)
 {
   struct crec *cache, **up, *tmp;
   int i;
@@ -603,11 +634,11 @@ void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
 
   if (!(opts & OPT_NO_HOSTS))
     read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
-  if (addn_hosts)
+  while (addn_hosts)
     {
-      read_hostsfile(addn_hosts, opts, buff, domain_suffix, 1);
-      addn_file = addn_hosts;
-    }
+      read_hostsfile(addn_hosts->fname, opts, buff, domain_suffix, addn_hosts->index);
+      addn_hosts = addn_hosts->next;
+    }  
 } 
 
 void cache_unhash_dhcp(void)
@@ -633,7 +664,8 @@ void cache_unhash_dhcp(void)
   dhcp_inuse = NULL;
 }
 
-void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd) 
+void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, 
+                         struct in_addr *host_address, time_t ttd) 
 {
   struct crec *crec;
   unsigned short flags =  F_DHCP | F_FORWARD | F_IPV4 | F_REVERSE;
@@ -645,22 +677,19 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
     {
       if (crec->flags & F_HOSTS)
        {
-         if (crec->addr.addr.addr4.s_addr != host_address->s_addr)
-           syslog(LOG_WARNING, "not naming DHCP lease for %s because it clashes with an /etc/hosts entry.", host_name);
-         return;
-       }
-      else if (!(crec->flags & F_DHCP))
-       {
-         if (!(crec->flags & F_NEG))
+         if (crec->addr.addr.addr.addr4.s_addr != host_address->s_addr)
            {
-             syslog(LOG_WARNING, "not naming DHCP lease for %s because it clashes with a cached name.", host_name);
-             return;
+             strcpy(daemon->namebuff, inet_ntoa(crec->addr.addr.addr.addr4));
+             syslog(LOG_WARNING, 
+                    "not giving name %s to the DHCP lease of %s because"
+                    "the name exists in %s with address %s", 
+                    host_name, inet_ntoa(*host_address),
+                    record_source(daemon->addn_hosts, crec->uid), daemon->namebuff);
            }
-               
-         /* name may have been searched for before being allocated to DHCP and 
-            therefore got a negative cache entry. If so delete it and continue. */
-         cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
+         return;
        }
+      else if (!(crec->flags & F_DHCP))
+       cache_scan_free(host_name, NULL, 0, F_IPV4 | F_FORWARD);
     }
  
   if ((crec = cache_find_by_addr(NULL, (struct all_addr *)host_address, 0, F_IPV4)))
@@ -684,7 +713,7 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
        crec->flags |= F_IMMORTAL;
       else
        crec->ttd = ttd;
-      crec->addr.addr.addr4 = *host_address;
+      crec->addr.addr.addr.addr4 = *host_address;
       crec->name.namep = host_name;
       crec->prev = dhcp_inuse;
       dhcp_inuse = crec;
@@ -694,12 +723,12 @@ void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t
 
 
 
-void dump_cache(int debug, int cache_size)
+void dump_cache(struct daemon *daemon)
 {
   syslog(LOG_INFO, "cache size %d, %d/%d cache insertions re-used unexpired cache entries.", 
-        cache_size, cache_live_freed, cache_inserted); 
+        daemon->cachesize, cache_live_freed, cache_inserted); 
   
-  if (debug)
+  if (daemon->options & (OPT_DEBUG | OPT_LOG))
     {
       struct crec *cache ;
       char addrbuff[ADDRSTRLEN];
@@ -711,14 +740,21 @@ void dump_cache(int debug, int cache_size)
          {
            if ((cache->flags & F_NEG) && (cache->flags & F_FORWARD))
              addrbuff[0] = 0;
+           else if (cache->flags & F_CNAME) 
+             {
+               addrbuff[0] = 0;
+               addrbuff[ADDRSTRLEN-1] = 0;
+               if (!is_outdated_cname_pointer(cache))
+                 strncpy(addrbuff, cache_get_name(cache->addr.cname.cache), ADDRSTRLEN);
+             }
 #ifdef HAVE_IPV6
            else if (cache->flags & F_IPV4)
-             inet_ntop(AF_INET, &cache->addr, addrbuff, ADDRSTRLEN);
+             inet_ntop(AF_INET, &cache->addr.addr, addrbuff, ADDRSTRLEN);
            else if (cache->flags & F_IPV6)
-             inet_ntop(AF_INET6, &cache->addr, addrbuff, ADDRSTRLEN);
+             inet_ntop(AF_INET6, &cache->addr.addr, addrbuff, ADDRSTRLEN);
 #else
             else 
-             strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr4));
+             strcpy(addrbuff, inet_ntoa(cache->addr.addr.addr.addr4));
 #endif
            syslog(LOG_DEBUG, 
 #ifdef HAVE_BROKEN_RTC
@@ -729,6 +765,7 @@ void dump_cache(int debug, int cache_size)
                   cache_get_name(cache), addrbuff,
                   cache->flags & F_IPV4 ? "4" : "",
                   cache->flags & F_IPV6 ? "6" : "",
+                  cache->flags & F_CNAME ? "C" : "",
                   cache->flags & F_FORWARD ? "F" : " ",
                   cache->flags & F_REVERSE ? "R" : " ",
                   cache->flags & F_IMMORTAL ? "I" : " ",
@@ -736,19 +773,34 @@ void dump_cache(int debug, int cache_size)
                   cache->flags & F_NEG ? "N" : " ",
                   cache->flags & F_NXDOMAIN ? "X" : " ",
                   cache->flags & F_HOSTS ? "H" : " ",
-                  cache->flags & F_ADDN ? "A" : " ",
 #ifdef HAVE_BROKEN_RTC
-                  cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd) ;
+                  cache->flags & F_IMMORTAL ? 0: (unsigned long)cache->ttd
 #else
-                  cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd))) ;
+                  cache->flags & F_IMMORTAL ? "\n" : ctime(&(cache->ttd)) 
 #endif
-         }
-     }
+                  );
+         }  
+    } 
 }
 
+static char *record_source(struct hostsfile *addn_hosts, int index)
+{
+  char *source = HOSTSFILE;
+  while (addn_hosts)
+    { 
+      if (addn_hosts->index == index)
+       {
+         source = addn_hosts->fname;
+         break;
+       }
+      addn_hosts = addn_hosts->next;
+    }
 
+  return source;
+}
 
-void log_query(unsigned short flags, char *name, struct all_addr *addr, unsigned short type)
+void log_query(unsigned short flags, char *name, struct all_addr *addr, 
+              unsigned short type, struct hostsfile *addn_hosts, int index)
 {
   char *source;
   char *verb = "is";
@@ -759,7 +811,7 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, unsigned
     return;
   
   strcpy(types, " ");
-
+  
   if (flags & F_NEG)
     {
       if (flags & F_REVERSE)
@@ -780,6 +832,8 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, unsigned
       else if (flags & F_IPV6)
        strcat(addrbuff, "-IPv6");
     }
+  else if (flags & F_CNAME)
+    strcpy(addrbuff, "<CNAME>");
   else
 #ifdef HAVE_IPV6
     inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
@@ -787,17 +841,12 @@ void log_query(unsigned short flags, char *name, struct all_addr *addr, unsigned
 #else
     strcpy(addrbuff, inet_ntoa(addr->addr.addr4));  
 #endif
-  
+    
   if (flags & F_DHCP)
     source = "DHCP";
   else if (flags & F_HOSTS)
-    {
-      if (flags & F_ADDN)
-       source = addn_file;
-      else
-       source = HOSTSFILE;
-    }
-   else if (flags & F_CONFIG)
+    source = record_source(addn_hosts, index);
+  else if (flags & F_CONFIG)
     source = "config";
   else if (flags & F_UPSTREAM)
     source = "reply";
index 756093c3aff351bca135cddfa85826c7a2ecc57c..4f12daa9e47042d690fd58053481e32a5363e35f 100644 (file)
@@ -12,7 +12,7 @@
 
 /* Author's email: simon@thekelleys.org.uk */
 
-#define VERSION "2.15"
+#define VERSION "2.16"
 
 #define FTABSIZ 150 /* max number of outstanding requests */
 #define MAX_PROCS 20 /* max no children for TCP requests */
@@ -156,7 +156,7 @@ HAVE_PSELECT
    If your C library implements pselect, define this.
 
 HAVE_BPF
-   If your OS implements Berkeley PAcket filter, define this.
+   If your OS implements Berkeley Packet filter, define this.
 
 NOTES:
    For Linux you should define 
@@ -176,10 +176,12 @@ NOTES:
    you should NOT define  
      HAVE_LINUX_IPV6_PROC 
    and you MAY define  
-     HAVE_ARC4RANDOM - OpenBSD and FreeBSD 
-     HAVE_DEV_URANDOM - OpenBSD and FreeBSD
-     HAVE_DEV_RANDOM - FreeBSD (OpenBSD with hardware random number generator)
-     HAVE_GETOPT_LONG - only if you link GNU getopt. 
+     HAVE_ARC4RANDOM - OpenBSD and FreeBSD and NetBSD version 2.0 or later
+     HAVE_DEV_URANDOM - OpenBSD and FreeBSD and NetBSD
+     HAVE_DEV_RANDOM - FreeBSD  and NetBSD 
+                       (OpenBSD with hardware random number generator)
+     HAVE_GETOPT_LONG - NetBSD 
+                       (FreeBSD and OpenBSD only if you link GNU getopt) 
 
 */
 
@@ -250,9 +252,6 @@ typedef unsigned long in_addr_t;
 #endif
 #endif
 
-/* #elif defined(__OpenBSD__)
-#error The sockets API in OpenBSD does not provide facilities required by dnsmasq
-*/
 #elif defined(__FreeBSD__) || defined(__OpenBSD__)
 #undef HAVE_LINUX_IPV6_PROC
 #undef HAVE_GETOPT_LONG
@@ -275,16 +274,16 @@ typedef unsigned long in_addr_t;
 #define BIND_8_COMPAT
 /* Define before sys/socket.h is included so we get socklen_t */
 #define _BSD_SOCKLEN_T_
-/* The three below are not defined in Mac OS X arpa/nameserv.h */
+/* This is not defined in Mac OS X arpa/nameserv.h */
 #define IN6ADDRSZ 16
  
 #elif defined(__NetBSD__)
 #undef HAVE_LINUX_IPV6_PROC
-#undef HAVE_GETOPT_LONG
+#define HAVE_GETOPT_LONG
 #undef HAVE_ARC4RANDOM
 #define HAVE_RANDOM
-#undef HAVE_DEV_URANDOM
-#undef HAVE_DEV_RANDOM
+#define HAVE_DEV_URANDOM
+#define HAVE_DEV_RANDOM
 #define HAVE_SOCKADDR_SA_LEN
 #undef HAVE_PSELECT
 #define HAVE_BPF
index 05b7e267b9d9ccafe8b9a8a8b006e370909bff46..124ca539da1e845f32d337dace4ae17107c33467 100644 (file)
@@ -18,13 +18,14 @@ void dhcp_init(struct daemon *daemon)
 {
   int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
   struct sockaddr_in saddr;
-  int oneopt = 1, zeroopt = 0;
+  int flags, oneopt = 1, zeroopt = 0;
   struct dhcp_config *configs, *cp;
 
   if (fd == -1)
     die ("cannot create DHCP socket : %s", NULL);
   
-  if (
+  if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+      fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
 #if defined(IP_PKTINFO)
       setsockopt(fd, SOL_IP, IP_PKTINFO, &oneopt, sizeof(oneopt)) == -1 ||
 #elif defined(IP_RECVIF)
@@ -72,6 +73,8 @@ void dhcp_init(struct daemon *daemon)
      socket receive buffer size to one to avoid that. (zero is
      rejected as non-sensical by some BSD kernels) */
   if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETHERTYPE_IP))) == -1 ||
+      (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+      fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
       setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &oneopt, sizeof(oneopt)) == -1)
     die("cannot create DHCP packet socket: %s. "
        "Is CONFIG_PACKET enabled in your kernel?", NULL);
@@ -160,7 +163,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
 #else
   {
     struct iname *name;
-    for (name = daemon->if_names; names->isloop; names = names->next);
+    for (name = daemon->if_names; name->isloop; name = name->next);
     strcpy(ifr.ifr_name, name->name);
   }
 #endif
@@ -257,7 +260,7 @@ void dhcp_packet(struct daemon *daemon, time_t now)
   lease_prune(NULL, now); /* lose any expired leases */
   newlen = dhcp_reply(daemon, iface_addr, ifr.ifr_name, sz, now);
   lease_update_file(0, now);
-  lease_update_dns();
+  lease_update_dns(daemon);
   
   if (newlen == 0)
     return;
@@ -283,7 +286,9 @@ void dhcp_packet(struct daemon *daemon, time_t now)
          dest.sin_addr = mess->ciaddr;
        }
       
-      sendto(daemon->dhcpfd, mess, newlen, 0, (struct sockaddr *)&dest, sizeof(dest));
+      while(sendto(daemon->dhcpfd, mess, newlen, 0, 
+                  (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
+           retry_send());
     }
   else
     {
@@ -353,7 +358,8 @@ void dhcp_packet(struct daemon *daemon, time_t now)
        iov[0].iov_len = sizeof(struct ether_header);
        iov[1].iov_base = (char *)rawpacket;
        iov[1].iov_len = ntohs(rawpacket->ip.ip_len);
-       writev(daemon->dhcp_raw_fd, iov, 2);
+       while (writev(daemon->dhcp_raw_fd, iov, 2) == -1 &&
+              errno == EINTR);
 #else
        struct sockaddr_ll dest;
        
@@ -362,9 +368,9 @@ void dhcp_packet(struct daemon *daemon, time_t now)
        dest.sll_ifindex = iface_index;
        dest.sll_protocol = htons(ETHERTYPE_IP);
        memcpy(dest.sll_addr, hwdest, ETHER_ADDR_LEN); 
-       sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len), 
-              0, (struct sockaddr *)&dest, sizeof(dest));
-       
+       while (sendto(daemon->dhcp_raw_fd, rawpacket, ntohs(rawpacket->ip.ip_len), 
+                     0, (struct sockaddr *)&dest, sizeof(dest)) == -1 &&
+              errno == EINTR);
 #endif
       }
     }
@@ -624,7 +630,7 @@ void dhcp_update_configs(struct dhcp_config *configs)
        (crec = cache_find_by_name(NULL, config->hostname, 0, F_IPV4)) &&
        (crec->flags & F_HOSTS))
       {
-       config->addr = crec->addr.addr.addr4;
+       config->addr = crec->addr.addr.addr.addr4;
        config->flags |= CONFIG_ADDR;
       }
 }
index 0d2503670a5fd5c9f7cfc3a39d40f34ee3f52519..cec4661211a3dbd5afcd10c65a652817bf77be4f 100644 (file)
@@ -314,7 +314,7 @@ int main (int argc, char **argv)
              dhcp_update_configs(daemon->dhcp_conf);
              lease_update_from_configs(daemon->dhcp_conf, daemon->domain_suffix); 
              lease_update_file(0, now); 
-             lease_update_dns();
+             lease_update_dns(daemon);
            }
          if (daemon->resolv_files && (daemon->options & OPT_NO_POLL))
            {
@@ -326,7 +326,7 @@ int main (int argc, char **argv)
       
       if (sigusr1)
        {
-         dump_cache(daemon->options & (OPT_DEBUG | OPT_LOG), daemon->cachesize);
+         dump_cache(daemon);
          sigusr1 = 0;
        }
       
@@ -379,7 +379,7 @@ int main (int argc, char **argv)
 
 #ifdef HAVE_ISC_READER
          if (daemon->lease_file && !daemon->dhcp)
-           load_dhcp(daemon->lease_file, daemon->domain_suffix, now, daemon->namebuff);
+           load_dhcp(daemon, now);
 #endif
 
          if (!(daemon->options & OPT_NO_POLL))
@@ -638,43 +638,45 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr)
   
   setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
 
-  if (sendto(daemon->dhcp_icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0, 
-            (struct sockaddr *)&saddr, sizeof(saddr)) == sizeof(struct icmp))
-    for (now = start = dnsmasq_time(daemon->uptime_fd); difftime(now, start) < 3.0;)
-      {
-       struct timeval tv;
-       fd_set rset;
-       struct sockaddr_in faddr;
-       int maxfd, len = sizeof(faddr);
-
-       tv.tv_usec = 250000;
-       tv.tv_sec = 0; 
-
-       FD_ZERO(&rset);
-       FD_SET(daemon->dhcp_icmp_fd, &rset);
-       maxfd = set_dns_listeners(daemon, &rset, daemon->dhcp_icmp_fd);
+  while (sendto(daemon->dhcp_icmp_fd, (char *)&packet.icmp, sizeof(struct icmp), 0, 
+               (struct sockaddr *)&saddr, sizeof(saddr)) == -1 &&
+        retry_send());
+  
+  for (now = start = dnsmasq_time(daemon->uptime_fd); difftime(now, start) < 3.0;)
+    {
+      struct timeval tv;
+      fd_set rset;
+      struct sockaddr_in faddr;
+      int maxfd, len = sizeof(faddr);
+      
+      tv.tv_usec = 250000;
+      tv.tv_sec = 0; 
+      
+      FD_ZERO(&rset);
+      FD_SET(daemon->dhcp_icmp_fd, &rset);
+      maxfd = set_dns_listeners(daemon, &rset, daemon->dhcp_icmp_fd);
                
-       if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
-         FD_ZERO(&rset);
-       
-       now = dnsmasq_time(daemon->uptime_fd);
-       check_dns_listeners(daemon, &rset, now);
-       
-       if (FD_ISSET(daemon->dhcp_icmp_fd, &rset) &&
-           recvfrom(daemon->dhcp_icmp_fd, &packet, sizeof(packet), 0,
-                    (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
-           saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
-           packet.icmp.icmp_type == ICMP_ECHOREPLY &&
-           packet.icmp.icmp_seq == 0 &&
-           packet.icmp.icmp_id == id)
-         {
-           gotreply = 1;
-           break;
-         }
-      }
-
+      if (select(maxfd+1, &rset, NULL, NULL, &tv) < 0)
+       FD_ZERO(&rset);
+      
+      now = dnsmasq_time(daemon->uptime_fd);
+      check_dns_listeners(daemon, &rset, now);
+      
+      if (FD_ISSET(daemon->dhcp_icmp_fd, &rset) &&
+         recvfrom(daemon->dhcp_icmp_fd, &packet, sizeof(packet), 0,
+                  (struct sockaddr *)&faddr, &len) == sizeof(packet) &&
+         saddr.sin_addr.s_addr == faddr.sin_addr.s_addr &&
+         packet.icmp.icmp_type == ICMP_ECHOREPLY &&
+         packet.icmp.icmp_seq == 0 &&
+         packet.icmp.icmp_id == id)
+       {
+         gotreply = 1;
+         break;
+       }
+    }
+  
   opt = 1;
   setsockopt(daemon->dhcp_icmp_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt));
-
+  
   return gotreply;
 }
index c16169d288ecef6197ad3599e0c55246f7d75bb1..54646842ea2b8c4c676539319a7f16da357ac49c 100644 (file)
@@ -95,6 +95,7 @@
 #define OPT_ETHERS         16384
 #define OPT_RESOLV_DOMAIN  32768
 #define OPT_NO_FORK        65536
+#define OPT_AUTHORITATIVE  131072
 
 struct all_addr {
   union {
@@ -129,7 +130,14 @@ union bigname {
 struct crec { 
   struct crec *next, *prev, *hash_next;
   time_t ttd; /* time to die */
-  struct all_addr addr;
+  int uid; 
+  union {
+    struct all_addr addr;
+    struct {
+      struct crec *cache;
+      int uid;
+    } cname;
+  } addr;
   unsigned short flags;
   union {
     char sname[SMALLDNAME];
@@ -139,7 +147,7 @@ struct crec {
 };
 
 #define F_IMMORTAL  1
-#define F_CONFIG   2
+#define F_CONFIG    2
 #define F_REVERSE   4
 #define F_FORWARD   8
 #define F_DHCP      16 
@@ -152,7 +160,7 @@ struct crec {
 #define F_SERVER    2048
 #define F_NXDOMAIN  4096
 #define F_QUERY     8192
-#define F_ADDN      16384
+#define F_CNAME     16384
 #define F_NOERR     32768
 
 /* struct sockaddr is not large enough to hold any address,
@@ -227,6 +235,13 @@ struct resolvc {
   char *name;
 };
 
+/* adn-hosts parms from command-line */
+struct hostsfile {
+  struct hostsfile *next;
+  char *fname;
+  int index; /* matches to cache entries fro logging */
+};
+
 struct frec {
   union mysockaddr source;
   struct all_addr dest;
@@ -234,6 +249,7 @@ struct frec {
   unsigned int iface;
   unsigned short orig_id, new_id;
   int fd;
+  unsigned int crc;
   time_t time;
   struct frec *next;
 };
@@ -340,7 +356,7 @@ struct daemon {
   int cachesize;
   int port, query_port;
   unsigned long local_ttl;
-  char *addn_hosts;
+  struct hostsfile *addn_hosts;
   struct dhcp_context *dhcp;
   struct dhcp_config *dhcp_conf;
   struct dhcp_opt *dhcp_opts;
@@ -369,7 +385,8 @@ struct daemon {
 
 /* cache.c */
 void cache_init(int cachesize, int log);
-void log_query(unsigned short flags, char *name, struct all_addr *addr, unsigned short type);
+void log_query(unsigned short flags, char *name, struct all_addr *addr, 
+              unsigned short type, struct hostsfile *addn_hosts, int index);
 struct crec *cache_find_by_addr(struct crec *crecp,
                                struct all_addr *addr, time_t now, 
                                unsigned short prot);
@@ -377,12 +394,12 @@ struct crec *cache_find_by_name(struct crec *crecp,
                                char *name, time_t now, unsigned short  prot);
 void cache_end_insert(void);
 void cache_start_insert(void);
-void cache_insert(char *name, struct all_addr *addr,
-                 time_t now, unsigned long ttl, unsigned short flags);
-void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts);
-void cache_add_dhcp_entry(char *host_name, struct in_addr *host_address, time_t ttd);
+struct crec *cache_insert(char *name, struct all_addr *addr,
+                         time_t now, unsigned long ttl, unsigned short flags);
+void cache_reload(int opts, char *buff, char *domain_suffix, struct hostsfile  *addn_hosts);
+void cache_add_dhcp_entry(struct daemon *daemon, char *host_name, struct in_addr *host_address, time_t ttd);
 void cache_unhash_dhcp(void);
-void dump_cache(int debug, int size);
+void dump_cache(struct daemon *daemon);
 char *cache_get_name(struct crec *crecp);
 
 /* rfc1035.c */
@@ -392,14 +409,14 @@ int setup_reply(HEADER *header, unsigned int qlen,
                struct all_addr *addrp, unsigned short flags,
                unsigned long local_ttl);
 void extract_addresses(HEADER *header, unsigned int qlen, char *namebuff, 
-                      time_t now, struct doctor *doctors);
-void extract_neg_addrs(HEADER *header, unsigned int qlen, char *namebuff, time_t now, unsigned short flags);
+                      time_t now, struct daemon *daemon);
 int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon *daemon, 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);
+unsigned int questions_crc(HEADER *header, unsigned int plen);
 int resize_packet(HEADER *header, unsigned int plen, 
                  unsigned char *pheader, unsigned int hlen);
 
@@ -417,6 +434,7 @@ int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
 int hostname_isequal(unsigned char *a, unsigned char *b);
 time_t dnsmasq_time(int fd);
 int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
+int retry_send(void);
 
 /* option.c */
 struct daemon *read_opts (int argc, char **argv);
@@ -452,7 +470,7 @@ struct dhcp_config *config_find_by_address(struct dhcp_config *configs, struct i
 
 /* lease.c */
 void lease_update_file(int force, time_t now);
-void lease_update_dns(void);
+void lease_update_dns(struct daemon *daemon);
 void lease_init(struct daemon *daemon, time_t now);
 struct dhcp_lease *lease_allocate(unsigned char *clid, int clid_len, struct in_addr addr);
 void lease_set_hwaddr(struct dhcp_lease *lease, unsigned char *hwaddr);
@@ -471,6 +489,6 @@ int icmp_ping(struct daemon *daemon, struct in_addr addr);
 
 /* isc.c */
 #ifdef HAVE_ISC_READER
-void load_dhcp(char *file, char *suffix, time_t now, char *hostname);
+void load_dhcp(struct daemon *daemon, time_t now);
 #endif
 
index 3731bdb46622e80f5a40c2209f192ef8e0943859..799c01b7fb43738f1837614525f24542272b191c 100644 (file)
@@ -19,7 +19,8 @@ static struct frec *frec_list;
 static struct frec *get_new_frec(time_t now);
 static struct frec *lookup_frec(unsigned short id);
 static struct frec *lookup_frec_by_sender(unsigned short id,
-                                         union mysockaddr *addr);
+                                         union mysockaddr *addr,
+                                         unsigned int crc);
 static unsigned short get_id(void);
 
 /* May be called more than once. */
@@ -104,13 +105,19 @@ static void send_from(int fd, int nowild, char *packet, int len,
     }
 #endif
   
-  /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
-     by returning EINVAL from sendmsg. In that case, try again without setting the
-     source address, since it will nearly alway be correct anyway.  IPv6 stinks. */
-  if (sendmsg(fd, &msg, 0) == -1 && errno == EINVAL)
+ retry:
+  if (sendmsg(fd, &msg, 0) == -1)
     {
-      msg.msg_controllen = 0;
-      sendmsg(fd, &msg, 0);
+      /* certain Linux kernels seem to object to setting the source address in the IPv6 stack
+        by returning EINVAL from sendmsg. In that case, try again without setting the
+        source address, since it will nearly alway be correct anyway.  IPv6 stinks. */
+      if (errno == EINVAL && msg.msg_controllen)
+       {
+         msg.msg_controllen = 0;
+         goto retry;
+       }
+      if (retry_send())
+       goto retry;
     }
 }
           
@@ -185,9 +192,9 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
   if (flags & ~(F_NOERR | F_NXDOMAIN)) /* flags set here means a literal found */
     {
       if (flags & F_QUERY)
-       log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0);
+       log_query(F_CONFIG | F_FORWARD | F_NEG, qdomain, NULL, 0, NULL, 0);
       else
-       log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0);
+       log_query(F_CONFIG | F_FORWARD | flags, qdomain, *addrpp, 0, NULL, 0);
     }
   else if (qtype && (daemon->options & OPT_NODOTS_LOCAL) && !strchr(qdomain, '.'))
     flags = F_NXDOMAIN;
@@ -196,7 +203,7 @@ static unsigned short search_servers(struct daemon *daemon, time_t now, struct a
     flags = F_NOERR;
 
   if (flags == F_NXDOMAIN || flags == F_NOERR)
-    log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0);
+    log_query(F_CONFIG | F_FORWARD | F_NEG | qtype | (flags & F_NXDOMAIN), qdomain, NULL, 0, NULL, 0);
 
   return  flags;
 }
@@ -213,11 +220,12 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
   unsigned short flags = 0;
   unsigned short gotname = extract_request(header, (unsigned int)plen, daemon->namebuff, NULL);
   struct server *start = NULL;
+  unsigned int crc = questions_crc(header,(unsigned int)plen);
   
   /* may be  recursion not speced or no servers available. */
   if (!header->rd || !daemon->servers)
     forward = NULL;
-  else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
+  else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, crc)))
     {
       /* retry on existing query, send to all available servers  */
       domain = forward->sentto->domain;
@@ -260,6 +268,7 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
          forward->new_id = get_id();
          forward->fd = udpfd;
          forward->orig_id = ntohs(header->id);
+         forward->crc = crc;
          header->id = htons(forward->new_id);
        }
     }
@@ -280,22 +289,29 @@ static void forward_query(struct daemon *daemon, int udpfd, union mysockaddr *ud
             must be NULL also. */
          
          if (type == (start->flags & SERV_TYPE) &&
-             (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
+             (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)) &&
+             !(start->flags & SERV_LITERAL_ADDRESS))
            {
-             if (!(start->flags & SERV_LITERAL_ADDRESS) &&
-                 sendto(start->sfd->fd, (char *)header, plen, 0,
+             if (sendto(start->sfd->fd, (char *)header, plen, 0,
                         &start->addr.sa,
-                        sa_len(&start->addr)) != -1)
+                        sa_len(&start->addr)) == -1)
+               {
+                 if (retry_send())
+                   continue;
+               }
+             else
                {
                  if (!gotname)
                    strcpy(daemon->namebuff, "query");
                  if (start->addr.sa.sa_family == AF_INET)
                    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&start->addr.in.sin_addr, 0); 
+                             (struct all_addr *)&start->addr.in.sin_addr, 0,
+                             NULL, 0); 
 #ifdef HAVE_IPV6
                  else
                    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&start->addr.in6.sin6_addr, 0);
+                             (struct all_addr *)&start->addr.in6.sin6_addr, 0,
+                             NULL, 0);
 #endif 
                  forwarded = 1;
                  forward->sentto = start;
@@ -330,7 +346,7 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
                         union mysockaddr *serveraddr, unsigned int n)
 {
   unsigned char *pheader, *sizep;
-  unsigned int plen;
+  unsigned int plen, munged = 0;
    
   /* If upstream is advertising a larger UDP packet size
         than we allow, trim it so that we don't get overlarge
@@ -365,45 +381,40 @@ static int process_reply(struct daemon *daemon, HEADER *header, time_t now,
   if (header->opcode != QUERY || (header->rcode != NOERROR && header->rcode != NXDOMAIN))
     return n;
   
-  if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
+  if (daemon->bogus_addr && header->rcode != NXDOMAIN &&
+      check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
     {
-      if (!(daemon->bogus_addr && 
-           check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now)))
-       extract_addresses(header, n, daemon->namebuff, now, daemon->doctors);
+      munged = 1;
+      header->rcode = NXDOMAIN;
+      header->aa = 0;
     }
-  else
+  else 
     {
-      unsigned short flags = F_NEG;
-      int munged = 0;
-
-      if (header->rcode == NXDOMAIN)
+      if (header->rcode == NXDOMAIN && 
+         extract_request(header, n, daemon->namebuff, NULL) &&
+         check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
        {
          /* 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,
             since we know that the domain exists, even if upstream doesn't */
-         if (extract_request(header, n, daemon->namebuff, NULL) &&
-             check_for_local_domain(daemon->namebuff, now, daemon->mxnames))
-           {
-             munged = 1;
-             header->rcode = NOERROR;
-           }
-         else
-           flags |= F_NXDOMAIN;
-       }
-      
-      if (!(daemon->options & OPT_NO_NEG))
-       extract_neg_addrs(header, n, daemon->namebuff, now, flags);
-         
-      /* do this after extract_neg_addrs. Ensure NODATA reply and remove
-        nameserver info. */
-      if (munged)
-       {
-         header->ancount = htons(0);
-         header->nscount = htons(0);
-         header->arcount = htons(0);
+         munged = 1;
+         header->aa = 1;
+         header->rcode = NOERROR;
        }
+  
+      extract_addresses(header, n, daemon->namebuff, now, daemon);
     }
-
+  
+  /* do this after extract_addresses. Ensure NODATA reply and remove
+     nameserver info. */
+  
+  if (munged)
+    {
+      header->ancount = htons(0);
+      header->nscount = htons(0);
+      header->arcount = htons(0);
+    }
+  
   /* the bogus-nxdomain stuff, doctor and NXDOMAIN->NODATA munging can all elide
      sections of the packet. Find the new length here and put back pseudoheader
      if it was removed. */
@@ -429,7 +440,9 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
 #endif
   
   header = (HEADER *)daemon->packet;
-  if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
+  forward = lookup_frec(ntohs(header->id));
+  
+  if (n >= (int)sizeof(HEADER) && header->qr && forward)
     {
       /* find good server by address if possible, otherwise assume the last one we sent to */ 
       if ((forward->sentto->flags & SERV_TYPE) == 0)
@@ -448,7 +461,8 @@ void reply_query(struct serverfd *sfd, struct daemon *daemon, time_t now)
       if ((n = process_reply(daemon, header, now, &serveraddr, (unsigned int)n)))
        {
          header->id = htons(forward->orig_id);
-         send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n, 
+         header->ra = 1; /* recursion if available */
+send_from(forward->fd, daemon->options & OPT_NOWILD, daemon->packet, n, 
                    &forward->source, &forward->dest, forward->iface);
          forward->new_id = 0; /* cancel */
        }
@@ -593,11 +607,11 @@ void receive_query(struct listener *listen, struct daemon *daemon, time_t now)
     {
       if (listen->family == AF_INET) 
        log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                 (struct all_addr *)&source_addr.in.sin_addr, type);
+                 (struct all_addr *)&source_addr.in.sin_addr, type, NULL, 0);
 #ifdef HAVE_IPV6
       else
        log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                 (struct all_addr *)&source_addr.in6.sin6_addr, type);
+                 (struct all_addr *)&source_addr.in6.sin6_addr, type, NULL, 0);
 #endif
     }
 
@@ -625,16 +639,8 @@ static int read_write(int fd, char *packet, int size, int rw)
        return 0;
       else if (n == -1)
        {
-         if (errno == EINTR)
+         if (retry_send())
            goto retry;
-         else if (errno == EAGAIN)
-           {
-             struct timespec waiter;
-             waiter.tv_sec = 0;
-             waiter.tv_nsec = 10000;
-             nanosleep(&waiter, NULL);
-             goto retry;
-           }
          else
            return 0;
        }
@@ -678,11 +684,11 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
            {
              if (peer_addr.sa.sa_family == AF_INET) 
                log_query(F_QUERY | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                         (struct all_addr *)&peer_addr.in.sin_addr, qtype);
+                         (struct all_addr *)&peer_addr.in.sin_addr, qtype, NULL, 0);
 #ifdef HAVE_IPV6
              else
                log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                         (struct all_addr *)&peer_addr.in6.sin6_addr, qtype);
+                         (struct all_addr *)&peer_addr.in6.sin6_addr, qtype, NULL, 0);
 #endif
            }
        }
@@ -763,11 +769,11 @@ char *tcp_request(struct daemon *daemon, int confd, time_t now)
                    strcpy(daemon->namebuff, "query");
                  if (last_server->addr.sa.sa_family == AF_INET)
                    log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&last_server->addr.in.sin_addr, 0); 
+                             (struct all_addr *)&last_server->addr.in.sin_addr, 0, NULL, 0); 
 #ifdef HAVE_IPV6
                  else
                    log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff, 
-                             (struct all_addr *)&last_server->addr.in6.sin6_addr, 0);
+                             (struct all_addr *)&last_server->addr.in6.sin6_addr, 0, NULL, 0);
 #endif 
                  
                  /* There's no point in updating the cache, since this process will exit and
@@ -857,13 +863,15 @@ static struct frec *lookup_frec(unsigned short id)
 }
 
 static struct frec *lookup_frec_by_sender(unsigned short id,
-                                         union mysockaddr *addr)
+                                         union mysockaddr *addr,
+                                         unsigned int crc)
 {
   struct frec *f;
   
   for(f = frec_list; f; f = f->next)
     if (f->new_id &&
        f->orig_id == id && 
+       f->crc == crc &&
        sockaddr_isequal(&f->source, addr))
       return f;
    
index 203bcb14658687722245b1cb5da54ecb8ee0d29b..0f21c3bee8787190b4ff6582e32b1b760e4836bc 100644 (file)
--- a/src/isc.c
+++ b/src/isc.c
@@ -55,8 +55,9 @@ static int next_token (char *token, int buffsize, FILE * fp)
   return count ? 1 : 0;
 }
 
-void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
+void load_dhcp(struct daemon *daemon, time_t now)
 {
+  char *hostname = daemon->namebuff;
   char token[MAXTOK], *dot;
   struct in_addr host_address;
   time_t ttd, tts;
@@ -64,10 +65,10 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
   struct isc_lease *lease, *tmp, **up;
   struct stat statbuf;
 
-  if (stat(file, &statbuf) == -1)
+  if (stat(daemon->lease_file, &statbuf) == -1)
     {
       if (!logged_lease)
-       syslog(LOG_WARNING, "failed to access %s: %m", file);
+       syslog(LOG_WARNING, "failed to access %s: %m", daemon->lease_file);
       logged_lease = 1;
       return;
     }
@@ -81,13 +82,13 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
   lease_file_size = statbuf.st_size;
   lease_file_inode = statbuf.st_ino;
   
-  if (!(fp = fopen (file, "r")))
+  if (!(fp = fopen (daemon->lease_file, "r")))
     {
-      syslog (LOG_ERR, "failed to load %s: %m", file);
+      syslog (LOG_ERR, "failed to load %s: %m", daemon->lease_file);
       return;
     }
   
-  syslog (LOG_INFO, "reading %s", file);
+  syslog (LOG_INFO, "reading %s", daemon->lease_file);
 
   while ((next_token(token, MAXTOK, fp)))
     {
@@ -109,7 +110,7 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
                            if (!canonicalise(hostname))
                              {
                                *hostname = 0;
-                               syslog(LOG_ERR, "bad name in %s", file); 
+                               syslog(LOG_ERR, "bad name in %s", daemon->lease_file); 
                              }
                        }
                       else if ((strcmp(token, "ends") == 0) ||
@@ -168,7 +169,7 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
 
                  if ((dot = strchr(hostname, '.')))
                    { 
-                     if (!suffix || hostname_isequal(dot+1, suffix))
+                     if (!daemon->domain_suffix || hostname_isequal(dot+1, daemon->domain_suffix))
                        {
                          syslog(LOG_WARNING, 
                                 "Ignoring DHCP lease for %s because it has an illegal domain part", 
@@ -198,11 +199,12 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
                        {
                          leases = lease;
                          strcpy(lease->name, hostname);
-                         if (suffix && (lease->fqdn = malloc(strlen(hostname) + strlen(suffix) + 2)))
+                         if (daemon->domain_suffix && 
+                             (lease->fqdn = malloc(strlen(hostname) + strlen(daemon->domain_suffix) + 2)))
                            {
                              strcpy(lease->fqdn, hostname);
                              strcat(lease->fqdn, ".");
-                             strcat(lease->fqdn, suffix);
+                             strcat(lease->fqdn, daemon->domain_suffix);
                            }
                        }
                    }
@@ -235,8 +237,8 @@ void load_dhcp(char *file, char *suffix, time_t now, char *hostname)
 
   for (lease = leases; lease; lease = lease->next)
     {
-      cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
-      cache_add_dhcp_entry(lease->name, &lease->addr, lease->expires);
+      cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires);
+      cache_add_dhcp_entry(daemon, lease->name, &lease->addr, lease->expires);
     }
 }
 
index eff740177d5ddc6d5ec5503b8d430b9fb2f749f6..e0d2948d760b3841e55a3071555f028bd12a4e37 100644 (file)
@@ -167,7 +167,7 @@ void lease_update_file(int force, time_t now)
     }
 }
 
-void lease_update_dns(void)
+void lease_update_dns(struct daemon *daemon)
 {
   struct dhcp_lease *lease;
   
@@ -177,8 +177,8 @@ void lease_update_dns(void)
       
       for (lease = leases; lease; lease = lease->next)
        {
-         cache_add_dhcp_entry(lease->fqdn, &lease->addr, lease->expires);
-         cache_add_dhcp_entry(lease->hostname, &lease->addr, lease->expires);
+         cache_add_dhcp_entry(daemon, lease->fqdn, &lease->addr, lease->expires);
+         cache_add_dhcp_entry(daemon, lease->hostname, &lease->addr, lease->expires);
        }
       
       dns_dirty = 0;
index 941f033b647189cf15532bee323caef318d983e1..034a5f6ac0b06211c2070a531029e93d14d1e54c 100644 (file)
@@ -256,6 +256,8 @@ static int create_ipv6_listener(struct listener **link, int port)
       setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
       setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
       setsockopt(tcpfd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 ||
+      (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+      fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
       (flags = fcntl(tcpfd, F_GETFL, 0)) == -1 ||
       fcntl(tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
 #ifdef IPV6_RECVPKTINFO
@@ -321,6 +323,8 @@ struct listener *create_wildcard_listeners(int port)
       !create_ipv6_listener(&l6, port) ||
 #endif
       setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
+      (flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+      fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
 #if defined(IP_PKTINFO) 
       setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
@@ -373,6 +377,8 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
            /* See Stevens 16.6 */
            (flags = fcntl(new->tcpfd, F_GETFL, 0)) == -1 ||
            fcntl(new->tcpfd, F_SETFL, flags | O_NONBLOCK) == -1 ||
+           (flags = fcntl(new->fd, F_GETFL, 0)) == -1 ||
+           fcntl(new->fd, F_SETFL, flags | O_NONBLOCK) == -1 ||
            bind(new->tcpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
            bind(new->fd, &iface->addr.sa, sa_len(&iface->addr)) == -1 ||
            listen(new->tcpfd, 5) == -1)
@@ -385,7 +391,8 @@ struct listener *create_bound_listeners(struct irec *interfaces, int port)
 struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
 {
   struct serverfd *sfd;
-  
+  int flags;
+
   /* may have a suitable one already */
   for (sfd = *sfds; sfd; sfd = sfd->next )
     if (sockaddr_isequal(&sfd->source_addr, addr))
@@ -402,7 +409,9 @@ struct serverfd *allocate_sfd(union mysockaddr *addr, struct serverfd **sfds)
       return NULL;
     }
   
-  if (bind(sfd->fd, (struct sockaddr *)addr, sa_len(addr)) == -1)
+  if (bind(sfd->fd, (struct sockaddr *)addr, sa_len(addr)) == -1 ||
+      (flags = fcntl(sfd->fd, F_GETFL, 0)) == -1 ||
+      fcntl(sfd->fd, F_SETFL, flags | O_NONBLOCK) == -1)
     {
       int errsave = errno; /* save error from bind. */
       close(sfd->fd);
index 3fe449284a0e31bad1fe7a4e8c48ea7effa349cc..a3b0dc810407f0edbc8a037d148c46be1f026ba7 100644 (file)
@@ -21,7 +21,7 @@ struct myoption {
   int val;
 };
 
-#define OPTSTRING "ZDNLERzowefnbvhdkqr: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:"
+#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:"
 
 static struct myoption opts[] = { 
   {"version", 0, 0, 'v'},
@@ -74,6 +74,7 @@ static struct myoption opts[] = {
   {"dhcp-userclass", 1, 0, 'j'},
   {"edns-packet-max", 1, 0, 'P'},
   {"keep-in-foreground", 0, 0, 'k'},
+  {"dhcp-authoritative", 0, 0, 'K'},
   {0, 0, 0, 0}
 };
 
@@ -91,6 +92,7 @@ static struct optflags optmap[] = {
   { 'n', OPT_NO_POLL },
   { 'd', OPT_DEBUG },
   { 'k', OPT_NO_FORK },
+  { 'K', OPT_AUTHORITATIVE },
   { 'o', OPT_ORDER },
   { 'R', OPT_NO_RESOLV },
   { 'E', OPT_EXPAND },
@@ -127,6 +129,7 @@ static char *usage =
 "-I, --except-interface=int          Specify interface(s) NOT to listen on.\n"
 "-j, --dhcp-userclass=<id>,<class>   Map DHCP user class to option set.\n"
 "-k, --keep-in-foreground            Do NOT fork into the background, do NOT run in debug mode.\n"
+"-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"
@@ -165,7 +168,7 @@ struct daemon *read_opts (int argc, char **argv)
   int option = 0, i;
   FILE *file_save = NULL, *f = NULL;
   char *file_name_save = NULL, *conffile = CONFFILE;
-  int conffile_set = 0;
+  int hosts_index = 1, conffile_set = 0;
   int line_save = 0, lineno = 0;
   opterr = 0;
   
@@ -398,15 +401,15 @@ struct daemon *read_opts (int argc, char **argv)
              break;
              
            case 'H':
-             if (daemon->addn_hosts)
-               {
-                 option = '?';
-                 problem = "only one addn hosts file allowed";
-               }
-             else
-               daemon->addn_hosts = safe_string_alloc(optarg);
-             break;
-             
+             {
+               struct hostsfile *new = safe_malloc(sizeof(struct hostsfile));
+               new->fname = safe_string_alloc(optarg);
+               new->index = hosts_index++;
+               new->next = daemon->addn_hosts;
+               daemon->addn_hosts = new;
+               break;
+             }
+
            case 's':
              if (strcmp (optarg, "#") == 0)
                daemon->options |= OPT_RESOLV_DOMAIN;
@@ -1009,10 +1012,11 @@ struct daemon *read_opts (int argc, char **argv)
                new->len = 0;
                new->is_addr = 0;
                new->netid = NULL;
+               new->val = NULL;
                                
                if ((comma = strchr(optarg, ',')))
                  {
-                   *comma = 0;
+                   *comma++ = 0;
                
                    for (cp = optarg; *cp; cp++)
                      if (!(*cp == ' ' || (*cp >='0' && *cp <= '9')))
@@ -1021,9 +1025,9 @@ struct daemon *read_opts (int argc, char **argv)
                    if (*cp)
                      {
                        new->netid = safe_string_alloc(optarg);
-                       optarg = comma + 1;
+                       optarg = comma;
                        if ((comma = strchr(optarg, ',')))
-                         *comma = 0;
+                         *comma++ = 0;
                      }
                  }
                
@@ -1031,118 +1035,190 @@ struct daemon *read_opts (int argc, char **argv)
                  {
                    option = '?';
                    problem = "bad dhcp-opt";
-                   if (new->netid)
-                     free(new->netid);
-                   free(new);
-                   break;
                  }
-               
-               daemon->dhcp_opts = new;
-               
-               if (!comma)
-                 break;
-               
-               /* characterise the value */
-               is_addr = is_hex = is_dec = 1;
-               addrs = digs = 1;
-               for (cp = comma+1; *cp; cp++)
-                 if (*cp == ',')
-                   {
-                     addrs++;
-                     is_dec = is_hex = 0;
-                   }
-                 else if (*cp == ':')
-                   {
-                     digs++;
-                     is_dec = is_addr = 0;
-                   }
-                 else if (*cp == '.')
-                   is_dec = is_hex = 0;
-                 else if (!(*cp >='0' && *cp <= '9'))
+               else if (comma && new->opt == 119)
+                 {
+                   /* dns search, RFC 3397 */
+                   unsigned char *q, *r, *tail;
+                   unsigned char *p = NULL;
+                   int newlen, len = 0;
+                   
+                   optarg = comma;
+                   if ((comma = strchr(optarg, ',')))
+                     *(comma++) = 0;
+
+                   while (optarg && *optarg)
                      {
-                       is_dec = is_addr = 0;
-                       if (!((*cp >='A' && *cp <= 'F') ||
-                             (*cp >='a' && *cp <= 'f')))
-                         is_hex = 0;
+                       if (!canonicalise(optarg))
+                         {
+                           option = '?';
+                           problem = "bad dhcp-search-opt";
+                           break;
+                         }
+                       
+                       if (!(r = realloc(p, len + strlen(optarg) + 2)))
+                         die("could not get memory", NULL);
+                       p = memmove(r, p, len);
+                       
+                       q = p + len;
+                       
+                       /* add string on the end in RFC1035 format */
+                       while (*optarg) 
+                         {
+                           char *cp = q++;
+                           int j;
+                           for (j = 0; *optarg && (*optarg != '.'); optarg++, j++)
+                             *q++ = *optarg;
+                           *cp = j;
+                           if (*optarg)
+                             optarg++;
+                         }
+                       *q++ = 0;
+                       
+                       /* Now tail-compress using earlier names. */
+                       newlen = q - p;
+                       for (tail = p + len; *tail; tail += (*tail) + 1)
+                         for (r = p; r - p < len; r += (*r) + 1)
+                           if (strcmp(r, tail) == 0)
+                             {
+                               PUTSHORT((r - p) | 0xc000, tail); 
+                               newlen = tail - p;
+                               goto end;
+                             }
+                     end:
+                       len = newlen;
+                   
+                       optarg = comma;
+                       if (optarg && (comma = strchr(optarg, ',')))
+                         *(comma++) = 0;
                      }
-               
-               if (is_hex && digs > 1)
+
+                   new->len = len;
+                   new->val = p;
+                 }
+               else if (comma)
                  {
-                   char *p = comma+1, *q, *r;
-                   new->len = digs;
-                   q = new->val = safe_malloc(new->len);
-                   while (*p)
+                   /* not option 119 */
+                   /* characterise the value */
+                   is_addr = is_hex = is_dec = 1;
+                   addrs = digs = 1;
+                   for (cp = comma; *cp; cp++)
+                     if (*cp == ',')
+                       {
+                         addrs++;
+                         is_dec = is_hex = 0;
+                       }
+                     else if (*cp == ':')
+                       {
+                         digs++;
+                         is_dec = is_addr = 0;
+                       }
+                     else if (*cp == '.')
+                       is_dec = is_hex = 0;
+                     else if (!(*cp >='0' && *cp <= '9'))
+                       {
+                         is_dec = is_addr = 0;
+                         if (!((*cp >='A' && *cp <= 'F') ||
+                               (*cp >='a' && *cp <= 'f')))
+                           is_hex = 0;
+                       }
+               
+                   if (is_hex && digs > 1)
                      {
-                       for (r = p; *r && *r != ':'; r++);
-                       if (*r)
+                       char *p = comma, *q, *r;
+                       new->len = digs;
+                       q = new->val = safe_malloc(new->len);
+                       while (*p)
                          {
-                           if (r != p)
+                           for (r = p; *r && *r != ':'; r++);
+                           if (*r)
                              {
-                               *r = 0;
-                               *(q++) = strtol(p, NULL, 16);
+                               if (r != p)
+                                 {
+                                   *r = 0;
+                                   *(q++) = strtol(p, NULL, 16);
+                                 }
+                               p = r+1;
                              }
-                           p = r+1;
+                           else
+                             {
+                               if (*p)
+                                 *(q++) = strtol(p, NULL, 16);
+                               break;
+                             }
+                         }
+                     }
+                   else if (is_dec)
+                     {
+                       /* Given that we don't know the length,
+                          this appaling hack is the best available */
+                       unsigned int val = atoi(comma);
+                       if (val < 256)
+                         {
+                           new->len = 1;
+                           new->val = safe_malloc(1);
+                           *(new->val) = val;
+                         }
+                       else if (val < 65536)
+                         {
+                           new->len = 2;
+                           new->val = safe_malloc(2);
+                           *(new->val) = val>>8;
+                           *(new->val+1) = val;
                          }
                        else
                          {
-                           if (*p)
-                             *(q++) = strtol(p, NULL, 16);
-                           break;
+                           new->len = 4;
+                           new->val = safe_malloc(4);
+                           *(new->val) = val>>24;
+                           *(new->val+1) = val>>16;
+                           *(new->val+2) = val>>8;
+                           *(new->val+3) = val;
                          }
                      }
-                 }
-               else if (is_dec)
-                 {
-                   /* Given that we don't know the length,
-                      this appaling hack is the best available */
-                   unsigned int val = atoi(comma+1);
-                   if (val < 256)
-                     {
-                       new->len = 1;
-                       new->val = safe_malloc(1);
-                       *(new->val) = val;
-                     }
-                   else if (val < 65536)
+                   else if (is_addr)   
                      {
-                       new->len = 2;
-                       new->val = safe_malloc(2);
-                       *(new->val) = val>>8;
-                       *(new->val+1) = val;
+                       struct in_addr in;
+                       unsigned char *op;
+                       new->len = INADDRSZ * addrs;
+                       new->val = op = safe_malloc(new->len);
+                       new->is_addr = 1;
+                       while (addrs--) 
+                         {
+                           cp = comma;
+                           if ((comma = strchr(cp, ',')))
+                             *comma++ = 0;
+                           in.s_addr = inet_addr(cp);
+                           memcpy(op, &in, INADDRSZ);
+                           op += INADDRSZ;
+                         }
                      }
                    else
                      {
-                       new->len = 4;
-                       new->val = safe_malloc(4);
-                       *(new->val) = val>>24;
-                       *(new->val+1) = val>>16;
-                       *(new->val+2) = val>>8;
-                       *(new->val+3) = val;
+                       /* text arg */
+                       new->len = strlen(comma);
+                       new->val = safe_malloc(new->len);
+                       memcpy(new->val, comma, new->len);
                      }
                  }
-               else if (is_addr)       
+
+               if (new->len > 256)
                  {
-                   struct in_addr in;
-                   unsigned char *op;
-                   new->len = INADDRSZ * addrs;
-                   new->val = op = safe_malloc(new->len);
-                   new->is_addr = 1;
-                   while (addrs--) 
-                     {
-                       cp = comma;
-                       if ((comma = strchr(cp+1, ',')))
-                         *comma = 0;
-                       in.s_addr = inet_addr(cp+1);
-                       memcpy(op, &in, INADDRSZ);
-                       op += INADDRSZ;
-                     }
+                   option = '?';
+                   problem = "dhcp-option too long";
                  }
-               else
+
+               if (option == '?')
                  {
-                   /* text arg */
-                   new->len = strlen(comma+1);
-                   new->val = safe_malloc(new->len);
-                   memcpy(new->val, comma+1, new->len);
+                   if (new->netid)
+                     free(new->netid);
+                   if (new->val)
+                     free(new->val);
+                   free(new);
                  }
+               else
+                 daemon->dhcp_opts = new;
+
                break;
              }
 
index 3dc980e3e46c9f5f94d4b22ca8347f48f6b0f15e..aa7fa9625af19df5706648e83431bffbc05e47d6 100644 (file)
@@ -306,28 +306,54 @@ static unsigned char *skip_questions(HEADER *header, unsigned int plen)
   return ansp;
 }
 
-int resize_packet(HEADER *header, unsigned int plen, unsigned char *pheader, unsigned int hlen)
+static unsigned char *skip_section(unsigned char *ansp, int count, HEADER *header, unsigned int plen)
 {
-  int i;
-  unsigned char *ansp = skip_questions(header, plen);
-  unsigned short rdlen;
+  int i, rdlen;
   
-  if (!ansp)
-    return 0;
-  
-  for (i = 0; 
-       i < (ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount)); 
-       i++)
+  for (i = 0; i < count; i++)
     {
       if (!(ansp = skip_name(ansp, header, plen)))
-       return 0
+       return NULL
       ansp += 8; /* type, class, TTL */
       GETSHORT(rdlen, ansp);
       if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) 
-       return 0;
+       return NULL;
       ansp += rdlen;
     }
 
+  return ansp;
+}
+
+/* CRC all the bytes of the question section. This is used to
+   safely detect query retransmision. */
+unsigned int questions_crc(HEADER *header, unsigned int plen)
+{
+  unsigned char *start, *end = skip_questions(header, plen);
+  unsigned int crc = 0xffffffff;
+
+  if (end)
+    for (start = (unsigned char *)(header+1); start < end; start++)
+      {
+       int i = 8;
+       crc ^= *start << 24;
+       while (i--)
+         crc = crc & 0x80000000 ? (crc << 1) ^ 0x04c11db7 : crc << 1;
+      }
+  return crc;
+}
+
+
+int resize_packet(HEADER *header, unsigned int plen, unsigned char *pheader, unsigned int hlen)
+{
+  unsigned char *ansp = skip_questions(header, plen);
+    
+  if (!ansp)
+    return 0;
+  
+  if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
+                           header, plen)))
+    return 0;
+    
   /* restore pseudoheader */
   if (pheader && ntohs(header->arcount) == 0)
     {
@@ -352,17 +378,9 @@ unsigned char *find_pseudoheader(HEADER *header, unsigned int plen, unsigned int
   if (arcount == 0 || !(ansp = skip_questions(header, plen)))
     return NULL;
   
-  for (i = 0; i < (ntohs(header->ancount) + ntohs(header->nscount)); i++)
-    {
-      if (!(ansp = skip_name(ansp, header, plen)))
-       return NULL; 
-      ansp += 8; /* type, class, TTL */
-      GETSHORT(rdlen, ansp);
-      if ((unsigned int)(ansp + rdlen - (unsigned char *)header) > plen) 
-       return NULL;
-      ansp += rdlen;
-    }
-
+  if (!(ansp = skip_section(ansp, ntohs(header->ancount) + ntohs(header->nscount), header, plen)))
+    return NULL; 
+    
   for (i = 0; i < arcount; i++)
     {
       unsigned char *save, *start = ansp;
@@ -402,9 +420,9 @@ static int private_net(struct all_addr *addrp)
     return 0;
 }
  
-static unsigned char *add_text_record(unsigned int nameoffset, unsigned char *p, 
+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)
+                                     unsigned short type, char *name, int *offset)
 {
   unsigned char *sav, *cp;
   int j;
@@ -433,254 +451,265 @@ static unsigned char *add_text_record(unsigned int nameoffset, unsigned char *p,
   j = p - sav - 2;
   PUTSHORT(j, sav); /* Real RDLENGTH */
   
+  if (offset)
+    *offset = sav - (unsigned char *)header;
+
   return p;
 }
 
-/* On receiving an NXDOMAIN or NODATA reply, determine which names are known
-   not to exist for negative caching. name if a working buffer passed in. */
-void extract_neg_addrs(HEADER *header, unsigned int qlen, char *name, time_t now, unsigned short flags) 
+static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
+{
+  for (; doctor; doctor = doctor->next)
+    if (is_same_net(doctor->in, *addr, doctor->mask))
+      {
+       addr->s_addr &= ~doctor->mask.s_addr;
+       addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
+       /* Since we munged the data, the server it came from is no longer authoritative */
+       header->nscount = htons(0);
+       header->arcount = htons(0);
+       header->aa = 0;
+       break;
+      }
+}
+
+static int find_soa(HEADER *header, unsigned int qlen)
 {
   unsigned char *p;
-  int i, found_soa = 0;
   int qtype, qclass, rdlen;
-  unsigned long ttl, minttl = 0;
-   
-  /* there may be more than one question with some questions
-     answered. We don't generate negative entries from those. */
-  if (ntohs(header->ancount) != 0)
-    return;
+  unsigned long ttl, minttl = ULONG_MAX;
+  int i, found_soa = 0;
   
-  if (!(p = skip_questions(header, qlen)))
-    return; /* bad packet */
+  /* first move to NS section and find TTL from any SOA section */
+  if (!(p = skip_questions(header, qlen)) ||
+      !(p = skip_section(p, ntohs(header->ancount), header, qlen)))
+    return 0; /* bad packet */
   
-  /* we first need to find SOA records, to get min TTL, then we
-     add a NEG cache entry for each question. */
-
   for (i=0; i<ntohs(header->nscount); i++)
     {
-      if (!extract_name(header, qlen, &p, name, 1))
-       return; /* bad packet */
-
+      if (!(p = skip_name(p, header, qlen)))
+       return 0; /* bad packet */
+      
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
       GETLONG(ttl, p);
       GETSHORT(rdlen, p);
-       
+      
       if ((qclass == C_IN) && (qtype == T_SOA))
        {
-         int dummy;
+         found_soa = 1;
+         if (ttl < minttl)
+           minttl = ttl;
+
          /* MNAME */
-         if (!extract_name(header, qlen, &p, name, 1))
-           return;
+         if (!(p = skip_name(p, header, qlen)))
+           return 0;
          /* RNAME */
-         if (!extract_name(header, qlen, &p, name, 1))
-           return;
-         GETLONG(dummy, p); /* SERIAL */
-         GETLONG(dummy, p); /* REFRESH */
-         GETLONG(dummy, p); /* RETRY */
-         GETLONG(dummy, p); /* EXPIRE */
-         if (!found_soa)
-           {
-             found_soa = 1;
-             minttl = ttl;
-           }
-         else if (ttl < minttl)
-           minttl = ttl;
+         if (!(p = skip_name(p, header, qlen)))
+           return 0;
+         p += 16; /* SERIAL REFRESH RETRY EXPIRE */
+         
          GETLONG(ttl, p); /* minTTL */
          if (ttl < minttl)
            minttl = ttl;
        }
       else
        p += rdlen;
-
-      if ((unsigned int)(p - (unsigned char *)header) > qlen)
-       return; /* bad packet */
-    }
-  
-  if (!found_soa)
-    return; /* failed to find SOA */
-
-  cache_start_insert();
-  
-  p = (unsigned char *)(header+1);
-  
-  for (i=0; i<ntohs(header->qdcount); i++)
-    {
-      struct all_addr addr;
-      int is_arpa;
-
-      if (!extract_name(header, qlen, &p, name, 1))
-       return; /* bad packet */
-      
-      GETSHORT(qtype, p); 
-      GETSHORT(qclass, p);
       
-      if (qclass == C_IN && qtype == T_PTR && (is_arpa = in_arpa_name_2_addr(name, &addr)))
-       cache_insert(name, &addr, now, minttl , is_arpa | F_REVERSE | flags);
-      else if (qclass == C_IN && qtype == T_A) 
-       cache_insert(name, NULL, now, minttl, F_IPV4 | F_FORWARD | flags);
-#ifdef HAVE_IPV6             
-      else if (qclass == C_IN && qtype == T_AAAA) 
-       cache_insert(name, NULL, now, minttl, F_IPV6 | F_FORWARD | flags);
-#endif
+      if ((unsigned int)(p - (unsigned char *)header) > qlen)
+       return 0; /* bad packet */
     }
-  
-  cache_end_insert();
-}
-
-static void dns_doctor(HEADER *header, struct doctor *doctor, struct in_addr *addr)
-{
-  for (; doctor; doctor = doctor->next)
-    if (is_same_net(doctor->in, *addr, doctor->mask))
-      {
-       addr->s_addr &= ~doctor->mask.s_addr;
-       addr->s_addr |= (doctor->out.s_addr & doctor->mask.s_addr);
-       /* Since we munged the data, the server it came from is no longer authoritative */
-       header->nscount = htons(0);
-       header->arcount = htons(0);
-       break;
-      }
+  return found_soa ? minttl : 0;
 }
 
-void extract_addresses(HEADER *header, unsigned int qlen, char *name, 
-                      time_t now, struct doctor *doctors)
+/* Note that the following code can create CNAME chains that don't point to a real record,
+   either because of lack of memory, or lack of SOA records.  These are treated by the cache code as 
+   expired and cleaned out that way. */
+void extract_addresses(HEADER *header, unsigned int qlen, char *name, time_t now, struct daemon *daemon)
 {
-  unsigned char *p, *psave, *endrr;
-  int qtype, qclass, rdlen;
-  unsigned long ttl;
-  int i;
-  
-  /* skip over questions */
-  if (!(p = skip_questions(header, qlen)))
-    return; /* bad packet */
-  
+  unsigned char *p, *p1, *endrr;
+  int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
+   
   cache_start_insert();
   
-  psave = p;
+  /* go through the questions. */
+  p = (unsigned char *)(header+1);
   
-  for (i=0; i<ntohs(header->ancount); i++)
+  for (i = 0; i<ntohs(header->qdcount); i++)
     {
-      unsigned char *origname = p;
+      int found = 0, cname_count = 5;
+      struct crec *cpp = NULL;
+      int flags = header->rcode == NXDOMAIN ? F_NXDOMAIN : 0;
+      unsigned long cttl = ULONG_MAX, attl, ttl = 0;
+      
       if (!extract_name(header, qlen, &p, name, 1))
        return; /* bad packet */
-
+           
       GETSHORT(qtype, p); 
       GETSHORT(qclass, p);
-      GETLONG(ttl, p);
-      GETSHORT(rdlen, p);
-       
-      endrr = p + rdlen;
-      if ((unsigned int)(endrr - (unsigned char *)header) > qlen)
-       return; /* bad packet */
       
       if (qclass != C_IN)
-       {
-         p = endrr;
-         continue;
-       }
+       continue;
 
-      if (qtype == T_A) /* A record. */
-       {
-         dns_doctor(header, doctors, (struct in_addr *)p);
-         cache_insert(name, (struct all_addr *)p, now, 
-                      ttl, F_IPV4 | F_FORWARD);
-       }
-#ifdef HAVE_IPV6
-      else if (qtype == T_AAAA) /* IPV6 address record. */
-       cache_insert(name, (struct all_addr *)p, now,
-                    ttl, F_IPV6 | F_FORWARD);
-#endif
-      else if (qtype == T_PTR)
-       {
-  /* PTR record */
+      /* PTRs: we chase CNAMEs here, since we have no way to 
+        represent them in the cache. */
+      if (qtype == T_PTR)
+       { 
          struct all_addr addr;
          int name_encoding = in_arpa_name_2_addr(name, &addr);
-         if (name_encoding)
+         
+         if (!name_encoding)
+           continue;
+
+         if (!(flags & F_NXDOMAIN))
            {
-             if (!extract_name(header, qlen, &p, name, 1))
-               return; /* bad packet */
-             cache_insert(name, &addr, now, 
-                          ttl, name_encoding | F_REVERSE); 
+           cname_loop:
+             if (!(p1 = skip_questions(header, qlen)))
+               return;
+             
+             for (j = 0; j<ntohs(header->ancount); j++) 
+               {
+                 if (!(res = extract_name(header, qlen, &p1, name, 0)))
+                   return; /* bad packet */
+                 
+                 GETSHORT(aqtype, p1); 
+                 GETSHORT(aqclass, p1);
+                 GETLONG(attl, p1);
+                 GETSHORT(ardlen, p1);
+                 endrr = p1+ardlen;
+                 
+                 /* TTL of record is minimum of CNAMES and PTR */
+                 if (attl < cttl)
+                   cttl = attl;
+
+                 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
+                   {
+                     if (!extract_name(header, qlen, &p1, name, 1))
+                       return;
+                     
+                     if (aqtype == T_CNAME)
+                       {
+                         if (!cname_count--)
+                           return; /* looped CNAMES */
+                         goto cname_loop;
+                       }
+                     
+                     cache_insert(name, &addr, now, cttl, name_encoding | F_REVERSE);
+                     found = 1; 
+                   }
+                 
+                 p1 = endrr;
+                 if ((unsigned int)(p1 - (unsigned char *)header) > qlen)
+                   return; /* bad packet */
+               }
+           }
+         
+          if (!found && !(daemon->options & OPT_NO_NEG))
+           {
+             if (!searched_soa)
+               {
+                 searched_soa = 1;
+                 ttl = find_soa(header, qlen);
+               }
+             if (ttl)
+               cache_insert(name, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags); 
            }
        }
-      else if (qtype == T_CNAME)
+      else
        {
-         /* CNAME, search whole answer section again */
-         unsigned char *endrr1;
-         unsigned long cttl;
-         int j;
-         unsigned char *targp = p;
-                 
-         p = psave; /* rewind p */
-         for (j=0; j<ntohs(header->ancount); j++)
+         /* everything other than PTR */
+         struct crec *newc;
+         
+         if (qtype == T_A)
+           flags |= F_IPV4;
+#ifdef HAVE_IPV6
+         else if (qtype == T_AAAA)
+           flags |= F_IPV6;
+#endif
+         else 
+           continue;
+           
+         if (!(flags & F_NXDOMAIN))
            {
-             int res;
-             unsigned char *tmp = targp; 
-             /* copy since it gets altered by extract_name */
-             /* get CNAME target each time round */
-             if (!extract_name(header, qlen, &tmp, name, 1))
-               return; /* bad packet */
-             /* compare this name with target of CNAME in name buffer */
-             if (!(res = extract_name(header, qlen, &p, name, 0)))
-               return; /* bad packet */
-             
-             GETSHORT(qtype, p); 
-             GETSHORT(qclass, p);
-             GETLONG(cttl, p);
-             GETSHORT(rdlen, p);
+           cname_loop1:
+             if (!(p1 = skip_questions(header, qlen)))
+               return;
              
-             endrr1 = p+rdlen;
-             if ((unsigned int)(endrr1 - (unsigned char *)header) > qlen)
-               return; /* bad packet */
-
-             /* is this RR name same as target of CNAME */
-             if ((qclass != C_IN) || (res == 2))
+             for (j = 0; j<ntohs(header->ancount); j++) 
                {
-                 p = endrr1;
-                 continue;
-               }
-
-             /* match, use name of CNAME, data from this RR
-                use min TTL of two */
-
-             if (ttl < cttl)
-               cttl = ttl;
-
-             /* get orig. name back again */
-             tmp = origname;
-             if (!extract_name(header, qlen, &tmp, name, 1))
-               return;
+                 if (!(res = extract_name(header, qlen, &p1, name, 0)))
+                   return; /* bad packet */
+                 
+                 GETSHORT(aqtype, p1); 
+                 GETSHORT(aqclass, p1);
+                 GETLONG(attl, p1);
+                 GETSHORT(ardlen, p1);
+                 endrr = p1+ardlen;
+                 
+                 if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
+                   {
+                     if (aqtype == T_CNAME)
+                       {
+                         if (!cname_count--)
+                           return; /* looped CNAMES */
+                         newc = cache_insert(name, NULL, now, attl, F_CNAME | F_FORWARD);
+                         if (cpp)
+                           {
+                             cpp->addr.cname.cache = newc;
+                             cpp->addr.cname.uid = newc->uid;
+                           }
 
-             if (qtype == T_A) /* A record. */
+                         cpp = newc;
+                         if (attl < cttl)
+                           cttl = attl;
+                         
+                         if (!extract_name(header, qlen, &p1, name, 1))
+                           return;
+                         goto cname_loop1;
+                       }
+                     else
+                       {
+                         found = 1;
+                         if (aqtype == T_A)
+                           dns_doctor(header, daemon->doctors, (struct in_addr *)p1);
+                         newc = cache_insert(name, (struct all_addr *)p1, now, attl, flags | F_FORWARD);
+                         if (cpp)
+                           {
+                             cpp->addr.cname.cache = newc;
+                             cpp->addr.cname.uid = newc->uid;
+                           }
+                         cpp = NULL;
+                       }
+                   }
+                 
+                 p1 = endrr;
+                 if ((unsigned int)(p1 - (unsigned char *)header) > qlen)
+                   return; /* bad packet */
+               }
+           }
+         
+         if (!found && !(daemon->options & OPT_NO_NEG))
+           {
+             if (!searched_soa)
                {
-                 dns_doctor(header, doctors, (struct in_addr *)p);
-                 cache_insert(name, (struct all_addr *)p, now, 
-                              cttl, F_IPV4 | F_FORWARD);
+                 searched_soa = 1;
+                 ttl = find_soa(header, qlen);
                }
-#ifdef HAVE_IPV6
-             else if (qtype == T_AAAA) /* IPV6 address record. */
-               cache_insert(name, (struct all_addr *)p, now, 
-                            cttl, F_IPV6 | F_FORWARD);
-#endif
-             else if (qtype == T_PTR)
+             /* If there's no SOA to get the TTL from, but there is a CNAME 
+                pointing at this, inherit it's TTL */
+             if (ttl || cpp)
                {
-                 /* PTR record extract address from CNAME name */
-                 struct all_addr addr;
-                 int name_encoding = in_arpa_name_2_addr(name, &addr);
-                 if (name_encoding)
+                 newc = cache_insert(name, (struct all_addr *)p, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags);    
+                 if (cpp)
                    {
-                     if (!extract_name(header, qlen, &p, name, 1))
-                       return; /* bad packet */
-                     cache_insert(name, &addr, now, cttl, 
-                                  name_encoding | F_REVERSE);
-                   } 
+                     cpp->addr.cname.cache = newc;
+                     cpp->addr.cname.uid = newc->uid;
+                   }
                }
-             p = endrr1;
            }
-       } 
-      p = endrr;
+       }
     }
-
+  
   cache_end_insert();
 }
 
@@ -818,14 +847,8 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
        for (baddrp = baddr; baddrp; baddrp = baddrp->next)
          if (memcmp(&baddrp->addr, p, INADDRSZ) == 0)
            {
-             /* Found a bogus address. Mangle the packet into an NXDOMAIN reply */
-             header->aa = 0;
-             header->ra = 1; /* recursion if available */
-             header->nscount = htons(0);
-             header->arcount = htons(0);
-             header->ancount = htons(0);
-             header->rcode = NXDOMAIN;
-             
+             /* Found a bogus address. Insert that info here, since there no SOA record
+                to get the ttl from in the normal processing */
              cache_start_insert();
              cache_insert(name, NULL, now, ttl, F_IPV4 | F_FORWARD | F_NEG | F_NXDOMAIN | F_CONFIG);
              cache_end_insert();
@@ -944,7 +967,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
              (qtype == T_SOA || qtype == T_SRV || (qtype == T_ANY && strchr(name, '_'))))
            {
              ans = 1;
-             log_query(F_CONFIG | F_NEG, name, &addr, 0);
+             log_query(F_CONFIG | F_NEG, name, &addr, 0, NULL, 0);
            }
          else 
            {
@@ -958,7 +981,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                          ans = 1;
                          if (!dryrun)
                            {
-                             log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0);
+                             log_query(F_CONFIG | F_REVERSE | F_IPV4 | F_NEG | F_NXDOMAIN, name, &addr, 0, NULL, 0);
                              nxdomain = 1;
                            }
                        }
@@ -974,7 +997,7 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                          ans = 1;
                          if (!dryrun)
                            {
-                             log_query(crecp->flags & ~F_FORWARD, name, &addr, 0);
+                             log_query(crecp->flags & ~F_FORWARD, name, &addr, 0, NULL, 0);
                              auth = 0;
                              if (crecp->flags & F_NXDOMAIN)
                                nxdomain = 1;
@@ -996,10 +1019,11 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                                auth = 0;
                              
-                             ansp = add_text_record(nameoffset, ansp, ttl, 0, T_PTR, 
-                                                    cache_get_name(crecp));
+                             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);
+                             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 */
@@ -1025,23 +1049,43 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
 #endif
                    }
                  
-                 if (qtype != type && qtype != T_ANY)
+                 if (qtype != type && qtype != T_ANY && qtype != T_CNAME)
                    continue;
-                 
+
+               cname_restart:
                  crecp = NULL;
-                 while ((crecp = cache_find_by_name(crecp, name, now, flag)))
+                 while ((crecp = cache_find_by_name(crecp, name, now, flag | F_CNAME)))
                    { 
+                     if (crecp->flags & F_CNAME)
+                       {
+                         if (qtype == T_CNAME)
+                           ans = 1;
+                         
+                         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);
+                           }
+                         strcpy(name, cache_get_name(crecp->addr.cname.cache));
+                         goto cname_restart;
+                       }
+                     
+                     if (qtype == T_CNAME)
+                       break;
+
                      /* 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, name, NULL, 0);
+                             log_query(crecp->flags, name, NULL, 0, NULL, 0);
                              auth = 0;
                              if (crecp->flags & F_NXDOMAIN)
                                nxdomain = 1;
@@ -1061,7 +1105,8 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                              
                              if (!(crecp->flags & (F_HOSTS | F_DHCP)))
                                auth = 0;
-                             log_query(crecp->flags & ~F_REVERSE, name, &crecp->addr, 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); 
@@ -1092,8 +1137,8 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                      ans = 1;
                      if (!dryrun)
                        {
-                         ansp = add_text_record(nameoffset, ansp, daemon->local_ttl, 1, T_MX, 
-                                                mx->mxtarget ? mx->mxtarget : daemon->mxtarget);
+                         ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX, 
+                                                mx->mxtarget ? mx->mxtarget : daemon->mxtarget, NULL);
                          anscount++;
                        }
                    }
@@ -1103,8 +1148,8 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, struct daemon
                      ans = 1;
                      if (!dryrun)
                        {
-                         ansp = add_text_record(nameoffset, ansp, daemon->local_ttl, 1, T_MX,  
-                                                (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget);
+                         ansp = add_text_record(header, nameoffset, ansp, daemon->local_ttl, 1, T_MX,  
+                                                (daemon->options & OPT_SELFMX) ? name : daemon->mxtarget, NULL);
                          anscount++;
                        }
                    }
index 27394621f206e66e488a3c4d22b21c2c81d4fbac..5514ce3eb11d9036333d71803977e9128c4598e1 100644 (file)
@@ -408,10 +408,12 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          (iface_addr.s_addr != option_addr(opt).s_addr))
        return 0;
       
-      log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, NULL);
-
       if (lease && lease->addr.s_addr == mess->ciaddr.s_addr)
        lease_prune(lease, now);
+      else
+       message = "unknown lease";
+
+      log_packet("RELEASE", &mess->ciaddr, mess->chaddr, iface_name, message);
        
       return 0;
       
@@ -473,22 +475,14 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
                  lease_prune(lease, now);
                  lease = NULL;
                }
-             
-             if (!lease)
-               { 
-                 if (lease_find_by_addr(mess->yiaddr))
-                   message = "address in use";
-                 else if (!(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
-                   message = "no leases left";
-               } 
            }
          else
            {
              /* INIT-REBOOT */
-             if (!lease)
+             if (!lease && !(daemon->options & OPT_AUTHORITATIVE))
                return 0;
              
-             if (lease->addr.s_addr != mess->yiaddr.s_addr)
+             if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
                message = "wrong address";
            }
        }
@@ -507,31 +501,40 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          mess->yiaddr = mess->ciaddr;
        }
       
+      log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
+      
       if (!message)
        {
          struct dhcp_config *addr_config;
+
          /* If a machine moves networks whilst it has a lease, we catch that here. */
          if (!is_same_net(mess->yiaddr, context->start, context->netmask))
            message = "wrong network";
 
-         /* Check for renewal of a lease which is now outside the allowed range. */
+         /* Check for renewal of a lease which is outside the allowed range. */
          else if (!address_available(context, mess->yiaddr) &&
                   (!have_config(config, CONFIG_ADDR) || config->addr.s_addr != mess->yiaddr.s_addr))
-           message = "address no longer available";
+           message = "address not available";
 
          /* Check if a new static address has been configured. Be very sure that
             when the client does DISCOVER, it will get the static address, otherwise
             an endless protocol loop will ensue. */
-
-         else if (have_config(config, CONFIG_ADDR) && !lease_find_by_addr(config->addr))
+         
+         else if (have_config(config, CONFIG_ADDR) && 
+                  config->addr.s_addr != mess->yiaddr.s_addr &&
+                  (!(ltmp = lease_find_by_addr(config->addr)) || ltmp == lease))
            message = "static lease available";
 
          /* Check to see if the address is reserved as a static address for another host */
          else if ((addr_config = config_find_by_address(daemon->dhcp_conf, mess->yiaddr)) && addr_config != config)
            message ="address reserved";
-       }
 
-      log_packet("REQUEST", &mess->yiaddr, mess->chaddr, iface_name, NULL);
+         else if ((ltmp = lease_find_by_addr(mess->yiaddr)) && ltmp != lease)
+           message = "address in use";
+         
+         else if (!lease && !(lease = lease_allocate(clid, clid_len, mess->yiaddr)))
+           message = "no leases left";
+       }
       
       if (message)
        {
@@ -541,30 +544,31 @@ int dhcp_reply(struct daemon *daemon, struct in_addr iface_addr, char *iface_nam
          bootp_option_put(mess, NULL, NULL);
          p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPNAK);
          p = option_put_string(p, end, OPTION_MESSAGE, message);
-         p = option_end(p, end, mess);
          mess->flags |= htons(0x8000); /* broadcast */
-         return p - (unsigned char *)mess;
        }
-      
-      log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
-      
-      lease_set_hwaddr(lease, mess->chaddr);
-      if (hostname)
-       lease_set_hostname(lease, hostname, daemon->domain_suffix);
-      lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
-      
-      bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
-      mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
-      p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
-      p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
-      p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
-      if (renewal_time != 0xffffffff)
+      else
        {
-         p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
-         p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
+         log_packet("ACK", &mess->yiaddr, mess->chaddr, iface_name, hostname);
+      
+         lease_set_hwaddr(lease, mess->chaddr);
+         if (hostname)
+           lease_set_hostname(lease, hostname, daemon->domain_suffix);
+         lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time);
+         
+         bootp_option_put(mess, daemon->dhcp_file, daemon->dhcp_sname);
+         mess->siaddr = daemon->dhcp_next_server.s_addr ? daemon->dhcp_next_server : iface_addr;
+         p = option_put(p, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
+         p = option_put(p, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(iface_addr.s_addr));
+         p = option_put(p, end, OPTION_LEASE_TIME, 4, renewal_time);
+         if (renewal_time != 0xffffffff)
+           {
+             p = option_put(p, end, OPTION_T1, 4, (renewal_time/2) - fuzz);
+             p = option_put(p, end, OPTION_T2, 4, ((renewal_time * 7)/8) - fuzz);
+           }
+         p = do_req_options(context, p, end, req_options, daemon, 
+                            hostname, iface_addr, netid, subnet_addr);
        }
-      p = do_req_options(context, p, end, req_options, daemon, 
-                        hostname, iface_addr, netid, subnet_addr);
+
       p = option_end(p, end, mess);
       return p - (unsigned char *)mess; 
       
index a9e8377b7a3ac769828d76753c5aaf4a3a70cf60..dc90eafa65ed991394a8229390154bb2ff3cc615 100644 (file)
@@ -251,3 +251,20 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask)
 {
   return (a.s_addr & mask.s_addr) == (b.s_addr & mask.s_addr);
 } 
+
+int retry_send(void)
+{
+   struct timespec waiter;
+   if (errno == EAGAIN)
+     {
+       waiter.tv_sec = 0;
+       waiter.tv_nsec = 10000;
+       nanosleep(&waiter, NULL);
+       return 1;
+     }
+   
+   if (errno == EINTR)
+     return 1;
+
+   return 0;
+}