summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2004-06-22 20:23:33 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2012-01-05 17:31:10 +0000
commitde37951cf4c3a380fd11a2bf4f03a4eed1137673 (patch)
tree9d2c510891855c3b0c0e0f84464ccedb589948ba
parenta222641cb06189d287bf2e00a3f1f26019b80ac7 (diff)
downloaddnsmasq-de37951cf4c3a380fd11a2bf4f03a4eed1137673.tar.gz
import of dnsmasq-2.9.tar.gzv2.9
-rw-r--r--CHANGELOG53
-rw-r--r--FAQ55
-rw-r--r--dnsmasq-rh.spec2
-rw-r--r--dnsmasq-suse.spec2
-rw-r--r--dnsmasq.814
-rw-r--r--doc.html7
-rw-r--r--src/cache.c21
-rw-r--r--src/config.h13
-rw-r--r--src/dhcp.c12
-rw-r--r--src/dnsmasq.c97
-rw-r--r--src/dnsmasq.h19
-rw-r--r--src/forward.c239
-rw-r--r--src/network.c84
-rw-r--r--src/option.c70
-rw-r--r--src/rfc1035.c11
15 files changed, 467 insertions, 232 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 90104ed..680fd5f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1003,3 +1003,56 @@ release 2.8
existing hostname, don't wipe it out.
Tweaked option parsing to flag some parameter errors.
+
+release 2.9
+ Fixed interface filter code for two effects: 1) Fixed bug
+ where queries sent via loopback interface
+ but to the address of another interface were ignored
+ unless the loopback interface was explicitly configured.
+ 2) on OpenBSD failure to configure one interface now
+ causes a fatal error on startup rather than an huge
+ stream of log messages. Thanks to Erik Jan Tromp for
+ finding that bug.
+
+ Changed server selection strategy to improve performance
+ when there are many available servers and some are
+ broken. The new algorithm is to pick as before for the
+ first try, but if a query is retried, to send to all
+ available servers in parallel. The first one to reply
+ then becomes prefered for the next query. This should
+ improve reliability without generating significant extra
+ upstream load.
+
+ Fixed breakage of special servers/addresses for
+ unqualified domains introduced in version 2.8
+
+ Allow fallback to "bind-interfaces" at runtime: Some
+ verions of *BSD seem to have enough stuff in the header
+ files to build but no kernel support. Also now log if
+ "bind-interfaces" is forced on.
+
+ Log replies from upstream servers which refuse to do
+ recursion - dnsmasq is not a recursive nameserver and
+ relies on upstream servers to do the recursion, this
+ flags a configuration error.
+
+ Disable client-id matching for hosts whose MAC address is
+ read from /etc/ethers. Patch from Oleg I. Vdovikin.
+
+ Extended --mx-host flag to allow arbitrary targets for MX
+ records, suggested by Moritz Bunkus.
+
+ Fixed build under NetBSD 2.0 - thanks to Felix Deichmann
+ for the patch.
+
+ Deal correctly with repeated addresses in /etc/hosts. The
+ first name found is now returned for reverse lookups,
+ rather than all of them.
+
+ Add back fatal errors when nonexistant
+ interfaces or interface addresses are given but only in
+ "bind-interfaces" mode. Principle of least surprise applies.
+
+ Allow # as the argument to --domain, meaning "read the
+ domain from the first search directive in
+ /etc.resolv.conf". Feature suggested by Evan Jones.
diff --git a/FAQ b/FAQ
index 8349bfc..48f3b47 100644
--- a/FAQ
+++ b/FAQ
@@ -206,19 +206,66 @@ A: What is happening is this: The boot process sends a DHCP
the MAC address has a static allocation, that address is still in
use by the first incarnation of the machine (the one from the boot,
without a client ID.) dnsmasq therefore has to give the machine a
- dynamic address from its pool. There are two ways to solve this:
+ dynamic address from its pool. There are three ways to solve this:
(1) persuade your DHCP client not to send a client ID, or (2) set up
the static assignment to the client ID, not the MAC address. The
default client-id will be 01:<MAC address>, so change the dhcp-host
line from "dhcp-host=11:22:33:44:55:66,1.2.3.4" to
- "dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4"
+ "dhcp-host=id:01:11:22:33:44:55:66,1.2.3.4" or (3) tell dnsmasq to
+ ignore client IDs for a particular MAC address, like this:
+ dhcp-host=11:22:33:44:55:66,id:*
Q: What network types are supported by the DHCP server?
A: Ethernet (and 802.11 wireless) are supported on all platforms. On
Linux Token Ring is also supported.
-
-
+Q: What is this strange "bind-interface" option?
+
+A: The DNS spec says that the reply to a DNS query must come from the
+ same address it was sent to. The traditional way to write an UDP
+ server to do this is to find all of the addresses belonging to the
+ machine (ie all the interfaces on the machine) and then create a
+ socket for each interface which is bound to the address of the
+ interface. Then when a packet is sent to address A, it is received
+ on the socket bound to address A and when the reply is also sent
+ via that socket, the source address is set to A by the kernel and
+ everything works. This is the how dnsmasq works when
+ "bind-interfaces" is set, with the obvious extension that is misses
+ out creating sockets for some interfaces depending on the
+ --interface, --address and --except-interface flags. The
+ disadvantage of this approach is that it breaks if interfaces don't
+ exist or are not configured when the daemon starts and does the
+ socket creation step. In a hotplug-aware world this is a real
+ problem.
+
+ The alternative approach is to have only one socket, which is bound
+ to the correct port and the wildcard IP address (0.0.0.0). That
+ socket will receive _all_ packets sent to port 53, no matter what
+ destination address they have. This solves the problem of
+ interfaces which are created or reconfigured after daemon
+ start-up. To make this work is more complicated because of the
+ "reply source address" problem. When a UDP packet is sent by a
+ socket bound to 0.0.0.0 its source address will be set to the
+ address of one of the machine's interfaces, but which one is not
+ determined and can vary depending on the OS being run. To get round
+ this it is neccessary to use a scary advanced API to determine the
+ address to which a query was sent, and force that to be the source
+ address in the reply. For IPv4 this stuff in non-portable and quite
+ often not even available (It's different between FreeBSD 5.x and
+ Linux, for instance, and FreeBSD 4.x, Linux 2.0.x and OpenBSD don't
+ have it at all.) Hence "bind-interfaces" has to always be available
+ as a fall back. For IPv6 the API is standard and universally
+ available.
+
+ It could be argued that if the --interface or --address flags are
+ used then binding interfaces is more appropriate, but using
+ wildcard binding means that dnsmasq will quite happily start up
+ after being told to use interfaces which don't exist, but which are
+ created later. Wildcard binding breaks the scenario when dnsmasq is
+ listening on one interface and another server (most probably BIND)
+ is listening on another. It's not possible for BIND to bind to an
+ (address,port) pair when dnsmasq has bound (wildcard,port), hence
+ the ability to explicitly turn off wildcard binding.
diff --git a/dnsmasq-rh.spec b/dnsmasq-rh.spec
index 2ac533e..4ec82dd 100644
--- a/dnsmasq-rh.spec
+++ b/dnsmasq-rh.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.8
+Version: 2.9
Release: 1
Copyright: GPL
Group: System Environment/Daemons
diff --git a/dnsmasq-suse.spec b/dnsmasq-suse.spec
index 8803a10..147ff07 100644
--- a/dnsmasq-suse.spec
+++ b/dnsmasq-suse.spec
@@ -5,7 +5,7 @@
###############################################################################
Name: dnsmasq
-Version: 2.8
+Version: 2.9
Release: 1
Copyright: GPL
Group: Productivity/Networking/DNS/Servers
diff --git a/dnsmasq.8 b/dnsmasq.8
index 5c7be0d..d72b949 100644
--- a/dnsmasq.8
+++ b/dnsmasq.8
@@ -120,7 +120,9 @@ option forces dnsmasq to really bind only the interfaces it is
listening on. About the only time when this is useful is when
running another nameserver on the same machine or using IP
alias. Specifying interfaces with IP alias automatically turns this
-option on.
+option on. Note that this only applies to the DNS part of dnsmasq, the
+DHCP server always binds the wildcard address in order to receive
+broadcast packets.
.TP
.B \-b, --bogus-priv
Bogus private reverse lookups. All reverse lookups for private IP ranges (ie 192.168.x.x, etc)
@@ -230,8 +232,10 @@ additional facility that /#/ matches any domain. Thus
answered from /etc/hosts or DHCP and not sent to an upstream
nameserver by a more specific --server directive.
.TP
-.B \-m, --mx-host=<mx name>
-Return an MX record named <mx name> pointing to the host specified in the --mx-target switch
+.B \-m, --mx-host=<mx name>[,<hostname>]
+Return an MX record named <mx name> pointing to the given hostname (if
+given), or
+the host specified in the --mx-target switch
or, if that switch is not given, the host on which dnsmasq
is running. This is useful for directing mail from systems on a LAN
to a central server.
@@ -409,7 +413,9 @@ for DHCP-configured hosts to claim. The intention is to constrain hostnames so t
.B --domain-suffix=thekelleys.org.uk
and have a machine whose DHCP hostname is "laptop". The IP address for that machine is available from
.B dnsmasq
-both as "laptop" and "laptop.thekelleys.org.uk".
+both as "laptop" and "laptop.thekelleys.org.uk". If the domain is
+given as "#" then the domain is read from the first "search" directive
+in /etc/resolv.conf (or equivalent).
.TP
.B \-E, --expand-hosts
Add the domain-suffix to simple names (without a period) in /etc/hosts
diff --git a/doc.html b/doc.html
index 37645b8..f19dc1d 100644
--- a/doc.html
+++ b/doc.html
@@ -18,8 +18,9 @@ connected to the internet via a modem, cable-modem or ADSL
connection but would be a good choice for any small network where low
resource use and ease of configuration are important.
<P>
-Dnsmasq is included in at least the following Linux distributions: Gentoo, Debian,
-Smoothwall, IP-Cop, floppyfw, Firebox, Freesco and
+Dnsmasq is included in at least the following Linux distributions:
+Gentoo, Debian, Slackware, Suse,
+Smoothwall, IP-Cop, floppyfw, Firebox, Freesco, CoyoteLinux and
Clarkconnect. It is also available as a FreeBSD port and is used in Linksys wireless routers.
<P>
Dnsmasq provides the following features:
@@ -86,7 +87,7 @@ in the .com and .net TLDs
<H2>Download.</H2>
-Download dnsmasq <A HREF="http://www.thekelleys.org.uk/dnsmasq/"> here</A>.
+<A HREF="http://www.thekelleys.org.uk/dnsmasq/"> Download</A> dnsmasq here.
The tarball includes this documentation, source, manpage and control files for building .rpms.
There are also pre-built i386 .rpms, and a
<A HREF="CHANGELOG"> CHANGELOG</A>.
diff --git a/src/cache.c b/src/cache.c
index 8dae411..4a01dc9 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -472,19 +472,21 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
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) &&
+ if (lookup && (lookup->flags & F_HOSTS) &&
memcmp(&lookup->addr, addr, addrlen) == 0)
free(cache);
else
{
+ /* Ensure there is only one address -> name mapping (first one trumps) */
+ 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_hash(cache);
-
}
}
-static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, unsigned short addn_flag)
+static void read_hostsfile(char *filename, int opts, char *buff, char *domain_suffix, int is_addn)
{
FILE *f = fopen(filename, "r");
char *line;
@@ -529,6 +531,9 @@ 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;
@@ -543,16 +548,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 | addn_flag);
- /* Only first name is cannonical and used for reverse lookups */
- flags &= ~F_REVERSE;
+ add_hosts_entry(cache, &addr, addrlen, flags);
}
if ((cache = malloc(sizeof(struct crec) + strlen(token)+1-SMALLDNAME)))
{
strcpy(cache->name.sname, token);
- add_hosts_entry(cache, &addr, addrlen, flags | addn_flag);
- /* Clear this here in case not done above. */
- flags &= ~F_REVERSE;
+ add_hosts_entry(cache, &addr, addrlen, flags);
}
}
else
@@ -604,7 +605,7 @@ void cache_reload(int opts, char *buff, char *domain_suffix, char *addn_hosts)
read_hostsfile(HOSTSFILE, opts, buff, domain_suffix, 0);
if (addn_hosts)
{
- read_hostsfile(addn_hosts, opts, buff, domain_suffix, F_ADDN);
+ read_hostsfile(addn_hosts, opts, buff, domain_suffix, 1);
addn_file = addn_hosts;
}
}
diff --git a/src/config.h b/src/config.h
index c7a435c..a2f13da 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,7 +12,7 @@
/* Author's email: simon@thekelleys.org.uk */
-#define VERSION "2.8"
+#define VERSION "2.9"
#define FTABSIZ 150 /* max number of outstanding requests */
#define TIMEOUT 20 /* drop queries after TIMEOUT seconds */
@@ -59,17 +59,6 @@
#endif
-/* determine if we can find the destination address of recieved packets
- and set the source address of sent ones. If so, we can use one socket
- bound to INADDR_ANY and cope with dynamically created interfaces.
- Linux does this differently to FreeBSD. */
-
-#if defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR))
-# define HAVE_UDP_SRC_DST
-#else
-# undef HAVE_UDP_SRC_DST
-#endif
-
/* Decide if we're going to support IPv6 */
/* We assume that systems which don't have IPv6
headers don't have ntop and pton either */
diff --git a/src/dhcp.c b/src/dhcp.c
index 5c6f747..a6c5d8e 100644
--- a/src/dhcp.c
+++ b/src/dhcp.c
@@ -129,13 +129,9 @@ void dhcp_packet(struct dhcp_context *contexts, char *packet,
return;
#else
- if (!names || !names->name || names->next)
- {
- syslog(LOG_ERR, "must set exactly one interface on broken systems without IP_RECVIF");
- return;
- }
- else
- strcpy(ifr.ifr_name, names->name);
+ while (names->isloop)
+ names = names->next;
+ strcpy(ifr.ifr_name, names->name);
#endif
#ifdef HAVE_BPF
@@ -572,7 +568,7 @@ struct dhcp_config *dhcp_read_ethers(struct dhcp_config *configs, char *buff)
config->addr = addr;
}
- config->flags |= CONFIG_HWADDR;
+ config->flags |= CONFIG_HWADDR | CONFIG_NOCLID;
memcpy(config->hwaddr, hwaddr, ETHER_ADDR_LEN);
count++;
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
index fff2ff7..7ace797 100644
--- a/src/dnsmasq.c
+++ b/src/dnsmasq.c
@@ -37,15 +37,16 @@ int main (int argc, char **argv)
int maxleases = MAXLEASES;
int query_port = 0;
int first_loop = 1;
+ int bind_fallback = 0;
unsigned long local_ttl = 0;
unsigned int options, min_leasetime;
char *runfile = RUNFILE;
time_t resolv_changed = 0;
time_t now, last = 0;
struct irec *interfaces = NULL;
- struct listener *listener, *listeners;
+ struct listener *listener, *listeners = NULL;
struct doctor *doctors = NULL;
- char *mxname = NULL;
+ struct mx_record *mxnames = NULL;
char *mxtarget = NULL;
char *lease_file = NULL;
char *addn_hosts = NULL;
@@ -106,7 +107,7 @@ int main (int argc, char **argv)
packet = safe_malloc(DNSMASQ_PACKETSZ);
dhcp_next_server.s_addr = 0;
- options = read_opts(argc, argv, dnamebuff, &resolv, &mxname, &mxtarget, &lease_file,
+ options = read_opts(argc, argv, dnamebuff, &resolv, &mxnames, &mxtarget, &lease_file,
&username, &groupname, &domain_suffix, &runfile,
&if_names, &if_addrs, &if_except, &bogus_addr,
&serv_addrs, &cachesize, &port, &query_port, &local_ttl, &addn_hosts,
@@ -124,18 +125,41 @@ int main (int argc, char **argv)
die("ISC dhcpd integration not available: set HAVE_ISC_READER in src/config.h", NULL);
#endif
-#ifndef HAVE_UDP_SRC_DST
- /* if we cannot support binding the wildcard address, set the "bind only
- interfaces in use" option */
- options |= OPT_NOWILD;
-#endif
-
interfaces = enumerate_interfaces(&if_names, &if_addrs, if_except, port);
- if (options & OPT_NOWILD)
- listeners = create_bound_listeners(interfaces);
- else
- listeners = create_wildcard_listeners(port);
+
+ if (!(options & OPT_NOWILD) && !(listeners = create_wildcard_listeners(port)))
+ {
+ bind_fallback = 1;
+ options |= OPT_NOWILD;
+ }
+ if (options & OPT_NOWILD)
+ {
+ struct iname *if_tmp;
+ listeners = create_bound_listeners(interfaces);
+
+ for (if_tmp = if_names; if_tmp; if_tmp = if_tmp->next)
+ if (if_tmp->name && !if_tmp->used)
+ die("unknown interface %s", if_tmp->name);
+
+ for (if_tmp = if_addrs; if_tmp; if_tmp = if_tmp->next)
+ if (!if_tmp->used)
+ {
+ char addrbuff[ADDRSTRLEN];
+#ifdef HAVE_IPV6
+ if (if_tmp->addr.sa.sa_family == AF_INET)
+ inet_ntop(AF_INET, &if_tmp->addr.in.sin_addr,
+ addrbuff, ADDRSTRLEN);
+ else
+ inet_ntop(AF_INET6, &if_tmp->addr.in6.sin6_addr,
+ addrbuff, ADDRSTRLEN);
+#else
+ strcpy(addrbuff, inet_ntoa(if_tmp->addr.in.sin_addr));
+#endif
+ die("no interface with address %s", addrbuff);
+ }
+ }
+
forward_init(1);
cache_init(cachesize, options & OPT_LOG);
@@ -148,6 +172,15 @@ int main (int argc, char **argv)
if (dhcp)
{
+#if !defined(IP_PKTINFO) && !defined(IP_RECVIF)
+ int c;
+ struct iname *tmp;
+ for (c = 0, tmp = if_names; tmp; tmp = tmp->next)
+ if (!tmp->isloop)
+ c++;
+ if (c != 1)
+ die("must set exactly one interface on broken systems without IP_RECVIF", NULL);
+#endif
dhcp_init(&dhcpfd, &dhcp_raw_fd);
leasefd = lease_init(lease_file, domain_suffix, dnamebuff, packet, now, maxleases);
}
@@ -228,12 +261,9 @@ int main (int argc, char **argv)
else
syslog(LOG_INFO, "started, version %s cache disabled", VERSION);
- if (options & OPT_LOCALMX)
- syslog(LOG_INFO, "serving MX record for local hosts target %s", mxtarget);
- else if (mxname)
- syslog(LOG_INFO, "serving MX record for mailhost %s target %s",
- mxname, mxtarget);
-
+ if (bind_fallback)
+ syslog(LOG_WARNING, "setting --bind-interfaces option because if OS limitations");
+
for (dhcp_tmp = dhcp; dhcp_tmp; dhcp_tmp = dhcp_tmp->next)
{
strcpy(dnamebuff, inet_ntoa(dhcp_tmp->start));
@@ -256,8 +286,9 @@ int main (int argc, char **argv)
if (getuid() == 0 || geteuid() == 0)
syslog(LOG_WARNING, "failed to drop root privs");
- servers = last_server = check_servers(serv_addrs, interfaces, &sfds);
-
+ servers = check_servers(serv_addrs, interfaces, &sfds);
+ last_server = NULL;
+
while (sigterm == 0)
{
fd_set rset;
@@ -275,9 +306,11 @@ int main (int argc, char **argv)
lease_update_dns();
}
if (resolv && (options & OPT_NO_POLL))
- servers = last_server =
- check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
- interfaces, &sfds);
+ {
+ servers = check_servers(reload_servers(resolv->name, dnamebuff, servers, query_port),
+ interfaces, &sfds);
+ last_server = NULL;
+ }
sighup = 0;
}
@@ -373,7 +406,7 @@ int main (int argc, char **argv)
else
{
res->logged = 0;
- if (statbuf.st_mtime > last_change)
+ if (difftime(statbuf.st_mtime, last_change) > 0.0)
{
last_change = statbuf.st_mtime;
latest = res;
@@ -382,20 +415,20 @@ int main (int argc, char **argv)
res = res->next;
}
- if (latest && last_change > resolv_changed)
+ if (latest && difftime(last_change, resolv_changed) > 0.0)
{
resolv_changed = last_change;
- servers = last_server =
- check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
- interfaces, &sfds);
+ servers = check_servers(reload_servers(latest->name, dnamebuff, servers, query_port),
+ interfaces, &sfds);
+ last_server = NULL;
}
}
}
for (serverfdp = sfds; serverfdp; serverfdp = serverfdp->next)
if (FD_ISSET(serverfdp->fd, &rset))
- last_server = reply_query(serverfdp->fd, options, packet, now,
- dnamebuff, last_server, bogus_addr, doctors);
+ last_server = reply_query(serverfdp, options, packet, now,
+ dnamebuff, servers, last_server, bogus_addr, doctors);
if (dhcp && FD_ISSET(dhcpfd, &rset))
dhcp_packet(dhcp, packet, dhcp_options, dhcp_configs, dhcp_vendors,
@@ -406,7 +439,7 @@ int main (int argc, char **argv)
for (listener = listeners; listener; listener = listener->next)
if (FD_ISSET(listener->fd, &rset))
last_server = receive_query(listener, packet,
- mxname, mxtarget, options, now, local_ttl, dnamebuff,
+ mxnames, mxtarget, options, now, local_ttl, dnamebuff,
if_names, if_addrs, if_except, last_server, servers);
}
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index 2ffb64e..cf8d1e7 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -56,7 +56,7 @@
#include <errno.h>
#include <pwd.h>
#include <grp.h>
-#if defined(__OpenBSD__)
+#if defined(__OpenBSD__) || defined(__NetBSD__)
# include <netinet/if_ether.h>
#else
# include <net/ethernet.h>
@@ -91,6 +91,7 @@
#define OPT_NODOTS_LOCAL 4096
#define OPT_NOWILD 8192
#define OPT_ETHERS 16384
+#define OPT_RESOLV_DOMAIN 32768
struct all_addr {
union {
@@ -112,6 +113,11 @@ struct doctor {
struct doctor *next;
};
+struct mx_record {
+ char *mxname, *mxtarget;
+ struct mx_record *next;
+};
+
union bigname {
char name[MAXDNAME];
union bigname *next; /* freelist */
@@ -206,6 +212,7 @@ struct listener {
struct iname {
char *name;
union mysockaddr addr;
+ int isloop, used;
struct iname *next;
};
@@ -337,7 +344,7 @@ int setup_reply(HEADER *header, unsigned int qlen,
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);
-int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
+int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now, unsigned long local_ttl,
char *namebuff);
int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
@@ -360,7 +367,7 @@ int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
/* option.c */
unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resolv_file,
- char **mxname, char **mxtarget, char **lease_file,
+ struct mx_record **mxnames, char **mxtarget, char **lease_file,
char **username, char **groupname,
char **domain_suffix, char **runfile,
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
@@ -373,11 +380,11 @@ unsigned int read_opts(int argc, char **argv, char *buff, struct resolvc **resol
/* forward.c */
void forward_init(int first);
-struct server *reply_query(int fd, int options, char *packet, time_t now,
- char *dnamebuff, struct server *last_server,
+struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
+ char *dnamebuff, struct server *servers, struct server *last_server,
struct bogus_addr *bogus_nxdomain, struct doctor *doctors);
-struct server *receive_query(struct listener *listen, char *packet, char *mxname,
+struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *namebuff,
struct iname *names, struct iname *addrs, struct iname *except,
diff --git a/src/forward.c b/src/forward.c
index fb4568c..fbf8790 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -120,22 +120,27 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
{
struct frec *forward;
char *domain = NULL;
- int type = 0;
- struct server *serv;
+ int forwardall = 0, type = 0;
struct all_addr *addrp = NULL;
unsigned short flags = 0;
unsigned short gotname = extract_request(header, (unsigned int)plen, dnamebuff);
-
+ struct server *start = NULL;
+
/* may be recursion not speced or no servers available. */
if (!header->rd || !servers)
forward = NULL;
else if ((forward = lookup_frec_by_sender(ntohs(header->id), udpaddr)))
{
- /* retry on existing query, send to next server */
+ /* retry on existing query, send to all available servers */
domain = forward->sentto->domain;
+ if (!(options & OPT_ORDER))
+ {
+ forwardall = 1;
+ last_server = NULL;
+ }
type = forward->sentto->flags & SERV_TYPE;
- if (!(forward->sentto = forward->sentto->next))
- forward->sentto = servers; /* at end of list, recycle */
+ if (!(start = forward->sentto->next))
+ start = servers; /* at end of list, recycle */
header->id = htons(forward->new_id);
}
else
@@ -148,29 +153,25 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
unsigned int namelen = strlen(dnamebuff);
unsigned int matchlen = 0;
-
+ struct server *serv;
+
for (serv=servers; serv; serv=serv->next)
/* domain matches take priority over NODOTS matches */
if ((serv->flags & SERV_FOR_NODOTS) && type != SERV_HAS_DOMAIN && !strchr(dnamebuff, '.'))
{
- if (serv->flags & SERV_LITERAL_ADDRESS)
+ unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
+ type = SERV_FOR_NODOTS;
+ flags = 0;
+ if ((serv->flags & SERV_LITERAL_ADDRESS) && (sflag & gotname))
{
- /* flags gets set if server is in fact an answer */
- unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
- if (sflag & gotname) /* only OK if addrfamily == query */
- {
- type = SERV_FOR_NODOTS;
- flags = sflag;
- if (serv->addr.sa.sa_family == AF_INET)
- addrp = (struct all_addr *)&serv->addr.in.sin_addr;
+ flags = sflag;
+ if (serv->addr.sa.sa_family == AF_INET)
+ addrp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
- else
- addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+ else
+ addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
- }
}
- else
- flags = 0;
}
else if (serv->flags & SERV_HAS_DOMAIN)
{
@@ -179,29 +180,20 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
hostname_isequal(dnamebuff + namelen - domainlen, serv->domain) &&
domainlen >= matchlen)
{
- if (serv->flags & SERV_LITERAL_ADDRESS)
- { /* flags gets set if server is in fact an answer */
- unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
- if ((sflag | F_QUERY ) & gotname) /* only OK if addrfamily == query */
- {
- type = SERV_HAS_DOMAIN;
- flags = gotname;
- domain = serv->domain;
- matchlen = domainlen;
- if (serv->addr.sa.sa_family == AF_INET)
- addrp = (struct all_addr *)&serv->addr.in.sin_addr;
+ unsigned short sflag = serv->addr.sa.sa_family == AF_INET ? F_IPV4 : F_IPV6;
+ type = SERV_HAS_DOMAIN;
+ domain = serv->domain;
+ matchlen = domainlen;
+ flags = 0;
+ if ((serv->flags & SERV_LITERAL_ADDRESS) && ((sflag | F_QUERY ) & gotname))
+ {
+ flags = gotname;
+ if (serv->addr.sa.sa_family == AF_INET)
+ addrp = (struct all_addr *)&serv->addr.in.sin_addr;
#ifdef HAVE_IPV6
- else
- addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
+ else
+ addrp = (struct all_addr *)&serv->addr.in6.sin6_addr;
#endif
- }
- }
- else
- {
- flags = 0; /* may be better match from previous literal */
- domain = serv->domain;
- matchlen = domainlen;
- type = SERV_HAS_DOMAIN;
}
}
}
@@ -231,10 +223,13 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
otherwise, use the one last known to work. */
if (type != 0 || (options & OPT_ORDER))
- forward->sentto = servers;
- else
- forward->sentto = last_server;
-
+ start = servers;
+ else if (!(start = last_server))
+ {
+ start = servers;
+ forwardall = 1;
+ }
+
forward->source = *udpaddr;
forward->dest = *dst_addr;
forward->new_id = get_id();
@@ -250,52 +245,52 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
if (!flags && forward)
{
- struct server *firstsentto = forward->sentto;
-
+ struct server *firstsentto = start;
+ int forwarded = 0;
+
while (1)
{
- int logflags = 0;
-
- if (forward->sentto->addr.sa.sa_family == AF_INET)
- {
- logflags = F_SERVER | F_IPV4 | F_FORWARD;
- addrp = (struct all_addr *)&forward->sentto->addr.in.sin_addr;
- }
-#ifdef HAVE_IPV6
- else
- {
- logflags = F_SERVER | F_IPV6 | F_FORWARD;
- addrp = (struct all_addr *)&forward->sentto->addr.in6.sin6_addr;
- }
-#endif
/* only send to servers dealing with our domain.
domain may be NULL, in which case server->domain
must be NULL also. */
- if (type == (forward->sentto->flags & SERV_TYPE) &&
- (type != SERV_HAS_DOMAIN || hostname_isequal(domain, forward->sentto->domain)))
+ if (type == (start->flags & SERV_TYPE) &&
+ (type != SERV_HAS_DOMAIN || hostname_isequal(domain, start->domain)))
{
- if (forward->sentto->flags & SERV_NO_ADDR)
+ if (start->flags & SERV_NO_ADDR)
flags = F_NOERR; /* NULL servers are OK. */
- else if (!(forward->sentto->flags & SERV_LITERAL_ADDRESS) &&
- sendto(forward->sentto->sfd->fd, (char *)header, plen, 0,
- &forward->sentto->addr.sa,
- sa_len(&forward->sentto->addr)) != -1)
+ else if (!(start->flags & SERV_LITERAL_ADDRESS) &&
+ sendto(start->sfd->fd, (char *)header, plen, 0,
+ &start->addr.sa,
+ sa_len(&start->addr)) != -1)
{
- log_query(logflags, gotname ? dnamebuff : "query", addrp);
- /* for no-domain, don't update last_server */
- return domain ? last_server : (forward->sentto->next ? forward->sentto->next : servers);
+ if (!gotname)
+ strcpy(dnamebuff, "query");
+ if (start->addr.sa.sa_family == AF_INET)
+ log_query(F_SERVER | F_IPV4 | F_FORWARD, dnamebuff,
+ (struct all_addr *)&start->addr.in.sin_addr);
+#ifdef HAVE_IPV6
+ else
+ log_query(F_SERVER | F_IPV6 | F_FORWARD, dnamebuff,
+ (struct all_addr *)&start->addr.in6.sin6_addr);
+#endif
+ forwarded = 1;
+ forward->sentto = start;
+ if (!forwardall)
+ break;
}
}
- if (!(forward->sentto = forward->sentto->next))
- forward->sentto = servers;
+ if (!(start = start->next))
+ start = servers;
- /* check if we tried all without success */
- if (forward->sentto == firstsentto)
+ if (start == firstsentto)
break;
}
+ if (forwarded)
+ return last_server;
+
/* could not send on, prepare to return */
header->id = htons(forward->orig_id);
forward->new_id = 0; /* cancel */
@@ -312,52 +307,81 @@ static struct server *forward_query(int udpfd, union mysockaddr *udpaddr,
}
/* returns new last_server */
-struct server *reply_query(int fd, int options, char *packet, time_t now,
- char *dnamebuff, struct server *last_server,
+struct server *reply_query(struct serverfd *sfd, int options, char *packet, time_t now,
+ char *dnamebuff, struct server *servers, struct server *last_server,
struct bogus_addr *bogus_nxdomain, struct doctor *doctors)
{
/* packet from peer server, extract data for cache, and send to
original requester */
struct frec *forward;
HEADER *header;
- int n = recv(fd, packet, PACKETSZ, 0);
+ union mysockaddr serveraddr;
+ socklen_t addrlen = sizeof(serveraddr);
+ int n = recvfrom(sfd->fd, packet, PACKETSZ, 0, &serveraddr.sa, &addrlen);
+
+ /* Determine the address of the server replying so that we can mark that as good */
+ serveraddr.sa.sa_family = sfd->source_addr.sa.sa_family;
+#ifdef HAVE_IPV6
+ if (serveraddr.sa.sa_family == AF_INET6)
+ serveraddr.in6.sin6_flowinfo = htonl(0);
+#endif
header = (HEADER *)packet;
- if (n >= (int)sizeof(HEADER) && header->qr)
+ if (n >= (int)sizeof(HEADER) && header->qr && (forward = lookup_frec(ntohs(header->id))))
{
- if ((forward = lookup_frec(ntohs(header->id))))
+ /* find good server by address if possible, otherwise assume the last one we sent to */
+ if ((forward->sentto->flags & SERV_TYPE) == 0)
+ {
+ for (last_server = servers; last_server; last_server = last_server->next)
+ if (!(last_server->flags & (SERV_LITERAL_ADDRESS | SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_NO_ADDR)) &&
+ sockaddr_isequal(&last_server->addr, &serveraddr))
+ break;
+ if (!last_server)
+ last_server = forward->sentto;
+ }
+
+ /* Complain loudly if the upstream server is non-recursive. */
+ if (!header->ra && header->rcode == NOERROR && ntohs(header->ancount) == 0)
{
- if (header->rcode == NOERROR || header->rcode == NXDOMAIN)
+ char addrbuff[ADDRSTRLEN];
+#ifdef HAVE_IPV6
+ if (serveraddr.sa.sa_family == AF_INET)
+ inet_ntop(AF_INET, &serveraddr.in.sin_addr, addrbuff, ADDRSTRLEN);
+ else if (serveraddr.sa.sa_family == AF_INET6)
+ inet_ntop(AF_INET6, &serveraddr.in6.sin6_addr, addrbuff, ADDRSTRLEN);
+#else
+ strcpy(addrbuff, inet_ntoa(serveraddr.in.sin_addr));
+#endif
+ syslog(LOG_WARNING, "nameserver %s refused to do a recursive query", addrbuff);
+ return NULL;
+ }
+
+ if ((header->rcode == NOERROR || header->rcode == NXDOMAIN) && header->opcode == QUERY)
+ {
+ if (!(bogus_nxdomain &&
+ header->rcode == NOERROR &&
+ check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
{
- if (!forward->sentto->domain)
- last_server = forward->sentto; /* known good */
- if (header->opcode == QUERY)
- {
- if (!(bogus_nxdomain &&
- header->rcode == NOERROR &&
- check_for_bogus_wildcard(header, (unsigned int)n, dnamebuff, bogus_nxdomain, now)))
- {
- if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
- extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
- else if (!(options & OPT_NO_NEG))
- extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
- }
- }
+ if (header->rcode == NOERROR && ntohs(header->ancount) != 0)
+ extract_addresses(header, (unsigned int)n, dnamebuff, now, doctors);
+ else if (!(options & OPT_NO_NEG))
+ extract_neg_addrs(header, (unsigned int)n, dnamebuff, now);
}
- header->id = htons(forward->orig_id);
- /* There's no point returning an upstream reply marked as truncated,
- since that will prod the resolver into moving to TCP - which we
- don't support. */
- header->tc = 0; /* goodbye truncate */
- send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
- forward->new_id = 0; /* cancel */
}
+
+ header->id = htons(forward->orig_id);
+ /* There's no point returning an upstream reply marked as truncated,
+ since that will prod the resolver into moving to TCP - which we
+ don't support. */
+ header->tc = 0; /* goodbye truncate */
+ send_from(forward->fd, options & OPT_NOWILD, packet, n, &forward->source, &forward->dest);
+ forward->new_id = 0; /* cancel */
}
-
+
return last_server;
}
-struct server *receive_query(struct listener *listen, char *packet, char *mxname,
+struct server *receive_query(struct listener *listen, char *packet, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *namebuff,
struct iname *names, struct iname *addrs, struct iname *except,
@@ -395,7 +419,8 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
msg.msg_iov = iov;
msg.msg_iovlen = 1;
- n = recvmsg(listen->fd, &msg, 0);
+ if ((n = recvmsg(listen->fd, &msg, 0)) == -1)
+ return last_server;
source_addr.sa.sa_family = listen->family;
#ifdef HAVE_IPV6
@@ -502,7 +527,7 @@ struct server *receive_query(struct listener *listen, char *packet, char *mxname
}
m = answer_request (header, ((char *) header) + PACKETSZ, (unsigned int)n,
- mxname, mxtarget, options, now, local_ttl, namebuff);
+ mxnames, mxtarget, options, now, local_ttl, namebuff);
if (m >= 1)
send_from(listen->fd, options & OPT_NOWILD, (char *)header, m, &source_addr, &dst_addr);
else
diff --git a/src/network.c b/src/network.c
index e2f0f83..2c0e0f4 100644
--- a/src/network.c
+++ b/src/network.c
@@ -30,14 +30,17 @@ static struct irec *add_iface(struct irec *list, char *name, union mysockaddr *a
/* we may need to check the whitelist */
if (names || addrs)
{
+ int found = 0;
+
for (tmp = names; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))
- break;
- if (!tmp)
- for (tmp = addrs; tmp; tmp = tmp->next)
- if (sockaddr_isequal(&tmp->addr, addr))
- break;
- if (!tmp)
+ found = tmp->used = 1;
+
+ for (tmp = addrs; tmp; tmp = tmp->next)
+ if (sockaddr_isequal(&tmp->addr, addr))
+ found = tmp->used = 1;
+
+ if (!found)
return list;
}
@@ -72,7 +75,7 @@ struct irec *enumerate_interfaces(struct iname **names,
if (fd == -1)
die ("cannot create socket to enumerate interfaces: %s", NULL);
-
+
while (1)
{
buf = safe_malloc(len);
@@ -137,15 +140,24 @@ struct irec *enumerate_interfaces(struct iname **names,
die("ioctl error getting interface flags: %m", NULL);
/* If we are restricting the set of interfaces to use, make
- sure that loopback interfaces are in that set. Note that
- this is done as addresses rather than interface names so
- as not to confuse the no-IPRECVIF workaround on the DHCP code */
+ sure that loopback interfaces are in that set. */
if (*names && (ifr->ifr_flags & IFF_LOOPBACK))
{
- struct iname *lo = safe_malloc(sizeof(struct iname));
- lo->addr = addr;
- lo->next = *addrs;
- *addrs = lo;
+ struct iname *lo;
+ for (lo = *names; lo; lo = lo->next)
+ if (lo->name && strcmp(lo->name, ifr->ifr_name) == 0)
+ {
+ lo->isloop = 1;
+ break;
+ }
+ if (!lo)
+ {
+ lo = safe_malloc(sizeof(struct iname));
+ lo->name = safe_string_alloc(ifr->ifr_name);
+ lo->isloop = lo->used = 1;
+ lo->next = *names;
+ *names = lo;
+ }
}
iface = add_iface(iface, ifr->ifr_name, &addr, *names, *addrs, except);
@@ -192,17 +204,7 @@ struct irec *enumerate_interfaces(struct iname **names,
}
if (found)
- {
- if (*names && (ifr->ifr_flags & IFF_LOOPBACK))
- {
- struct iname *lo = safe_malloc(sizeof(struct iname));
- lo->addr = addr6;
- lo->next = *addrs;
- *addrs = lo;
- }
-
- iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except);
- }
+ iface = add_iface(iface, ifr->ifr_name, &addr6, *names, *addrs, except);
}
#endif /* LINUX */
}
@@ -220,6 +222,9 @@ struct irec *enumerate_interfaces(struct iname **names,
struct listener *create_wildcard_listeners(int port)
{
+#if !(defined(IP_PKTINFO) || (defined(IP_RECVDSTADDR) && defined(IP_RECVIF) && defined(IP_SENDSRCADDR)))
+ return NULL;
+#else
union mysockaddr addr;
int opt = 1;
struct listener *listen;
@@ -235,7 +240,10 @@ struct listener *create_wildcard_listeners(int port)
#endif
listen = safe_malloc(sizeof(struct listener));
if ((listen->fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
- die("failed to create socket: %s", NULL);
+ {
+ free(listen);
+ return NULL;
+ }
if (setsockopt(listen->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
#if defined(IP_PKTINFO)
setsockopt(listen->fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 ||
@@ -244,7 +252,11 @@ struct listener *create_wildcard_listeners(int port)
setsockopt(listen->fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 ||
#endif
bind(listen->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
- die("failed to bind socket: %s", NULL);
+ {
+ close(listen->fd);
+ free(listen);
+ return NULL;
+ }
listen->next = NULL;
listen->family = AF_INET;
#ifdef HAVE_IPV6
@@ -260,7 +272,11 @@ struct listener *create_wildcard_listeners(int port)
if (errno != EPROTONOSUPPORT &&
errno != EAFNOSUPPORT &&
errno != EINVAL)
- die("failed to create IPv6 socket: %s", NULL);
+ {
+ close(listen->fd);
+ free(listen);
+ return NULL;
+ }
}
else
{
@@ -271,11 +287,19 @@ struct listener *create_wildcard_listeners(int port)
if (setsockopt(listen->next->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 ||
setsockopt(listen->next->fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1 ||
bind(listen->next->fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1)
- die("failed to bind IPv6 socket: %s", NULL);
+ {
+ close(listen->next->fd);
+ free(listen->next);
+ close(listen->fd);
+ free(listen);
+ return NULL;
+ }
}
#endif
return listen;
+
+#endif
}
struct listener *create_bound_listeners(struct irec *interfaces)
@@ -455,7 +479,7 @@ struct server *reload_servers(char *fname, char *buff, struct server *serv, int
if (!token || strcmp(token, "nameserver") != 0)
continue;
- if (!(token = strtok(NULL, " \t\n")))
+ if (!(token = strtok(NULL, " \t\n\r")))
continue;
#ifdef HAVE_IPV6
diff --git a/src/option.c b/src/option.c
index 077c384..8b1c51b 100644
--- a/src/option.c
+++ b/src/option.c
@@ -154,7 +154,7 @@ static char *usage =
unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **resolv_files,
- char **mxname, char **mxtarget, char **lease_file,
+ struct mx_record **mxnames, char **mxtarget, char **lease_file,
char **username, char **groupname, char **domain_suffix, char **runfile,
struct iname **if_names, struct iname **if_addrs, struct iname **if_except,
struct bogus_addr **bogus_addr, struct server **serv_addrs, int *cachesize, int *port,
@@ -346,11 +346,22 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
}
case 'm':
- if (!canonicalise(optarg))
- option = '?';
- else
- *mxname = safe_string_alloc(optarg);
- break;
+ {
+ char *comma = strchr(optarg, ',');
+ if (comma)
+ *(comma++) = 0;
+ if (!canonicalise(optarg) || (comma && !canonicalise(comma)))
+ option = '?';
+ else
+ {
+ struct mx_record *new = safe_malloc(sizeof(struct mx_record));
+ new->next = *mxnames;
+ *mxnames = new;
+ new->mxname = safe_string_alloc(optarg);
+ new->mxtarget = safe_string_alloc(comma); /* may be NULL */
+ }
+ break;
+ }
case 't':
if (!canonicalise(optarg))
@@ -371,7 +382,9 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
break;
case 's':
- if (!canonicalise(optarg))
+ if (strcmp (optarg, "#") == 0)
+ flags |= OPT_RESOLV_DOMAIN;
+ else if (!canonicalise(optarg))
option = '?';
else
*domain_suffix = safe_string_alloc(optarg);
@@ -393,6 +406,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* new->name may be NULL if someone does
"interface=" to disable all interfaces except loop. */
new->name = safe_string_alloc(optarg);
+ new->isloop = new->used = 0;
if (strchr(optarg, ':'))
flags |= OPT_NOWILD;
break;
@@ -481,7 +495,7 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* # matches everything and becomes a zero length domain string */
if (strcmp(optarg, "#") == 0)
domain = "";
- else if (!canonicalise(optarg))
+ else if (!canonicalise(optarg) && strlen(optarg) != 0)
option = '?';
else
domain = safe_string_alloc(optarg); /* NULL if strlen is zero */
@@ -1191,13 +1205,18 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
/* only one of these need be specified: the other defaults to the
host-name */
- if ((flags & OPT_LOCALMX) || *mxname || *mxtarget)
+ if ((flags & OPT_LOCALMX) || *mxnames || *mxtarget)
{
if (gethostname(buff, MAXDNAME) == -1)
die("cannot get host-name: %s", NULL);
- if (!*mxname)
- *mxname = safe_string_alloc(buff);
+ if (!*mxnames)
+ {
+ *mxnames = safe_malloc(sizeof(struct mx_record));
+ (*mxnames)->next = NULL;
+ (*mxnames)->mxtarget = NULL;
+ (*mxnames)->mxname = safe_string_alloc(buff);
+ }
if (!*mxtarget)
*mxtarget = safe_string_alloc(buff);
@@ -1207,7 +1226,36 @@ unsigned int read_opts (int argc, char **argv, char *buff, struct resolvc **reso
*resolv_files = 0;
else if (*resolv_files && (*resolv_files)->next && (flags & OPT_NO_POLL))
die("only one resolv.conf file allowed in no-poll mode.", NULL);
+
+ if (flags & OPT_RESOLV_DOMAIN)
+ {
+ char *line;
+
+ if (!*resolv_files || (*resolv_files)->next)
+ die("must have exactly one resolv.conf to read domain from.", NULL);
+
+ if (!(f = fopen((*resolv_files)->name, "r")))
+ die("failed to read %s: %m", (*resolv_files)->name);
+
+ while ((line = fgets(buff, MAXDNAME, f)))
+ {
+ char *token = strtok(line, " \t\n\r");
+
+ if (!token || strcmp(token, "search") != 0)
+ continue;
+
+ if ((token = strtok(NULL, " \t\n\r")) &&
+ canonicalise(token) &&
+ (*domain_suffix = safe_string_alloc(token)))
+ break;
+ }
+
+ fclose(f);
+ if (!*domain_suffix)
+ die("no search directive found in %s", (*resolv_files)->name);
+ }
+
return flags;
}
diff --git a/src/rfc1035.c b/src/rfc1035.c
index 4f3df70..a587426 100644
--- a/src/rfc1035.c
+++ b/src/rfc1035.c
@@ -728,7 +728,7 @@ int check_for_bogus_wildcard(HEADER *header, unsigned int qlen, char *name,
}
/* return zero if we can't answer from cache, or packet size if we can */
-int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
+int answer_request(HEADER *header, char *limit, unsigned int qlen, struct mx_record *mxnames,
char *mxtarget, unsigned int options, time_t now,
unsigned long local_ttl, char *name)
{
@@ -988,9 +988,14 @@ int answer_request(HEADER *header, char *limit, unsigned int qlen, char *mxname,
if (qtype == T_MX || qtype == T_ANY)
{
- if (mxname && hostname_isequal(name, mxname))
+ struct mx_record *mx;
+ for (mx = mxnames; mx; mx = mx->next)
+ if (hostname_isequal(name, mx->mxname))
+ break;
+ if (mx)
{
- ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX, mxtarget);
+ ansp = add_text_record(nameoffset, ansp, local_ttl, 1, T_MX,
+ mx->mxtarget ? mx->mxtarget : mxtarget);
anscount++;
ans = 1;
}