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.
+
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.
+
+
--- /dev/null
+#!/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);
+}
###############################################################################
Name: dnsmasq
-Version: 2.15
+Version: 2.16
Release: 1
Copyright: GPL
Group: System Environment/Daemons
###############################################################################
Name: dnsmasq
-Version: 2.15
+Version: 2.16
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
.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
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
#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
# 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
--- 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
#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
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)
{
cache_size = size;
big_free = NULL;
bignames_left = size/10;
- addn_file = NULL;
+ index = 0;
cache_inserted = cache_live_freed = 0;
{
cache_link(crecp);
crecp->flags = 0;
+ crecp->uid = index++;
}
}
{
crecp->flags &= ~F_FORWARD;
crecp->flags &= ~F_REVERSE;
-
+ crecp->uid = index++; /* invalidate CNAMES pointing to this. */
+
if (cache_tail)
cache_tail->next = crecp;
else
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.
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;
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)))
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;
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))
/* 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. */
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
{
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
!(big_name = (union bigname *)malloc(sizeof(union bigname))))
{
insert_error = 1;
- return;
+ return NULL;
}
else
bignames_left--;
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 */
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;
}
{
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) &&
{
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))
{
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
{
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;
else
continue;
- if (is_addn)
- flags |= F_ADDN;
-
while ((token = strtok(NULL, " \t\n\r")) && (*token != '#'))
{
struct crec *cache;
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
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;
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)
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;
{
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)))
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;
-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];
{
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
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" : " ",
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";
return;
strcpy(types, " ");
-
+
if (flags & F_NEG)
{
if (flags & F_REVERSE)
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,
#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";
/* 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 */
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
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)
*/
#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
#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
{
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)
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);
#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
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;
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
{
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;
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
}
}
(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;
}
}
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))
{
if (sigusr1)
{
- dump_cache(daemon->options & (OPT_DEBUG | OPT_LOG), daemon->cachesize);
+ dump_cache(daemon);
sigusr1 = 0;
}
#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))
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;
}
#define OPT_ETHERS 16384
#define OPT_RESOLV_DOMAIN 32768
#define OPT_NO_FORK 65536
+#define OPT_AUTHORITATIVE 131072
struct all_addr {
union {
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];
};
#define F_IMMORTAL 1
-#define F_CONFIG 2
+#define F_CONFIG 2
#define F_REVERSE 4
#define F_FORWARD 8
#define F_DHCP 16
#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,
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;
unsigned int iface;
unsigned short orig_id, new_id;
int fd;
+ unsigned int crc;
time_t time;
struct frec *next;
};
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;
/* 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);
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 */
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);
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);
/* 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);
/* 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
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. */
}
#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;
}
}
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;
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;
}
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;
forward->new_id = get_id();
forward->fd = udpfd;
forward->orig_id = ntohs(header->id);
+ forward->crc = crc;
header->id = htons(forward->new_id);
}
}
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;
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
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. */
#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)
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 */
}
{
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
}
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;
}
{
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
}
}
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
}
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;
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;
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;
}
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)))
{
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) ||
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",
{
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);
}
}
}
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);
}
}
}
}
-void lease_update_dns(void)
+void lease_update_dns(struct daemon *daemon)
{
struct dhcp_lease *lease;
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;
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
!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)
/* 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)
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))
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);
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'},
{"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}
};
{ 'n', OPT_NO_POLL },
{ 'd', OPT_DEBUG },
{ 'k', OPT_NO_FORK },
+ { 'K', OPT_AUTHORITATIVE },
{ 'o', OPT_ORDER },
{ 'R', OPT_NO_RESOLV },
{ 'E', OPT_EXPAND },
"-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"
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;
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;
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')))
if (*cp)
{
new->netid = safe_string_alloc(optarg);
- optarg = comma + 1;
+ optarg = comma;
if ((comma = strchr(optarg, ',')))
- *comma = 0;
+ *comma++ = 0;
}
}
{
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;
}
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)
{
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;
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;
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();
}
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();
(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
{
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;
}
}
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;
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 */
#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;
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);
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++;
}
}
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++;
}
}
(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;
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";
}
}
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)
{
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;
{
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;
+}