Compare commits

..
13 Commits
Author SHA1 Message Date
Simon Kelley 1db9943c68 Extend specifying DNS servers by domain-name to --rev-server
Also Dbus SetDomainServers method.

Revert getaddrinfo hints.ai_socktype to SOCK_DGRAM to eliminate
duplicating every address three times for DGRAM, STREAM and RAW
in the results.
2022-11-06 21:10:19 +00:00
Simon Kelley 5b868c213b Fix breakage of --local=/domain.name/1.2.3.4 in immediately previous commit. 2022-11-06 20:18:27 +00:00
Dominik Derigs 2d8905dafd Allow domain names as well is IP addresses in --server options. 2022-11-05 11:49:52 +00:00
Simon Kelley 9002108551 Bump Debian version and close bug. 2022-11-02 22:18:35 +00:00
Simon Kelley d3c21c596e Reconcile "names" and "address" counts when reading hostfiles. 2022-10-30 15:40:20 +00:00
Simon Kelley 34fac952b6 Inotify: make "flushed" log message more understandable.
Saying we've "flushed x outdated entries" is confusing, since
the count is the total number of entries in the modified file,
most of which are going	to get added straight back when	the file
is re-read.

The log now looks like

dnsmasq: inotify: /tmp/dir/1 (new or modified)
dnsmasq: inotify: flushed 1 addresses read from /tmp/dir/1
dnsmasq: read /tmp/dir/1 - 2 addresses

which hopefully make it more obvious that /tmp/dir/1 contained one
address before, and now contains two.
2022-10-27 13:24:37 +01:00
Dominik Derigs 92c32e0bac Do not (try to) re-read deleted files inside a --hostsdir. 2022-10-27 12:36:38 +01:00
Simon Kelley 1bcad67806 Fix in dhcpv4 rapid-commit code.
1) Cosmetic: don't log the tags twice.

2) Functional. If a host has an old lease for a different address,
   the rapid-commit will appear to work, but the old lease will
   not be removed and the new lease will not be recorded, so
   the client and server will have conflicting state, leading to
   problems later.
2022-10-27 12:04:58 +01:00
Simon Kelley fe9a134baf Add --no-round-robin option. 2022-10-18 16:06:48 +01:00
Simon Kelley 930428fb97 Fix loss of DNS servers on config reload.
A bug, introduced in 2.87, which could result in DNS
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (ie hard coded or another dynamic source)
could mysteriously disappear.
2022-10-17 21:15:43 +01:00
Dominik Derigs 936be022d9 Handle multiple addresses when removing duplicates in host files. 2022-10-16 22:30:08 +01:00
Dominik Derigs 0017dd74d5 Enhance --hostdir so that records are automatically removed when re-reading.
Initial patch from Dominik Derigs, re-written by Simon Kelley.
2022-10-16 22:10:48 +01:00
Dominik Derigs 0ba25a0512 Improve logging of DNS record source from --hostsdir files.
Patch author Dominik Derigs <dl6er@dl6er.de> with subsequent bugfixes
and tweaks from Simon Kelley.
2022-10-16 21:14:16 +01:00
10 changed files with 532 additions and 185 deletions

No files matched your search

+27 -1
View File
@@ -18,8 +18,34 @@ version 2.88
flushed when its TTL expires, so the cache becomes
strictly least-recently-used.
Make --hostsdir (but NOT --dhcp-hostsdir and --dhcp-optsdir)
handle removal of whole files or entries within files.
Thanks to Dominik Derigs for the initial patches for this.
Fix bug, introduced in 2.87, which could result in DNS
servers being removed from the configuration when reloading
server configuration from DBus, or re-reading /etc/resolv.conf
Only servers from the same source should be replaced, but some
servers from other sources (ie hard coded or another dynamic source)
could mysteriously disappear. Thanks to all reporting this,
but especially Christopher J. Masden who reduced the problem
to an easily reproducible case which saved mucg labour in
finding it.
Add --no-round-robin option.
Allow domain names as well as IP addresses when specifying
upstream DNS servers. There are some gotchas associated with this,
(it will mysteriously fail to work if the dnsmasq instance
being started is in the path from the system resolver to the DNS)
and a seemingly sensible configuration like
--server=domain.name@1.2.3.4 is unactionable if domain.name
only resolves to an IPv6 address). There are, however,
cases where is can be useful. Thanks to Dominik Derigs for
the patch.
version 2.87
(version 2.87
Allow arbitrary prefix lengths in --rev-server and
--domain=....,local
+8
View File
@@ -1,3 +1,11 @@
dnsmasq (2.88-1) unstable; urgency=low
* New upstream.
* Fix loss of server configuration (closes: #1020830)
Git commit 930428fb970f4991e5c2933fd5a5d2504c18a551
-- Simon Kelley <simon@thekelleys.org.uk> Wed, 2 Nov 2022 22:15:45 +0000
dnsmasq (2.87-1) unstable; urgency=low
* New upstream. (closes: #1001209, #1003156)
+17 -4
View File
@@ -60,7 +60,8 @@ in alphabetical order.
.TP
.B --hostsdir=<path>
Read all the hosts files contained in the directory. New or changed files
are read automatically. See \fB--dhcp-hostsdir\fP for details.
are read automatically and modified and deleted files have removed records
automatically deleted.
.TP
.B \-E, --expand-hosts
Add the domain to simple names (without a period) in /etc/hosts
@@ -461,8 +462,8 @@ Tells dnsmasq to never forward A or AAAA queries for plain names, without dots
or domain parts, to upstream nameservers. If the name is not known
from /etc/hosts or DHCP then a "not found" answer is returned.
.TP
.B \-S, --local, --server=[/[<domain>]/[domain/]][<ipaddr>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify IP address of upstream servers directly. Setting this flag does
.B \-S, --local, --server=[/[<domain>]/[domain/]][<server>[#<port>]][@<interface>][@<source-ip>[#<port>]]
Specify upstream servers directly. Setting this flag does
not suppress reading of /etc/resolv.conf, use \fB--no-resolv\fP to do that. If one or more
optional domains are given, that server is used only for those domains
and they are queried only using the specified server. This is
@@ -530,8 +531,14 @@ The query-port flag is ignored for any servers which have a
source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
Upstream servers may be specified with a hostname rather than an IP address.
In this case, dnsmasq will try to use the system resolver to get the IP address
of a server during startup. If name resolution fails, starting dnsmasq fails, too.
If the system's configuration is such that the system resolver sends DNS queries
through the dnsmasq instance which is starting up then this will time-out and fail.
.TP
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
.B --rev-server=<ip-address>[/<prefix-len>][,<server>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
@@ -815,6 +822,12 @@ Disable negative caching. Negative caching allows dnsmasq to remember
"no such domain" answers from upstream nameservers and answer
identical queries without forwarding them again.
.TP
.B --no-round-robin
Dnsmasq normally permutes the order of A or AAAA records for the same
name on successive queries, for load-balancing. This turns off that
behaviour, so that the records are always returned in the order
that they are received from upstream.
.TP
.B --use-stale-cache
When set, if a DNS name exists in the cache, but its time-to-live has expired, dnsmasq will return the data anyway. (It attempts to refresh the
data with an upstream query after returning the stale data.) This can improve speed and reliability. It comes at the expense
+48 -17
View File
@@ -189,7 +189,7 @@ static void rehash(int size)
else if (new_size <= hash_size || !(new = whine_malloc(new_size * sizeof(struct crec *))))
return;
for(i = 0; i < new_size; i++)
for (i = 0; i < new_size; i++)
new[i] = NULL;
old = hash_table;
@@ -233,7 +233,8 @@ static void cache_hash(struct crec *crecp)
immortal entries are at the end of the hash-chain.
This allows reverse searches and garbage collection to be optimised */
struct crec **up = hash_bucket(cache_get_name(crecp));
char *name = cache_get_name(crecp);
struct crec **up = hash_bucket(name);
if (!(crecp->flags & F_REVERSE))
{
@@ -244,6 +245,11 @@ static void cache_hash(struct crec *crecp)
while (*up && !((*up)->flags & F_IMMORTAL))
up = &((*up)->hash_next);
}
/* Preserve order when inserting the same name multiple times. */
while (*up && hostname_isequal(cache_get_name(*up), name))
up = &((*up)->hash_next);
crecp->hash_next = *up;
*up = crecp;
}
@@ -388,6 +394,27 @@ static int is_expired(time_t now, struct crec *crecp)
return 1;
}
/* Remove entries with a given UID from the cache */
unsigned int cache_remove_uid(const unsigned int uid)
{
int i;
unsigned int removed = 0;
struct crec *crecp, **up;
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_HOSTS | F_DHCP | F_CONFIG)) && crecp->uid == uid)
{
*up = crecp->hash_next;
free(crecp);
removed++;
}
else
up = &crecp->hash_next;
return removed;
}
static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned short class, time_t now,
unsigned int flags, struct crec **target_crec, unsigned int *target_uid)
{
@@ -701,7 +728,7 @@ static struct crec *really_insert(char *name, union all_addr *addr, unsigned sho
new->ttd = now + (time_t)ttl;
new->next = new_chain;
new_chain = new;
return new;
}
@@ -879,7 +906,7 @@ int cache_find_non_terminal(char *name, time_t now)
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
{
struct crec *ans;
int no_rr = prot & F_NO_RR;
int no_rr = (prot & F_NO_RR) || option_bool(OPT_NORR);
prot &= ~F_NO_RR;
@@ -1027,16 +1054,17 @@ struct crec *cache_find_by_addr(struct crec *crecp, union all_addr *addr,
static void add_hosts_entry(struct crec *cache, union all_addr *addr, int addrlen,
unsigned int index, struct crec **rhash, int hashsz)
{
struct crec *lookup = cache_find_by_name(NULL, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6));
int i;
unsigned int j;
struct crec *lookup = NULL;
/* Remove duplicates in hosts files. */
if (lookup && (lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
while ((lookup = cache_find_by_name(lookup, cache_get_name(cache), 0, cache->flags & (F_IPV4 | F_IPV6))))
if ((lookup->flags & F_HOSTS) && memcmp(&lookup->addr, addr, addrlen) == 0)
{
free(cache);
return;
}
/* Ensure there is only one address -> name mapping (first one trumps)
We do this by steam here, The entries are kept in hash chains, linked
@@ -1141,7 +1169,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
{
FILE *f = fopen(filename, "r");
char *token = daemon->namebuff, *domain_suffix = NULL;
int addr_count = 0, name_count = cache_size, lineno = 1;
int names_done = 0, name_count = cache_size, lineno = 1;
unsigned int flags = 0;
union all_addr addr;
int atnl, addrlen = 0;
@@ -1177,8 +1205,6 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
continue;
}
addr_count++;
/* rehash every 1000 names. */
if (rhash && ((name_count - cache_size) > 1000))
{
@@ -1210,6 +1236,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
if ((cache = whine_malloc(SIZEOF_BARE_CREC + strlen(canon) + 1)))
{
@@ -1218,6 +1245,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
cache->ttd = daemon->local_ttl;
add_hosts_entry(cache, &addr, addrlen, index, rhash, hashsz);
name_count++;
names_done++;
}
free(canon);
@@ -1234,7 +1262,7 @@ int read_hostsfile(char *filename, unsigned int index, int cache_size, struct cr
if (rhash)
rehash(name_count);
my_syslog(LOG_INFO, _("read %s - %d addresses"), filename, addr_count);
my_syslog(LOG_INFO, _("read %s - %d names"), filename, names_done);
return name_count;
}
@@ -1861,6 +1889,7 @@ void dump_cache(time_t now)
char *record_source(unsigned int index)
{
struct hostsfile *ah;
struct dyndir *dd;
if (index == SRC_CONFIG)
return "config";
@@ -1872,9 +1901,11 @@ char *record_source(unsigned int index)
return ah->fname;
#ifdef HAVE_INOTIFY
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
/* Dynamic directories contain multiple files */
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
for (ah = dd->files; ah; ah = ah->next)
if (ah->index == index)
return ah->fname;
#endif
return "<unknown>";
+7 -1
View File
@@ -292,6 +292,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
u16 flags = 0;
char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL;
struct server_details sdetails = { 0 };
sdetails.addr = &addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (strings)
{
@@ -375,7 +380,8 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
}
/* parse the IP address */
if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
if ((addr_err = parse_server(str_addr, &sdetails)) ||
(addr_err = parse_server_addr(&sdetails)))
{
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s",
+25 -5
View File
@@ -133,6 +133,7 @@ typedef unsigned long long u64;
#include <sys/uio.h>
#include <syslog.h>
#include <dirent.h>
#include <netdb.h>
#ifndef HAVE_LINUX_NETWORK
# include <net/if_dl.h>
#endif
@@ -280,7 +281,8 @@ struct event_desc {
#define OPT_STRIP_ECS 69
#define OPT_STRIP_MAC 70
#define OPT_STALE_CACHE 71
#define OPT_LAST 72
#define OPT_NORR 72
#define OPT_LAST 73
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -687,10 +689,17 @@ struct hostsfile {
struct hostsfile *next;
int flags;
char *fname;
unsigned int index; /* matches to cache entries for logging */
};
struct dyndir {
struct dyndir *next;
struct hostsfile *files;
int flags;
char *dname;
#ifdef HAVE_INOTIFY
int wd; /* inotify watch descriptor */
#endif
unsigned int index; /* matches to cache entries for logging */
};
/* packet-dump flags */
@@ -1152,6 +1161,7 @@ extern struct daemon {
u32 umbrella_org;
u32 umbrella_asset;
u8 umbrella_device[8];
int host_index;
struct hostsfile *addn_hosts;
struct dhcp_context *dhcp, *dhcp6;
struct ra_interface *ra_interfaces;
@@ -1172,7 +1182,8 @@ extern struct daemon {
int doing_ra, doing_dhcp6;
struct dhcp_netid_list *dhcp_ignore, *dhcp_ignore_names, *dhcp_gen_names;
struct dhcp_netid_list *force_broadcast, *bootp_dynamic;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file, *dynamic_dirs;
struct hostsfile *dhcp_hosts_file, *dhcp_opts_file;
struct dyndir *dynamic_dirs;
int dhcp_max, tftp_max, tftp_mtu;
int dhcp_server_port, dhcp_client_port;
int start_tftp_port, end_tftp_port;
@@ -1282,6 +1293,14 @@ extern struct daemon {
#endif
} *daemon;
struct server_details {
union mysockaddr *addr, *source_addr;
struct addrinfo *hostinfo;
char *interface, *source, *scope_id, *interface_opt;
int serv_port, source_port, addr_type, scope_index, valid, resolved;
u16 *flags;
};
/* cache.c */
void cache_init(void);
void next_uid(struct crec *crecp);
@@ -1295,6 +1314,7 @@ struct crec *cache_find_by_name(struct crec *crecp,
char *name, time_t now, unsigned int prot);
void cache_end_insert(void);
void cache_start_insert(void);
unsigned int cache_remove_uid(const unsigned int uid);
int cache_recv_insert(time_t now, int fd);
struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class,
time_t now, unsigned long ttl, unsigned int flags);
@@ -1456,8 +1476,8 @@ void read_servers_file(void);
void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list);
char *parse_server(char *arg, union mysockaddr *addr,
union mysockaddr *source_addr, char *interface, u16 *flags);
char *parse_server(char *arg, struct server_details *sdetails);
char *parse_server_addr(struct server_details *sdetails);
int option_read_dynfile(char *file, int flags);
/* forward.c */
+3 -1
View File
@@ -683,7 +683,9 @@ int add_update_server(int flags,
serv->next = NULL;
}
break;
}
}
else
up = &serv->next;
}
if (serv)
+92 -45
View File
@@ -133,81 +133,112 @@ void inotify_dnsmasq_init()
}
}
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
{
/* Check if this file is already known in dd->files */
struct hostsfile *ah = NULL;
for(ah = dd->files; ah; ah = ah->next)
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
return ah;
/* Not known, create new hostsfile record for this dyndir */
struct hostsfile *newah = NULL;
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
return NULL;
/* Add this file to the tip of the linked list */
newah->next = dd->files;
dd->files = newah;
/* Copy flags, set index and the full file path */
newah->flags = dd->flags;
newah->index = daemon->host_index++;
newah->fname = path;
return newah;
}
/* initialisation for dynamic-dir. Set inotify watch for each directory, and read pre-existing files */
void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revhashsz)
{
struct hostsfile *ah;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
struct dyndir *dd;
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
{
DIR *dir_stream = NULL;
struct dirent *ent;
struct stat buf;
if (!(ah->flags & flag))
if (!(dd->flags & flag))
continue;
if (stat(ah->fname, &buf) == -1)
if (stat(dd->dname, &buf) == -1)
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
if (!(S_ISDIR(buf.st_mode)))
{
my_syslog(LOG_ERR, _("bad dynamic directory %s: %s"),
ah->fname, _("not a directory"));
dd->dname, _("not a directory"));
continue;
}
if (!(ah->flags & AH_WD_DONE))
if (!(dd->flags & AH_WD_DONE))
{
ah->wd = inotify_add_watch(daemon->inotifyfd, ah->fname, IN_CLOSE_WRITE | IN_MOVED_TO);
ah->flags |= AH_WD_DONE;
dd->wd = inotify_add_watch(daemon->inotifyfd, dd->dname, IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE);
dd->flags |= AH_WD_DONE;
}
/* Read contents of dir _after_ calling add_watch, in the hope of avoiding
a race which misses files being added as we start */
if (ah->wd == -1 || !(dir_stream = opendir(ah->fname)))
if (dd->wd == -1 || !(dir_stream = opendir(dd->dname)))
{
my_syslog(LOG_ERR, _("failed to create inotify for %s: %s"),
ah->fname, strerror(errno));
dd->dname, strerror(errno));
continue;
}
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
(ent->d_name[0] == '#' && ent->d_name[lenfile - 1] == '#') ||
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
if (!(ah = dyndir_addhosts(dd, path)))
{
free(path);
continue;
}
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (ah->flags & AH_HOSTS)
if (dd->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
#ifdef HAVE_DHCP
else if (ah->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, ah->flags);
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
option_read_dynfile(path, dd->flags);
#endif
}
free(path);
}
}
@@ -218,7 +249,7 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
int inotify_check(time_t now)
{
int hit = 0;
struct hostsfile *ah;
struct dyndir *dd;
while (1)
{
@@ -249,36 +280,51 @@ int inotify_check(time_t now)
if (res->wd == in->wd && strcmp(res->file, in->name) == 0)
hit = 1;
for (ah = daemon->dynamic_dirs; ah; ah = ah->next)
if (ah->wd == in->wd)
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
if (dd->wd == in->wd)
{
size_t lendir = strlen(ah->fname);
size_t lendir = strlen(dd->dname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
{
strcpy(path, ah->fname);
struct hostsfile *ah = NULL;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify, new or changed file %s"), path);
if (ah->flags & AH_HOSTS)
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if (dd->flags & AH_HOSTS)
{
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
if ((ah = dyndir_addhosts(dd, path)))
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
const unsigned int removed = cache_remove_uid(ah->index);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(path, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
}
#ifdef HAVE_DHCP
else if (ah->flags & AH_DHCP_HST)
else if (dd->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
{
@@ -289,11 +335,12 @@ int inotify_check(time_t now)
lease_update_dns(1);
}
}
else if (ah->flags & AH_DHCP_OPT)
else if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
free(path);
if (!ah)
free(path);
}
}
}
+297 -110
View File
@@ -184,6 +184,7 @@ struct myoption {
#define LOPT_RANDPORT_LIM 375
#define LOPT_FAST_RETRY 376
#define LOPT_STALE_CACHE 377
#define LOPT_NORR 378
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -236,6 +237,7 @@ static const struct myoption opts[] =
{ "localmx", 0, 0, 'L' },
{ "local-ttl", 1, 0, 'T' },
{ "no-negcache", 0, 0, 'N' },
{ "no-round-robin", 0, 0, LOPT_NORR },
{ "addn-hosts", 1, 0, 'H' },
{ "hostsdir", 1, 0, LOPT_HOST_INOTIFY },
{ "query-port", 1, 0, 'Q' },
@@ -567,6 +569,7 @@ static struct {
{ LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
{ LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
{ LOPT_QUIET_TFTP, OPT_QUIET_TFTP, NULL, gettext_noop("Do not log routine TFTP."), NULL },
{ LOPT_NORR, OPT_NORR, NULL, gettext_noop("Suppress round-robin ordering of DNS records."), NULL },
{ 0, 0, NULL, NULL, NULL }
};
@@ -852,117 +855,236 @@ static char *parse_mysockaddr(char *arg, union mysockaddr *addr)
return NULL;
}
char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_addr, char *interface, u16 *flags)
char *parse_server(char *arg, struct server_details *sdetails)
{
int source_port = 0, serv_port = NAMESERVER_PORT;
char *portno, *source;
char *interface_opt = NULL;
int scope_index = 0;
char *scope_id;
sdetails->serv_port = NAMESERVER_PORT;
char *portno;
int ecode = 0;
struct addrinfo hints = { 0 };
*interface = 0;
*sdetails->interface = 0;
if (strcmp(arg, "#") == 0)
{
if (flags)
*flags |= SERV_USE_RESOLV;
if (sdetails->flags)
*sdetails->flags |= SERV_USE_RESOLV;
sdetails->valid = 1;
return NULL;
}
if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) &&
!atoi_check16(portno, &source_port))
if ((sdetails->source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(sdetails->source, '#')) &&
!atoi_check16(portno, &sdetails->source_port))
return _("bad port");
if ((portno = split_chr(arg, '#')) && /* is there a port no. */
!atoi_check16(portno, &serv_port))
!atoi_check16(portno, &sdetails->serv_port))
return _("bad port");
scope_id = split_chr(arg, '%');
sdetails->scope_id = split_chr(arg, '%');
if (source) {
interface_opt = split_chr(source, '@');
if (sdetails->source) {
sdetails->interface_opt = split_chr(sdetails->source, '@');
if (interface_opt)
if (sdetails->interface_opt)
{
#if defined(SO_BINDTODEVICE)
safe_strncpy(interface, source, IF_NAMESIZE);
source = interface_opt;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
sdetails->source = sdetails->interface_opt;
#else
return _("interface binding not supported");
#endif
}
}
if (inet_pton(AF_INET, arg, &addr->in.sin_addr) > 0)
if (inet_pton(AF_INET, arg, &sdetails->addr->in.sin_addr) > 0)
sdetails->addr_type = AF_INET;
else if (inet_pton(AF_INET6, arg, &sdetails->addr->in6.sin6_addr) > 0)
sdetails->addr_type = AF_INET6;
else
{
addr->in.sin_port = htons(serv_port);
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET;
/* if the argument is neither an IPv4 not an IPv6 address, it might be a
hostname and we should try to resolve it to a suitable address. */
memset(&hints, 0, sizeof(hints));
/* The AI_ADDRCONFIG flag ensures that then IPv4 addresses are returned in
the result only if the local system has at least one IPv4 address
configured, and IPv6 addresses are returned only if the local system
has at least one IPv6 address configured. The loopback address is not
considered for this case as valid as a configured address. This flag is
useful on, for example, IPv4-only systems, to ensure that getaddrinfo()
does not return IPv6 socket addresses that would always fail in
subsequent connect() or bind() attempts. */
hints.ai_flags = AI_ADDRCONFIG;
#if defined(HAVE_IDN) && defined(AI_IDN)
/* If the AI_IDN flag is specified and we have glibc 2.3.4 or newer, then
the node name given in node is converted to IDN format if necessary.
The source encoding is that of the current locale. */
hints.ai_flags |= AI_IDN;
#endif
/* The value AF_UNSPEC indicates that getaddrinfo() should return socket
addresses for any address family (either IPv4 or IPv6, for example)
that can be used with node <arg> and service "domain". */
hints.ai_family = AF_UNSPEC;
/* Get addresses suitable for sending datagrams. We assume that we can use the
same addresses for TCP connections. Settting this to zero gets each address
threes times, for SOCK_STREAM, SOCK_RAW and SOCK_DGRAM, which is not useful. */
hints.ai_socktype = SOCK_DGRAM;
/* Get address associated with this hostname */
ecode = getaddrinfo(arg, NULL, &hints, &sdetails->hostinfo);
if (ecode == 0)
{
/* The getaddrinfo() function allocated and initialized a linked list of
addrinfo structures, one for each network address that matches node
and service, subject to the restrictions imposed by our <hints>
above, and returns a pointer to the start of the list in <hostinfo>.
The items in the linked list are linked by the <ai_next> field. */
sdetails->valid = 1;
sdetails->resolved = 1;
return NULL;
}
else
{
/* Lookup failed, return human readable error string */
if (ecode == EAI_AGAIN)
return _("Cannot resolve server name");
else
return _((char*)gai_strerror(ecode));
}
}
sdetails->valid = 1;
return NULL;
}
char *parse_server_addr(struct server_details *sdetails)
{
if (sdetails->addr_type == AF_INET)
{
sdetails->addr->in.sin_port = htons(sdetails->serv_port);
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET;
#ifdef HAVE_SOCKADDR_SA_LEN
source_addr->in.sin_len = addr->in.sin_len = sizeof(struct sockaddr_in);
#endif
source_addr->in.sin_addr.s_addr = INADDR_ANY;
source_addr->in.sin_port = htons(daemon->query_port);
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
sdetails->source_addr->in.sin_port = htons(daemon->query_port);
if (source)
if (sdetails->source)
{
if (flags)
*flags |= SERV_HAS_SOURCE;
source_addr->in.sin_port = htons(source_port);
if (!(inet_pton(AF_INET, source, &source_addr->in.sin_addr) > 0))
if (sdetails->flags)
*sdetails->flags |= SERV_HAS_SOURCE;
sdetails->source_addr->in.sin_port = htons(sdetails->source_port);
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 0)
{
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 1)
{
sdetails->source_addr->sa.sa_family = AF_INET6;
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if (!sdetails->resolved)
return _("cannot use IPv4 server address with IPv6 source address");
}
else
{
#if defined(SO_BINDTODEVICE)
if (interface_opt)
return _("interface can only be specified once");
source_addr->in.sin_addr.s_addr = INADDR_ANY;
safe_strncpy(interface, source, IF_NAMESIZE);
if (sdetails->interface_opt)
return _("interface can only be specified once");
sdetails->source_addr->in.sin_addr.s_addr = INADDR_ANY;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
return _("interface binding not supported");
return _("interface binding not supported");
#endif
}
}
}
}
else if (inet_pton(AF_INET6, arg, &addr->in6.sin6_addr) > 0)
else if (sdetails->addr_type == AF_INET6)
{
if (scope_id && (scope_index = if_nametoindex(scope_id)) == 0)
if (sdetails->scope_id && (sdetails->scope_index = if_nametoindex(sdetails->scope_id)) == 0)
return _("bad interface name");
addr->in6.sin6_port = htons(serv_port);
addr->in6.sin6_scope_id = scope_index;
source_addr->in6.sin6_addr = in6addr_any;
source_addr->in6.sin6_port = htons(daemon->query_port);
source_addr->in6.sin6_scope_id = 0;
addr->sa.sa_family = source_addr->sa.sa_family = AF_INET6;
addr->in6.sin6_flowinfo = source_addr->in6.sin6_flowinfo = 0;
sdetails->addr->in6.sin6_port = htons(sdetails->serv_port);
sdetails->addr->in6.sin6_scope_id = sdetails->scope_index;
sdetails->source_addr->in6.sin6_addr = in6addr_any;
sdetails->source_addr->in6.sin6_port = htons(daemon->query_port);
sdetails->source_addr->in6.sin6_scope_id = 0;
sdetails->addr->sa.sa_family = sdetails->source_addr->sa.sa_family = AF_INET6;
sdetails->addr->in6.sin6_flowinfo = sdetails->source_addr->in6.sin6_flowinfo = 0;
#ifdef HAVE_SOCKADDR_SA_LEN
addr->in6.sin6_len = source_addr->in6.sin6_len = sizeof(addr->in6);
sdetails->addr->in6.sin6_len = sdetails->source_addr->in6.sin6_len = sizeof(addr->in6);
#endif
if (source)
if (sdetails->source)
{
if (flags)
*flags |= SERV_HAS_SOURCE;
source_addr->in6.sin6_port = htons(source_port);
if (inet_pton(AF_INET6, source, &source_addr->in6.sin6_addr) == 0)
if (sdetails->flags)
*sdetails->flags |= SERV_HAS_SOURCE;
sdetails->source_addr->in6.sin6_port = htons(sdetails->source_port);
if (inet_pton(AF_INET6, sdetails->source, &sdetails->source_addr->in6.sin6_addr) == 0)
{
if (inet_pton(AF_INET, sdetails->source, &sdetails->source_addr->in.sin_addr) == 1)
{
sdetails->source_addr->sa.sa_family = AF_INET;
/* When resolving a server IP by hostname, we can simply skip mismatching
server / source IP pairs. Otherwise, when an IP address is given directly,
this is a fatal error. */
if(!sdetails->resolved)
return _("cannot use IPv6 server address with IPv4 source address");
}
else
{
#if defined(SO_BINDTODEVICE)
if (interface_opt)
return _("interface can only be specified once");
source_addr->in6.sin6_addr = in6addr_any;
safe_strncpy(interface, source, IF_NAMESIZE);
if (sdetails->interface_opt)
return _("interface can only be specified once");
sdetails->source_addr->in6.sin6_addr = in6addr_any;
safe_strncpy(sdetails->interface, sdetails->source, IF_NAMESIZE);
#else
return _("interface binding not supported");
return _("interface binding not supported");
#endif
}
}
}
}
else
return _("bad address");
return NULL;
}
static int parse_server_next(struct server_details *sdetails)
{
/* Looping over resolved addresses? */
if (sdetails->hostinfo)
{
/* Get address type */
sdetails->addr_type = sdetails->hostinfo->ai_family;
/* Get address */
if (sdetails->addr_type == AF_INET)
memcpy(&sdetails->addr->in.sin_addr,
&((struct sockaddr_in *) sdetails->hostinfo->ai_addr)->sin_addr,
sizeof(sdetails->addr->in.sin_addr));
else if (sdetails->addr_type == AF_INET6)
memcpy(&sdetails->addr->in6.sin6_addr,
&((struct sockaddr_in6 *) sdetails->hostinfo->ai_addr)->sin6_addr,
sizeof(sdetails->addr->in6.sin6_addr));
/* Iterate to the next available address */
sdetails->valid = sdetails->hostinfo->ai_next != NULL;
sdetails->hostinfo = sdetails->hostinfo->ai_next;
return 1;
}
else if (sdetails->valid)
{
/* When using an IP address, we return the address only once */
sdetails->valid = 0;
return 1;
}
/* Stop iterating here, we used all available addresses */
return 0;
}
static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
{
int i, j;
@@ -973,22 +1095,27 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails = { 0 };
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
else if ((string = parse_server(server, &sdetails)))
return string;
if (from_file)
flags |= SERV_FROM_FILE;
rem = size & 0x7;
addrbytes = (32 - size) >> 3;
addrbits = (32 - size) & 7;
if (size > 32 || size < 1)
return _("bad IPv4 prefix length");
/* Zero out last address bits according to CIDR mask */
((u8 *)addr4)[3-addrbytes] &= ~((1 << addrbits)-1);
@@ -1002,23 +1129,37 @@ static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int
*domain = 0;
string = domain;
msize = size/8;
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
{
int dig = ((unsigned char *)addr4)[j];
if (j == msize)
dig += i;
string += sprintf(string, "%d.", dig);
}
sprintf(string, "in-addr.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
if (flags & SERV_LITERAL_ADDRESS)
{
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
else
{
while (parse_server_next(&sdetails))
{
if ((string = parse_server_addr(&sdetails)))
return string;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
}
}
return NULL;
}
@@ -1032,10 +1173,15 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
struct server_details sdetails = { 0 };
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
else if ((string = parse_server(server, &sdetails)))
return string;
if (from_file)
@@ -1076,10 +1222,24 @@ static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, in
sprintf(string, "ip6.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
if (flags & SERV_LITERAL_ADDRESS)
{
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
else
{
while (parse_server_next(&sdetails))
{
if ((string = parse_server_addr(&sdetails)))
return string;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
}
}
return NULL;
}
@@ -2162,15 +2322,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
case LOPT_DHCP_HOST: /* --dhcp-hostsfile */
case LOPT_DHCP_OPTS: /* --dhcp-optsfile */
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
case LOPT_HOST_INOTIFY: /* --hostsdir */
case 'H': /* --addn-hosts */
{
struct hostsfile *new = opt_malloc(sizeof(struct hostsfile));
static unsigned int hosts_index = SRC_AH;
new->fname = opt_string_alloc(arg);
new->index = hosts_index++;
new->index = daemon->host_index++;
new->flags = 0;
if (option == 'H')
{
@@ -2186,21 +2342,29 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
new->next = daemon->dhcp_opts_file;
daemon->dhcp_opts_file = new;
}
else
{
new->next = daemon->dynamic_dirs;
daemon->dynamic_dirs = new;
if (option == LOPT_DHCP_INOTIFY)
new->flags |= AH_DHCP_HST;
else if (option == LOPT_DHOPT_INOTIFY)
new->flags |= AH_DHCP_OPT;
else if (option == LOPT_HOST_INOTIFY)
new->flags |= AH_HOSTS;
}
break;
}
case LOPT_DHCP_INOTIFY: /* --dhcp-hostsdir */
case LOPT_DHOPT_INOTIFY: /* --dhcp-optsdir */
case LOPT_HOST_INOTIFY: /* --hostsdir */
{
struct dyndir *new = opt_malloc(sizeof(struct dyndir));
new->dname = opt_string_alloc(arg);
new->flags = 0;
new->next = daemon->dynamic_dirs;
daemon->dynamic_dirs = new;
if (option == LOPT_DHCP_INOTIFY)
new->flags |= AH_DHCP_HST;
else if (option == LOPT_DHOPT_INOTIFY)
new->flags |= AH_DHCP_OPT;
else if (option == LOPT_HOST_INOTIFY)
new->flags |= AH_HOSTS;
break;
}
case LOPT_AUTHSERV: /* --auth-server */
comma = split(arg);
@@ -2793,6 +2957,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
struct server_details sdetails = { 0 };
sdetails.addr = &serv_addr;
sdetails.source_addr = &source_addr;
sdetails.interface = interface;
sdetails.flags = &flags;
unhide_metas(arg);
/* split the domain args, if any and skip to the end of them. */
@@ -2825,36 +2995,52 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
else
{
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
if ((err = parse_server(arg, &sdetails)))
ret_err(err);
}
if (servers_only && option == 'S')
flags |= SERV_FROM_FILE;
while (1)
while ((flags & SERV_LITERAL_ADDRESS) || parse_server_next(&sdetails))
{
/* server=//1.2.3.4 is special. */
if (lastdomain)
{
if (strlen(domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
if (!(flags & SERV_LITERAL_ADDRESS) && (err = parse_server_addr(&sdetails)))
ret_err(err);
/* address=/#/ matches the same as without domain */
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
domain[0] = 0;
/* When source is set only use DNS records of the same type and skip all others */
if (flags & SERV_HAS_SOURCE && sdetails.addr_type != sdetails.source_addr->sa.sa_family)
continue;
while (1)
{
/* server=//1.2.3.4 is special. */
if (lastdomain)
{
if (strlen(domain) == 0)
flags |= SERV_FOR_NODOTS;
else
flags &= ~SERV_FOR_NODOTS;
/* address=/#/ matches the same as without domain */
if (option == 'A' && domain[0] == '#' && domain[1] == 0)
domain[0] = 0;
}
if (!add_update_server(flags, sdetails.addr, sdetails.source_addr, sdetails.interface, domain, &addr))
ret_err(gen_err);
if (!lastdomain || domain == lastdomain)
break;
domain += strlen(domain) + 1;
}
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
ret_err(gen_err);
if (!lastdomain || domain == lastdomain)
if (flags & SERV_LITERAL_ADDRESS)
break;
domain += strlen(domain) + 1;
}
if (sdetails.resolved)
freeaddrinfo(sdetails.hostinfo);
break;
}
@@ -5532,7 +5718,8 @@ void read_opts(int argc, char **argv, char *compile_opts)
daemon->soa_retry = SOA_RETRY;
daemon->soa_expiry = SOA_EXPIRY;
daemon->randport_limit = 1;
daemon->host_index = SRC_AH;
#ifndef NO_ID
add_txt("version.bind", "dnsmasq-" VERSION, 0 );
add_txt("authors.bind", "Simon Kelley", 0);
+8 -1
View File
@@ -1153,15 +1153,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
tagif_netid = run_tag_if(&context->netid);
}
log_tags(tagif_netid, ntohl(mess->xid));
apply_delay(mess->xid, recvtime, tagif_netid);
if (option_bool(OPT_RAPID_COMMIT) && option_find(mess, sz, OPTION_RAPID_COMMIT, 0))
{
rapid_commit = 1;
/* If a lease exists for this host and another address, squash it. */
if (lease && lease->addr.s_addr != mess->yiaddr.s_addr)
{
lease_prune(lease, now);
lease = NULL;
}
goto rapid_commit;
}
log_tags(tagif_netid, ntohl(mess->xid));
daemon->metrics[METRIC_DHCPOFFER]++;
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);