diff options
author | Simon Kelley <simon@thekelleys.org.uk> | 2004-10-21 20:24:00 +0100 |
---|---|---|
committer | Simon Kelley <simon@thekelleys.org.uk> | 2012-01-05 17:31:11 +0000 |
commit | fd9fa4811dda712e9078b851e1608cbcc7afa898 (patch) | |
tree | 490d831d79b78994aae6a21d76814907761f4f59 | |
parent | 36717eeefcfd6bbd3e875027112080a59fe1f5c1 (diff) | |
download | dnsmasq-fd9fa4811dda712e9078b851e1608cbcc7afa898.tar.gz |
import of dnsmasq-2.16.tar.gzv2.16
-rw-r--r-- | CHANGELOG | 39 | ||||
-rw-r--r-- | FAQ | 16 | ||||
-rwxr-xr-x | contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl | 249 | ||||
-rw-r--r-- | dnsmasq-rh.spec | 2 | ||||
-rw-r--r-- | dnsmasq-suse.spec | 2 | ||||
-rw-r--r-- | dnsmasq.8 | 10 | ||||
-rw-r--r-- | dnsmasq.conf.example | 15 | ||||
-rw-r--r-- | rpm/dnsmasq-SuSE.patch | 4 | ||||
-rw-r--r-- | src/cache.c | 189 | ||||
-rw-r--r-- | src/config.h | 25 | ||||
-rw-r--r-- | src/dhcp.c | 26 | ||||
-rw-r--r-- | src/dnsmasq.c | 78 | ||||
-rw-r--r-- | src/dnsmasq.h | 46 | ||||
-rw-r--r-- | src/forward.c | 140 | ||||
-rw-r--r-- | src/isc.c | 26 | ||||
-rw-r--r-- | src/lease.c | 6 | ||||
-rw-r--r-- | src/network.c | 13 | ||||
-rw-r--r-- | src/option.c | 282 | ||||
-rw-r--r-- | src/rfc1035.c | 525 | ||||
-rw-r--r-- | src/rfc2131.c | 80 | ||||
-rw-r--r-- | src/util.c | 17 |
21 files changed, 1174 insertions, 616 deletions
@@ -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. + @@ -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 index 0000000..3c4a1f1 --- /dev/null +++ b/contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl @@ -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); +} diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec index 3594e3f..3652a24 100644 --- a/dnsmasq-rh.spec +++ b/dnsmasq-rh.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.15 +Version: 2.16 Release: 1 Copyright: GPL Group: System Environment/Daemons diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec index b379e0a..c1ca8a0 100644 --- a/dnsmasq-suse.spec +++ b/dnsmasq-suse.spec @@ -5,7 +5,7 @@ ############################################################################### Name: dnsmasq -Version: 2.15 +Version: 2.16 Release: 1 Copyright: GPL Group: Productivity/Networking/DNS/Servers @@ -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 diff --git a/dnsmasq.conf.example b/dnsmasq.conf.example index 7fdd24a..d82159e 100644 --- a/dnsmasq.conf.example +++ b/dnsmasq.conf.example @@ -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 diff --git a/rpm/dnsmasq-SuSE.patch b/rpm/dnsmasq-SuSE.patch index 9982808..c32a7d8 100644 --- a/rpm/dnsmasq-SuSE.patch +++ b/rpm/dnsmasq-SuSE.patch @@ -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 diff --git a/src/cache.c b/src/cache.c index 1419631..64f24af 100644 --- a/src/cache.c +++ b/src/cache.c @@ -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"; diff --git a/src/config.h b/src/config.h index 756093c..4f12daa 100644 --- a/src/config.h +++ b/src/config.h @@ -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 @@ -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; } } diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 0d25036..cec4661 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -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; } diff --git a/src/dnsmasq.h b/src/dnsmasq.h index c16169d..5464684 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -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 diff --git a/src/forward.c b/src/forward.c index 3731bdb..799c01b 100644 --- a/src/forward.c +++ b/src/forward.c @@ -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; @@ -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); } } diff --git a/src/lease.c b/src/lease.c index eff7401..e0d2948 100644 --- a/src/lease.c +++ b/src/lease.c @@ -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; diff --git a/src/network.c b/src/network.c index 941f033..034a5f6 100644 --- a/src/network.c +++ b/src/network.c @@ -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); diff --git a/src/option.c b/src/option.c index 3fe4492..a3b0dc8 100644 --- a/src/option.c +++ b/src/option.c @@ -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; } diff --git a/src/rfc1035.c b/src/rfc1035.c index 3dc980e..aa7fa96 100644 --- a/src/rfc1035.c +++ b/src/rfc1035.c @@ -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++; } } diff --git a/src/rfc2131.c b/src/rfc2131.c index 2739462..5514ce3 100644 --- a/src/rfc2131.c +++ b/src/rfc2131.c @@ -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; @@ -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; +} |