Compare commits

...
15 Commits
13 changed files with 956 additions and 375 deletions

No files matched your search

+5
View File
@@ -62,6 +62,11 @@ version 2.69
an invaluable period of full-time work to get it to an invaluable period of full-time work to get it to
a workable state. a workable state.
Add --rev-server. Thanks to Dave Taht for suggesting this.
Add --servers-file. Allows dynamic update of upstream servers
full access to configuration.
version 2.68 version 2.68
Use random addresses for DHCPv6 temporary address Use random addresses for DHCPv6 temporary address
+4
View File
@@ -19,6 +19,10 @@ LDFLAGS = $(shell dpkg-buildflags --get LDFLAGS)
DEB_COPTS = $(COPTS) DEB_COPTS = $(COPTS)
# The nettle library in Debian is too old to include
# ECC support.
DEB_COPTS += -DNO_NETTLE_ECC
TARGET = install-i18n TARGET = install-i18n
DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS) DEB_BUILD_ARCH_OS := $(shell dpkg-architecture -qDEB_BUILD_ARCH_OS)
+15
View File
@@ -415,6 +415,14 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq. implemented on all platforms supported by dnsmasq.
.TP .TP
.B --rev-server=<ip-address>/<prefix-len>,<ipaddr>[#<port>][@<source-ip>|<interface>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
.B --rev-server=1.2.3.0/24,192.168.0.1
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
.TP
.B \-A, --address=/<domain>/[domain/]<ipaddr> .B \-A, --address=/<domain>/[domain/]<ipaddr>
Specify an IP address to return for any host in the given domains. Specify an IP address to return for any host in the given domains.
Queries in the domains are never forwarded and always replied to Queries in the domains are never forwarded and always replied to
@@ -1670,6 +1678,13 @@ files. If extension(s) are given, any files which end in those
extensions are skipped. Any files whose names end in ~ or start with . or start and end extensions are skipped. Any files whose names end in ~ or start with . or start and end
with # are always skipped. This flag may be given on the command with # are always skipped. This flag may be given on the command
line or in a configuration file. line or in a configuration file.
.TP
.B --servers-file=<file>
A special case of
.B --conf-file
which differs in two respects. Firstly, only --server and --rev-server are allowed
in the configuration file included. Secondly, the file is re-read and the configuration
therein is updated when dnsmasq recieves SIGHUP.
.SH CONFIG FILE .SH CONFIG FILE
At startup, dnsmasq reads At startup, dnsmasq reads
.I /etc/dnsmasq.conf, .I /etc/dnsmasq.conf,
+6 -4
View File
@@ -448,12 +448,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
int freed_all = flags & F_REVERSE; int freed_all = flags & F_REVERSE;
int free_avail = 0; int free_avail = 0;
/* Don't log DNSSEC records here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
{
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* Don;t mess with TTL for DNSSEC records. */
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl) if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
ttl = daemon->max_cache_ttl; ttl = daemon->max_cache_ttl;
}
/* Don't log keys here, done elsewhere */
if (flags & (F_IPV4 | F_IPV6 | F_CNAME))
log_query(flags | F_UPSTREAM, name, addr, NULL);
/* if previous insertion failed give up now. */ /* if previous insertion failed give up now. */
if (insert_error) if (insert_error)
+14 -112
View File
@@ -108,108 +108,6 @@ static void remove_watch(DBusWatch *watch, void *data)
w = data; /* no warning */ w = data; /* no warning */
} }
static void add_update_server(union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if ((serv->flags & SERV_FROM_DBUS) &&
(serv->flags & SERV_MARK))
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
serv->flags &= ~SERV_MARK;
break;
}
if (!serv && (serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
memset(serv, 0, sizeof(struct server));
if (domain && !(serv->domain = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
serv->next = daemon->servers;
daemon->servers = serv;
serv->flags = SERV_FROM_DBUS;
if (domain)
{
strcpy(serv->domain, domain);
serv->flags |= SERV_HAS_DOMAIN;
}
}
}
if (serv)
{
if (interface)
strcpy(serv->interface, interface);
else
serv->interface[0] = 0;
if (source_addr->in.sin_family == AF_INET &&
addr->in.sin_addr.s_addr == 0 &&
serv->domain)
serv->flags |= SERV_NO_ADDR;
else
{
serv->flags &= ~SERV_NO_ADDR;
serv->addr = *addr;
serv->source_addr = *source_addr;
}
}
}
static void mark_dbus(void)
{
struct server *serv;
/* mark everything from DBUS */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_FROM_DBUS)
serv->flags |= SERV_MARK;
}
static void cleanup_dbus()
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
static void dbus_read_servers(DBusMessage *message) static void dbus_read_servers(DBusMessage *message)
{ {
DBusMessageIter iter; DBusMessageIter iter;
@@ -218,7 +116,7 @@ static void dbus_read_servers(DBusMessage *message)
dbus_message_iter_init(message, &iter); dbus_message_iter_init(message, &iter);
mark_dbus(); mark_servers(SERV_FROM_DBUS);
while (1) while (1)
{ {
@@ -289,13 +187,13 @@ static void dbus_read_servers(DBusMessage *message)
domain = NULL; domain = NULL;
if (!skip) if (!skip)
add_update_server(&addr, &source_addr, NULL, domain); add_update_server(SERV_FROM_DBUS, &addr, &source_addr, NULL, domain);
} while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); } while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
} }
/* unlink and free anything still marked. */ /* unlink and free anything still marked. */
cleanup_dbus(); cleanup_servers();
} }
static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings) static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
@@ -319,7 +217,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
strings ? "Expected array of string" : "Expected array of string arrays"); strings ? "Expected array of string" : "Expected array of string arrays");
} }
mark_dbus(); mark_servers(SERV_FROM_DBUS);
/* array_iter points to each "as" element in the outer array */ /* array_iter points to each "as" element in the outer array */
dbus_message_iter_recurse(&iter, &array_iter); dbus_message_iter_recurse(&iter, &array_iter);
@@ -327,6 +225,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
{ {
const char *str = NULL; const char *str = NULL;
union mysockaddr addr, source_addr; union mysockaddr addr, source_addr;
int flags = 0;
char interface[IF_NAMESIZE]; char interface[IF_NAMESIZE];
char *str_addr, *str_domain = NULL; char *str_addr, *str_domain = NULL;
@@ -416,9 +315,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
memset(&interface, 0, sizeof(interface)); memset(&interface, 0, sizeof(interface));
/* parse the IP address */ /* parse the IP address */
addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, NULL); if ((addr_err = parse_server(str_addr, &addr, &source_addr, (char *) &interface, &flags)))
if (addr_err)
{ {
error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS, error = dbus_message_new_error_printf(message, DBUS_ERROR_INVALID_ARGS,
"Invalid IP address '%s': %s", "Invalid IP address '%s': %s",
@@ -426,6 +323,11 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
break; break;
} }
/* 0.0.0.0 for server address == NULL, for Dbus */
if (addr.in.sin_family == AF_INET &&
addr.in.sin_addr.s_addr == 0)
flags |= SERV_NO_ADDR;
if (strings) if (strings)
{ {
char *p; char *p;
@@ -439,7 +341,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
else else
p = NULL; p = NULL;
add_update_server(&addr, &source_addr, interface, str_domain); add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str_domain);
} while ((str_domain = p)); } while ((str_domain = p));
} }
else else
@@ -454,7 +356,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_get_basic(&string_iter, &str); dbus_message_iter_get_basic(&string_iter, &str);
dbus_message_iter_next (&string_iter); dbus_message_iter_next (&string_iter);
add_update_server(&addr, &source_addr, interface, str); add_update_server(flags | SERV_FROM_DBUS, &addr, &source_addr, interface, str);
} while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING); } while (dbus_message_iter_get_arg_type(&string_iter) == DBUS_TYPE_STRING);
} }
@@ -462,7 +364,7 @@ static DBusMessage* dbus_read_servers_ex(DBusMessage *message, int strings)
dbus_message_iter_next(&array_iter); dbus_message_iter_next(&array_iter);
} }
cleanup_dbus(); cleanup_servers();
if (dup) if (dup)
free(dup); free(dup);
+1
View File
@@ -68,6 +68,7 @@
#define T_RRSIG 46 #define T_RRSIG 46
#define T_NSEC 47 #define T_NSEC 47
#define T_DNSKEY 48 #define T_DNSKEY 48
#define T_NSEC3 50
#define T_TKEY 249 #define T_TKEY 249
#define T_TSIG 250 #define T_TSIG 250
#define T_AXFR 252 #define T_AXFR 252
+19 -2
View File
@@ -98,7 +98,10 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{
daemon->keyname = safe_malloc(MAXDNAME); daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
}
#endif #endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
@@ -1114,7 +1117,7 @@ static void async_event(int pipe, time_t now)
{ {
pid_t p; pid_t p;
struct event_desc ev; struct event_desc ev;
int i; int i, check = 0;
char *msg; char *msg;
/* NOTE: the memory used to return msg is leaked: use msgs in events only /* NOTE: the memory used to return msg is leaked: use msgs in events only
@@ -1125,11 +1128,25 @@ static void async_event(int pipe, time_t now)
{ {
case EVENT_RELOAD: case EVENT_RELOAD:
clear_cache_and_reload(now); clear_cache_and_reload(now);
if (daemon->port != 0 && daemon->resolv_files && option_bool(OPT_NO_POLL))
if (daemon->port != 0)
{
if (daemon->resolv_files && option_bool(OPT_NO_POLL))
{ {
reload_servers(daemon->resolv_files->name); reload_servers(daemon->resolv_files->name);
check = 1;
}
if (daemon->servers_file)
{
read_servers_file();
check = 1;
}
if (check)
check_servers(); check_servers();
} }
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
rerun_scripts(); rerun_scripts();
#endif #endif
+13
View File
@@ -456,6 +456,7 @@ union mysockaddr {
#define SERV_COUNTED 512 /* workspace for log code */ #define SERV_COUNTED 512 /* workspace for log code */
#define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */ #define SERV_USE_RESOLV 1024 /* forward this domain in the normal way */
#define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */ #define SERV_NO_REBIND 2048 /* inhibit dns-rebind protection */
#define SERV_FROM_FILE 4096 /* read from --servers-file */
struct serverfd { struct serverfd {
int fd; int fd;
@@ -533,6 +534,7 @@ struct hostsfile {
#define STAT_NEED_DS 4 #define STAT_NEED_DS 4
#define STAT_NEED_KEY 5 #define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6 #define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define FREC_NOREBIND 1 #define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
@@ -864,6 +866,7 @@ extern struct daemon {
unsigned int options, options2; unsigned int options, options2;
struct resolvc default_resolv, *resolv_files; struct resolvc default_resolv, *resolv_files;
time_t last_resolv; time_t last_resolv;
char *servers_file;
struct mx_srv_record *mxnames; struct mx_srv_record *mxnames;
struct naptr *naptr; struct naptr *naptr;
struct txt_record *txt, *rr; struct txt_record *txt, *rr;
@@ -939,6 +942,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */ char *namebuff; /* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */ char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
#endif #endif
unsigned int local_answer, queries_forwarded, auth_answer; unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list; struct frec *frec_list;
@@ -1033,6 +1037,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes); char *name, int isExtract, int extrabytes);
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
unsigned char *skip_questions(struct dns_header *header, size_t plen); unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen, unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep); char *name, unsigned short *typep);
size_t setup_reply(struct dns_header *header, size_t qlen, size_t setup_reply(struct dns_header *header, size_t qlen,
@@ -1130,6 +1135,7 @@ void read_opts (int argc, char **argv, char *compile_opts);
char *option_string(int prot, unsigned int opt, unsigned char *val, char *option_string(int prot, unsigned int opt, unsigned char *val,
int opt_len, char *buf, int buf_len); int opt_len, char *buf, int buf_len);
void reread_dhcp(void); void reread_dhcp(void);
void read_servers_file(void);
void set_option_bool(unsigned int opt); void set_option_bool(unsigned int opt);
void reset_option_bool(unsigned int opt); void reset_option_bool(unsigned int opt);
struct hostsfile *expand_filelist(struct hostsfile *list); struct hostsfile *expand_filelist(struct hostsfile *list);
@@ -1153,6 +1159,13 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
int random_sock(int family); int random_sock(int family);
void pre_allocate_sfds(void); void pre_allocate_sfds(void);
int reload_servers(char *fname); int reload_servers(char *fname);
void mark_servers(int flag);
void cleanup_servers(void);
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain);
void check_servers(void); void check_servers(void);
int enumerate_interfaces(int reset); int enumerate_interfaces(int reset);
void create_wildcard_listeners(void); void create_wildcard_listeners(void);
+609 -121
View File
@@ -21,6 +21,10 @@
#include <nettle/rsa.h> #include <nettle/rsa.h>
#include <nettle/dsa.h> #include <nettle/dsa.h>
#ifndef NO_NETTLE_ECC
# include <nettle/ecdsa.h>
# include <nettle/ecc-curve.h>
#endif
#include <nettle/nettle-meta.h> #include <nettle/nettle-meta.h>
#include <gmp.h> #include <gmp.h>
@@ -208,9 +212,83 @@ static int dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned
return nettle_dsa_sha1_verify_digest(key, digest, sig_struct); return nettle_dsa_sha1_verify_digest(key, digest, sig_struct);
} }
static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len, #ifndef NO_NETTLE_ECC
unsigned char *digest, int algo) static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{ {
unsigned char *p;
unsigned int t;
struct ecc_point *key;
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
static mpz_t x, y;
static struct dsa_signature *sig_struct;
if (!sig_struct)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))))
return 0;
nettle_dsa_signature_init(sig_struct);
mpz_init(x);
mpz_init(y);
}
switch (algo)
{
case 13:
if (!key_256)
{
if (!(key_256 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_256, &nettle_secp_256r1);
}
key = key_256;
t = 32;
break;
case 14:
if (!key_384)
{
if (!(key_384 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_384, &nettle_secp_384r1);
}
key = key_384;
t = 48;
break;
default:
return 0;
}
if (sig_len != 2*t || key_len != 2*t ||
(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
mpz_import(x, t , 1, 1, 0, 0, p);
mpz_import(y, t , 1, 1, 0, 0, p + t);
if (!ecc_point_set(key, x, y))
return 0;
mpz_import(sig_struct->r, t, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, t, 1, 1, 0, 0, sig + t);
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
}
#endif
static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
(void)digest_len;
switch (algo) switch (algo)
{ {
case 1: case 5: case 7: case 8: case 10: case 1: case 5: case 7: case 8: case 10:
@@ -218,6 +296,11 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha
case 3: case 6: case 3: case 6:
return dsa_verify(key_data, key_len, sig, sig_len, digest, algo); return dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
#ifndef NO_NETTLE_ECC
case 13: case 14:
return dnsmasq_ecdsa_verify(key_data, key_len, sig, sig_len, digest, digest_len, algo);
#endif
} }
return 0; return 0;
@@ -415,6 +498,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
int rdlen1, rdlen2, left1, left2, len1, len2, len, rc; int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
u16 *dp1, *dp2; u16 *dp1, *dp2;
unsigned char *end1, *end2; unsigned char *end1, *end2;
/* Note that these have been determined to be OK previously,
so we don't need to check for NULL return here. */
unsigned char *p1 = skip_name(rrset[i], header, plen, 10); unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10); unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
@@ -476,12 +561,15 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
/* Validate a single RRset (class, type, name) in the supplied DNS reply /* Validate a single RRset (class, type, name) in the supplied DNS reply
Return code: Return code:
STAT_SECURE if it validates. STAT_SECURE if it validates.
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
STAT_INSECURE can't validate (no RRSIG, bad packet). STAT_INSECURE can't validate (no RRSIG, bad packet).
STAT_BOGUS signature is wrong. STAT_BOGUS signature is wrong.
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
if key is non-NULL, use that key, which has the algo and tag given in the params of those names, if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
otherwise find the key in the cache. otherwise find the key in the cache.
name is unchanged on exit. keyname is used as workspace and trashed.
*/ */
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in) int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
@@ -594,18 +682,18 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
return STAT_INSECURE; return STAT_INSECURE;
/* Sort RRset records into canonical order. /* Sort RRset records into canonical order.
Note that at this point keyname and name buffs are Note that at this point keyname and daemon->workspacename buffs are
unused, and used as workspace by the sort. */ unused, and used as workspace by the sort. */
sort_rrset(header, plen, rr_desc, rrsetidx, rrset, name, keyname); sort_rrset(header, plen, rr_desc, rrsetidx, rrset, daemon->workspacename, keyname);
/* Now try all the sigs to try and find one which validates */ /* Now try all the sigs to try and find one which validates */
for (j = 0; j <sigidx; j++) for (j = 0; j <sigidx; j++)
{ {
unsigned char *psav, *sig; unsigned char *psav, *sig, *digest;
int i, wire_len, sig_len; int i, wire_len, sig_len;
const struct nettle_hash *hash; const struct nettle_hash *hash;
void *ctx; void *ctx;
unsigned char *digest; char *name_start;
u32 nsigttl; u32 nsigttl;
p = sigs[j]; p = sigs[j];
@@ -623,6 +711,31 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!extract_name(header, plen, &p, keyname, 1, 0)) if (!extract_name(header, plen, &p, keyname, 1, 0))
return STAT_INSECURE; return STAT_INSECURE;
/* RFC 4035 5.3.1 says that the Signer's Name field MUST equal
the name of the zone containing the RRset. We can't tell that
for certain, but we can check that the RRset name is equal to
or encloses the signers name, which should be enough to stop
an attacker using signatures made with the key of an unrelated
zone he controls. Note that the root key is always allowed. */
if (*keyname != 0)
{
int failed = 0;
for (name_start = name; !hostname_isequal(name_start, keyname); )
if ((name_start = strchr(name_start, '.')))
name_start++; /* chop a label off and try again */
else
{
failed = 1;
break;
}
/* Bad sig, try another */
if (failed)
continue;
}
/* Other 5.3.1 checks */
if (!check_date_range(sig_inception, sig_expiration) || if (!check_date_range(sig_inception, sig_expiration) ||
labels > name_labels || labels > name_labels ||
!(hash = hash_find(algo_digest_name(algo))) || !(hash = hash_find(algo_digest_name(algo))) ||
@@ -647,13 +760,14 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
{ {
int seg; int seg;
unsigned char *end, *cp; unsigned char *end, *cp;
char *name_start = name;
u16 len, *dp; u16 len, *dp;
p = rrset[i]; p = rrset[i];
if (!extract_name(header, plen, &p, name, 1, 10)) if (!extract_name(header, plen, &p, name, 1, 10))
return STAT_INSECURE; return STAT_INSECURE;
name_start = name;
/* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */ /* if more labels than in RRsig name, hash *.<no labels in rrsig labels field> 4035 5.3.2 */
if (labels < name_labels) if (labels < name_labels)
{ {
@@ -703,7 +817,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (key) if (key)
{ {
if (algo_in == algo && keytag_in == key_tag && if (algo_in == algo && keytag_in == key_tag &&
verify(key, keylen, sig, sig_len, digest, algo)) verify(key, keylen, sig, sig_len, digest, hash->digest_size, algo))
return STAT_SECURE; return STAT_SECURE;
} }
else else
@@ -713,8 +827,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (crecp->addr.key.algo == algo && if (crecp->addr.key.algo == algo &&
crecp->addr.key.keytag == key_tag && crecp->addr.key.keytag == key_tag &&
crecp->uid == class && crecp->uid == class &&
verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, algo)) verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo))
return STAT_SECURE; return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE;
} }
} }
@@ -746,7 +860,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(qclass, p); GETSHORT(qclass, p);
if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0) if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
return STAT_INSECURE; return STAT_BOGUS;
/* See if we have cached a DS record which validates this key */ /* See if we have cached a DS record which validates this key */
if (!(crecp = cache_find_by_name(NULL, name, now, F_DS))) if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
@@ -780,7 +894,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(flags, p); GETSHORT(flags, p);
if (*p++ != 3) if (*p++ != 3)
return STAT_INSECURE; return STAT_BOGUS;
algo = *p++; algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4); keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
key = NULL; key = NULL;
@@ -870,7 +984,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
GETSHORT(flags, p); GETSHORT(flags, p);
if (*p++ != 3) if (*p++ != 3)
return STAT_INSECURE; return STAT_BOGUS;
algo = *p++; algo = *p++;
keytag = dnskey_keytag(algo, flags, p, rdlen - 4); keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
@@ -966,7 +1080,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
GETSHORT(qclass, p); GETSHORT(qclass, p);
if (qtype != T_DS || qclass != class || ntohs(header->ancount) == 0) if (qtype != T_DS || qclass != class || ntohs(header->ancount) == 0)
return STAT_INSECURE; return STAT_BOGUS;
val = dnssec_validate_reply(now, header, plen, name, keyname, NULL); val = dnssec_validate_reply(now, header, plen, name, keyname, NULL);
@@ -1044,14 +1158,454 @@ static int hostname_cmp(const char *a, const char *b)
} }
} }
/* Find all the NSEC or NSEC3 records in a reply.
return an array of pointers to them. */
static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
{
static unsigned char **nsecset = NULL;
static int nsecset_sz = 0;
int type_found = -1;
unsigned char *p = skip_questions(header, plen);
int type, class, rdlen, i, nsecs_found;
/* Move to NS section */
if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
return 0;
for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
{
unsigned char *pstart = p;
if (!(p = skip_name(p, header, plen, 10)))
return 0;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
{
/* No mixed NSECing 'round here, thankyouverymuch */
if (type_found == T_NSEC && type == T_NSEC3)
return 0;
if (type_found == T_NSEC3 && type == T_NSEC)
return 0;
type_found = type;
if (nsecs_found == nsecset_sz)
{
unsigned char **new;
/* Protect against insane/malicious queries which bloat the workspace
and eat CPU in the sort */
if (nsecs_found >= 100)
return 0;
/* expand */
if (!(new = whine_malloc((nsecset_sz + 5) * sizeof(unsigned char **))))
return 0;
if (nsecset)
{
memcpy(new, nsecset, nsecset_sz * sizeof(unsigned char **));
free(nsecset);
}
nsecset = new;
nsecset_sz += 5;
}
nsecset[nsecs_found++] = pstart;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return 0;
}
*nsecsetp = nsecset;
*nsecsetl = nsecs_found;
return type_found;
}
static int prove_non_existance_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
{
int i, rc, rdlen;
unsigned char *p, *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
/* Find NSEC record that proves name doesn't exist */
for (i = 0; i < nsec_count; i++)
{
p = nsecs[i];
if (!extract_name(header, plen, &p, workspace1, 1, 10))
return STAT_INSECURE;
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
if (!extract_name(header, plen, &p, workspace2, 1, 10))
return STAT_INSECURE;
rc = hostname_cmp(workspace1, name);
if (rc == 0)
{
/* 4035 para 5.4. Last sentence */
if (type == T_NSEC || type == T_RRSIG)
return STAT_SECURE;
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it */
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_INSECURE;
if (p[0] == type >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return STAT_SECURE;
}
else if (rc == -1)
{
/* Normal case, name falls between NSEC name and next domain name,
wrap around case, name falls between NSEC name (rc == -1) and end */
if (hostname_cmp(workspace2, name) == 1 || hostname_cmp(workspace1, workspace2) == 1)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (hostname_cmp(workspace1, workspace2) == 1 && hostname_cmp(workspace2, name) == 1)
return STAT_SECURE;
}
}
return STAT_BOGUS;
}
/* return digest length, or zero on error */
static int hash_name(char *in, unsigned char **out, struct nettle_hash const *hash,
unsigned char *salt, int salt_len, int iterations)
{
void *ctx;
unsigned char *digest;
int i;
if (!hash_init(hash, &ctx, &digest))
return 0;
hash->update(ctx, to_wire(in), (unsigned char *)in);
hash->update(ctx, salt_len, salt);
hash->digest(ctx, hash->digest_size, digest);
for(i = 0; i < iterations; i++)
{
hash->update(ctx, hash->digest_size, digest);
hash->update(ctx, salt_len, salt);
hash->digest(ctx, hash->digest_size, digest);
}
from_wire(in);
*out = digest;
return hash->digest_size;
}
/* Decode base32 to first "." or end of string */
static int base32_decode(char *in, unsigned char *out)
{
int oc, on, c, mask, i;
unsigned char *p = out;
for (c = *in, oc = 0, on = 0; c != 0 && c != '.'; c = *++in)
{
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'a' && c <= 'v')
c -= 'a', c += 10;
else if (c >= 'A' && c <= 'V')
c -= 'A', c += 10;
else
return 0;
for (mask = 0x10, i = 0; i < 5; i++)
{
if (c & mask)
oc |= 1;
mask = mask >> 1;
if (((++on) & 7) == 0)
*p++ = oc;
oc = oc << 1;
}
}
if ((on & 7) != 0)
return 0;
return p - out;
}
static int prove_non_existance_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
{
unsigned char *salt, *p, *digest;
int digest_len, i, iterations, salt_len, hash_len, base32_len, algo = 0;
struct nettle_hash const *hash;
char *closest_encloser, *next_closest, *wildcard;
/* Look though the NSEC3 records to find the first one with
an algorithm we support (currently only algo == 1).
Take the algo, iterations, and salt of that record
as the ones we're going to use, and prune any
that don't match. */
for (i = 0; i < nsec_count; i++)
{
if (!(p = skip_name(nsecs[i], header, plen, 15)))
return STAT_INSECURE; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
if (algo == 1)
break; /* known algo */
}
/* No usable NSEC3s */
if (i == nsec_count)
return STAT_BOGUS;
p++; /* flags */
GETSHORT (iterations, p);
salt_len = *p++;
salt = p;
if (!CHECK_LEN(header, salt, plen, salt_len))
return STAT_INSECURE; /* bad packet */
/* Now prune so we only have NSEC3 records with same iterations, salt and algo */
for (i = 0; i < nsec_count; i++)
{
unsigned char *nsec3p = nsecs[i];
int this_iter;
nsecs[i] = NULL; /* Speculative, will be restored if OK. */
if (!(p = skip_name(nsec3p, header, plen, 15)))
return STAT_INSECURE; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
if (*p++ != algo)
continue;
p++; /* flags */
GETSHORT(this_iter, p);
if (this_iter != iterations)
continue;
if (salt_len != *p++)
continue;
if (!CHECK_LEN(header, p, plen, salt_len))
return STAT_INSECURE; /* bad packet */
if (memcmp(p, salt, salt_len) != 0)
continue;
/* All match, put the pointer back */
nsecs[i] = nsec3p;
}
/* Algo is checked as 1 above */
if (!(hash = hash_find("sha1")))
return STAT_INSECURE;
/* Now, we need the "closest encloser NSEC3" */
closest_encloser = name;
next_closest = NULL;
do
{
if (*closest_encloser == '.')
closest_encloser++;
if ((digest_len = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
if (digest_len == base32_len &&
memcmp(digest, workspace2, digest_len) == 0)
break; /* Gotit */
}
if (i != nsec_count)
break;
next_closest = closest_encloser;
}
while ((closest_encloser = strchr(closest_encloser, '.')));
/* No usable NSEC3s */
if (i == nsec_count)
return STAT_BOGUS;
if (!next_closest)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
Now we just need to check the type map. p points to the RR data for the record. */
int rdlen;
unsigned char *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
hash_len = *p++;
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE; /* bad packet */
p += hash_len;
rdlen -= p - psave;
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_INSECURE;
if (p[0] == type >> 8)
{
/* Does the NSEC3 say our type exists? */
if (offset < p[1] && (p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return STAT_SECURE;
}
/* Look for NSEC3 that proves the non-existance of the next-closest encloser */
if ((digest_len = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE;
if (digest_len == base32_len && hash_len == base32_len)
{
if (memcmp(workspace2, digest, digest_len) <= 0)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
return STAT_SECURE;
}
}
}
/* Finally, check that there's no seat of wildcard synthesis */
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
return STAT_BOGUS;
wildcard--;
*wildcard = '*';
if ((digest_len = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_len = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE;
if (digest_len == base32_len && hash_len == base32_len)
{
if (memcmp(workspace2, digest, digest_len) <= 0)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if (memcmp(p, digest, digest_len) > 0 || memcmp(workspace2, p, digest_len) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_len) > 0 && memcmp(p, digest, digest_len) > 0)
return STAT_SECURE;
}
}
}
return STAT_BOGUS;
}
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */ /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */ /* Returns are the same as validate_rrset, plus the class if the missing key is in *class */
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class) int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class)
{ {
unsigned char *ans_start, *p1, *p2; unsigned char *ans_start, *p1, *p2, **nsecs;
int type1, class1, rdlen1, type2, class2, rdlen2; int type1, class1, rdlen1, type2, class2, rdlen2;
int i, j, rc, have_nsec, have_nsec_equal, cname_count = 5; int i, j, rc, nsec_count, cname_count = 10;
int nsec_type = 0;
if (RCODE(header) == SERVFAIL)
return STAT_BOGUS;
if ((RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) || ntohs(header->qdcount) != 1) if ((RCODE(header) != NXDOMAIN && RCODE(header) != NOERROR) || ntohs(header->qdcount) != 1)
return STAT_INSECURE; return STAT_INSECURE;
@@ -1099,7 +1653,33 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct blockdata *key; struct blockdata *key;
struct crec *crecp; struct crec *crecp;
if ((rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0)) != STAT_SECURE) rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
if (rc == STAT_SECURE_WILDCARD)
{
/* An attacker replay a wildcard answer with a different
answer and overlay a genuine RR. To prove this
hasn't happened, the answer must prove that
the gennuine record doesn't exist. Check that here. */
if (!nsec_type)
{
nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1);
if (nsec_type == 0)
return STAT_INSECURE; /* Bad packet */
if (nsec_type == -1)
return STAT_BOGUS; /* No NSECs */
}
if (nsec_type == T_NSEC)
rc = prove_non_existance_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
else
rc = prove_non_existance_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
if (rc != STAT_SECURE)
return rc;
}
else if (rc != STAT_SECURE)
{ {
if (class) if (class)
*class = class1; /* Class for DS or DNSKEY */ *class = class1; /* Class for DS or DNSKEY */
@@ -1235,7 +1815,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{ {
/* Do we have an answer for the question? */ /* Do we have an answer for the question? */
if (type1 == type2) if (type1 == type2)
return RCODE(header) == NXDOMAIN ? STAT_INSECURE : STAT_SECURE; return RCODE(header) == NXDOMAIN ? STAT_BOGUS : STAT_SECURE;
else if (type2 == T_CNAME) else if (type2 == T_CNAME)
{ {
/* looped CNAMES */ /* looped CNAMES */
@@ -1252,117 +1832,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return STAT_INSECURE; return STAT_INSECURE;
} }
/* NXDOMAIN or NODATA reply, look for NSEC records to support that. /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
At this point, p1 points to the start of the auth section.
Use keyname as workspace */
for (have_nsec = 0, have_nsec_equal = 0, p2 = NULL, rdlen2 = 0, j = ntohs(header->nscount); j != 0; j--)
{
unsigned char *nsec_start = p1;
if (!extract_name(header, plen, &p1, keyname, 1, 10))
return STAT_INSECURE; /* bad packet */
GETSHORT(type2, p1); /* First marshall the NSEC records, if we've not done it previously */
GETSHORT(class2, p1); if (!nsec_type)
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (class1 == class2 && type2 == T_NSEC)
{ {
have_nsec = 1; nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1);
rc = hostname_cmp(name, keyname);
if (rc >= 0) if (nsec_type == 0)
{ return STAT_INSECURE; /* Bad packet */
if (p2) if (nsec_type == -1)
{ return STAT_BOGUS; /* No NSECs */
unsigned char *psave = p2;
/* new NSEC is smaller than name,
is it bigger than previous one? */
/* get previous one into name buffer */
if (!extract_name(header, plen, &psave, name, 1, 0))
return STAT_INSECURE; /* bad packet */
if (hostname_cmp(name, keyname) < 0)
{
p2 = nsec_start;
rdlen2 = rdlen1;
} }
/* restore query name */ if (nsec_type == T_NSEC)
psave = (unsigned char *)(header+1); return prove_non_existance_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
if (!extract_name(header, plen, &psave, name, 1, 0))
return STAT_INSECURE;
}
else else
{ return prove_non_existance_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
/* There was no previous best candidate */
p2 = nsec_start;
rdlen2 = rdlen1;
}
}
if (rc == 0)
have_nsec_equal = 1;
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_INSECURE;
}
if (p2)
{
unsigned char *psave;
p2 = skip_name(p2, header, plen, 0);
p2 += 10; /* type, class, ttl, rdlen */
psave = p2;
extract_name(header, plen, &p2, keyname, 1, 0);
rdlen2 -= p2 - psave;
}
/* At this point, have_nsec is set if there's at least one NSEC
have_nsec_equal is set if there's an NSEC with the same name as the query;
p2 points to the type bit maps of the biggest NSEC smaller than or equal to the query
or NULL if the query is smaller than all of them.
Keyname holds the next domain name for that NSEC.
rdlen2 is the length of the bitmap field */
if (RCODE(header) == NOERROR && have_nsec_equal)
{
int offset = (type1 & 0xff) >> 3;
int mask = 0x80 >> (type1 & 0x07);
while (rdlen2 >= 2)
{
if (p2[0] == type1 >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p2[1] &&
(p2[offset+2] & mask) != 0)
return STAT_INSECURE;
break; /* finshed checking */
}
rdlen2 -= p2[1];
p2 += p2[1];
}
return STAT_SECURE;
}
if (RCODE(header) == NXDOMAIN && have_nsec)
{
if (!p2 || hostname_cmp(name, keyname) < 0)
return STAT_SECURE; /* Before the first, or in a proven gap */
}
return STAT_INSECURE;
} }
/* Compute keytag (checksum to quickly index a key). See RFC4034 */ /* Compute keytag (checksum to quickly index a key). See RFC4034 */
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
{ {
+4 -1
View File
@@ -256,7 +256,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash)))) else if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
{ {
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
/* If we've already got an answer to this query, but we're awaiting keys for vaildation, /* If we've already got an answer to this query, but we're awaiting keys for validation,
there's no point retrying the query, retry the key query instead...... */ there's no point retrying the query, retry the key query instead...... */
if (forward->blocking_query) if (forward->blocking_query)
{ {
@@ -1431,6 +1431,9 @@ unsigned char *tcp_request(int confd, time_t now,
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
{ {
size = add_do_bit(header, size, ((char *) header) + 65536); size = add_do_bit(header, size, ((char *) header) + 65536);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD; header->hb4 |= HB4_CD;
} }
#endif #endif
+132 -80
View File
@@ -1254,87 +1254,187 @@ void pre_allocate_sfds(void)
} }
} }
void mark_servers(int flag)
{
struct server *serv;
/* mark everything with argument flag */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & flag)
serv->flags |= SERV_MARK;
}
void cleanup_servers(void)
{
struct server *serv, *tmp, **up;
/* unlink and free anything still marked. */
for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp)
{
tmp = serv->next;
if (serv->flags & SERV_MARK)
{
server_gone(serv);
*up = serv->next;
if (serv->domain)
free(serv->domain);
free(serv);
}
else
up = &serv->next;
}
}
void add_update_server(int flags,
union mysockaddr *addr,
union mysockaddr *source_addr,
const char *interface,
const char *domain)
{
struct server *serv, *next = NULL;
char *domain_str = NULL;
/* See if there is a suitable candidate, and unmark */
for (serv = daemon->servers; serv; serv = serv->next)
if (serv->flags & SERV_MARK)
{
if (domain)
{
if (!(serv->flags & SERV_HAS_DOMAIN) || !hostname_isequal(domain, serv->domain))
continue;
}
else
{
if (serv->flags & SERV_HAS_DOMAIN)
continue;
}
break;
}
if (serv)
{
domain_str = serv->domain;
next = serv->next;
}
else if ((serv = whine_malloc(sizeof (struct server))))
{
/* Not found, create a new one. */
if (domain && !(domain_str = whine_malloc(strlen(domain)+1)))
{
free(serv);
serv = NULL;
}
else
{
struct server *s;
/* Add to the end of the chain, for order */
if (!daemon->servers)
daemon->servers = serv;
else
{
for (s = daemon->servers; s->next; s = s->next);
s->next = serv;
}
if (domain)
strcpy(domain_str, domain);
}
}
if (serv)
{
memset(serv, 0, sizeof(struct server));
serv->flags = flags;
serv->domain = domain_str;
serv->next = next;
serv->queries = serv->failed_queries = 0;
if (domain)
serv->flags |= SERV_HAS_DOMAIN;
if (interface)
strcpy(serv->interface, interface);
if (addr)
serv->addr = *addr;
if (source_addr)
serv->source_addr = *source_addr;
}
}
void check_servers(void) void check_servers(void)
{ {
struct irec *iface; struct irec *iface;
struct server *new, *tmp, *ret = NULL; struct server *serv;
int port = 0; int port = 0;
/* interface may be new since startup */ /* interface may be new since startup */
if (!option_bool(OPT_NOWILD)) if (!option_bool(OPT_NOWILD))
enumerate_interfaces(0); enumerate_interfaces(0);
for (new = daemon->servers; new; new = tmp) for (serv = daemon->servers; serv; serv = serv->next)
{ {
tmp = new->next; if (!(serv->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
if (!(new->flags & (SERV_LITERAL_ADDRESS | SERV_NO_ADDR | SERV_USE_RESOLV | SERV_NO_REBIND)))
{ {
port = prettyprint_addr(&new->addr, daemon->namebuff); port = prettyprint_addr(&serv->addr, daemon->namebuff);
/* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */ /* 0.0.0.0 is nothing, the stack treats it like 127.0.0.1 */
if (new->addr.sa.sa_family == AF_INET && if (serv->addr.sa.sa_family == AF_INET &&
new->addr.in.sin_addr.s_addr == 0) serv->addr.in.sin_addr.s_addr == 0)
{ {
free(new); serv->flags |= SERV_MARK;
continue; continue;
} }
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
if (sockaddr_isequal(&new->addr, &iface->addr)) if (sockaddr_isequal(&serv->addr, &iface->addr))
break; break;
if (iface) if (iface)
{ {
my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff); my_syslog(LOG_WARNING, _("ignoring nameserver %s - local interface"), daemon->namebuff);
free(new); serv->flags |= SERV_MARK;
continue; continue;
} }
/* Do we need a socket set? */ /* Do we need a socket set? */
if (!new->sfd && if (!serv->sfd &&
!(new->sfd = allocate_sfd(&new->source_addr, new->interface)) && !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface)) &&
errno != 0) errno != 0)
{ {
my_syslog(LOG_WARNING, my_syslog(LOG_WARNING,
_("ignoring nameserver %s - cannot make/bind socket: %s"), _("ignoring nameserver %s - cannot make/bind socket: %s"),
daemon->namebuff, strerror(errno)); daemon->namebuff, strerror(errno));
free(new); serv->flags |= SERV_MARK;
continue; continue;
} }
} }
/* reverse order - gets it right. */ if (!(serv->flags & SERV_NO_REBIND))
new->next = ret;
ret = new;
if (!(new->flags & SERV_NO_REBIND))
{ {
if (new->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV))
{ {
char *s1, *s2; char *s1, *s2;
if (!(new->flags & SERV_HAS_DOMAIN)) if (!(serv->flags & SERV_HAS_DOMAIN))
s1 = _("unqualified"), s2 = _("names"); s1 = _("unqualified"), s2 = _("names");
else if (strlen(new->domain) == 0) else if (strlen(serv->domain) == 0)
s1 = _("default"), s2 = ""; s1 = _("default"), s2 = "";
else else
s1 = _("domain"), s2 = new->domain; s1 = _("domain"), s2 = serv->domain;
if (new->flags & SERV_NO_ADDR) if (serv->flags & SERV_NO_ADDR)
my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2); my_syslog(LOG_INFO, _("using local addresses only for %s %s"), s1, s2);
else if (new->flags & SERV_USE_RESOLV) else if (serv->flags & SERV_USE_RESOLV)
my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2);
else if (!(new->flags & SERV_LITERAL_ADDRESS)) else if (!(serv->flags & SERV_LITERAL_ADDRESS))
my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2); my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s"), daemon->namebuff, port, s1, s2);
} }
else if (new->interface[0] != 0) else if (serv->interface[0] != 0)
my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, new->interface); my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface);
else else
my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port);
} }
} }
daemon->servers = ret; cleanup_servers();
} }
/* Return zero if no servers found, in that case we keep polling. /* Return zero if no servers found, in that case we keep polling.
@@ -1343,9 +1443,6 @@ int reload_servers(char *fname)
{ {
FILE *f; FILE *f;
char *line; char *line;
struct server *old_servers = NULL;
struct server *new_servers = NULL;
struct server *serv;
int gotone = 0; int gotone = 0;
/* buff happens to be MAXDNAME long... */ /* buff happens to be MAXDNAME long... */
@@ -1355,26 +1452,7 @@ int reload_servers(char *fname)
return 0; return 0;
} }
/* move old servers to free list - we can reuse the memory mark_servers(SERV_FROM_RESOLV);
and not risk malloc if there are the same or fewer new servers.
Servers which were specced on the command line go to the new list. */
for (serv = daemon->servers; serv;)
{
struct server *tmp = serv->next;
if (serv->flags & SERV_FROM_RESOLV)
{
serv->next = old_servers;
old_servers = serv;
/* forward table rules reference servers, so have to blow them away */
server_gone(serv);
}
else
{
serv->next = new_servers;
new_servers = serv;
}
serv = tmp;
}
while ((line = fgets(daemon->namebuff, MAXDNAME, f))) while ((line = fgets(daemon->namebuff, MAXDNAME, f)))
{ {
@@ -1434,38 +1512,12 @@ int reload_servers(char *fname)
continue; continue;
#endif #endif
if (old_servers) add_update_server(SERV_FROM_RESOLV, &addr, &source_addr, NULL, NULL);
{
serv = old_servers;
old_servers = old_servers->next;
}
else if (!(serv = whine_malloc(sizeof (struct server))))
continue;
/* this list is reverse ordered:
it gets reversed again in check_servers */
serv->next = new_servers;
new_servers = serv;
serv->addr = addr;
serv->source_addr = source_addr;
serv->domain = NULL;
serv->interface[0] = 0;
serv->sfd = NULL;
serv->flags = SERV_FROM_RESOLV;
serv->queries = serv->failed_queries = 0;
gotone = 1; gotone = 1;
} }
/* Free any memory not used. */
while (old_servers)
{
struct server *tmp = old_servers->next;
free(old_servers);
old_servers = tmp;
}
daemon->servers = new_servers;
fclose(f); fclose(f);
cleanup_servers();
return gotone; return gotone;
} }
+133 -54
View File
@@ -141,6 +141,8 @@ struct myoption {
#define LOPT_SEC_VALID 329 #define LOPT_SEC_VALID 329
#define LOPT_TRUST_ANCHOR 330 #define LOPT_TRUST_ANCHOR 330
#define LOPT_DNSSEC_DEBUG 331 #define LOPT_DNSSEC_DEBUG 331
#define LOPT_REV_SERV 332
#define LOPT_SERVERS_FILE 333
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
@@ -157,6 +159,7 @@ static const struct myoption opts[] =
{ "user", 2, 0, 'u' }, { "user", 2, 0, 'u' },
{ "group", 2, 0, 'g' }, { "group", 2, 0, 'g' },
{ "resolv-file", 2, 0, 'r' }, { "resolv-file", 2, 0, 'r' },
{ "servers-file", 1, 0, LOPT_SERVERS_FILE },
{ "mx-host", 1, 0, 'm' }, { "mx-host", 1, 0, 'm' },
{ "mx-target", 1, 0, 't' }, { "mx-target", 1, 0, 't' },
{ "cache-size", 2, 0, 'c' }, { "cache-size", 2, 0, 'c' },
@@ -178,6 +181,7 @@ static const struct myoption opts[] =
{ "pid-file", 2, 0, 'x' }, { "pid-file", 2, 0, 'x' },
{ "strict-order", 0, 0, 'o' }, { "strict-order", 0, 0, 'o' },
{ "server", 1, 0, 'S' }, { "server", 1, 0, 'S' },
{ "rev-server", 1, 0, LOPT_REV_SERV },
{ "local", 1, 0, LOPT_LOCAL }, { "local", 1, 0, LOPT_LOCAL },
{ "address", 1, 0, 'A' }, { "address", 1, 0, 'A' },
{ "conf-file", 2, 0, 'C' }, { "conf-file", 2, 0, 'C' },
@@ -347,7 +351,9 @@ static struct {
{ 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL }, { 'Q', ARG_ONE, "<integer>", gettext_noop("Force the originating port for upstream DNS queries."), NULL },
{ 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL }, { 'R', OPT_NO_RESOLV, NULL, gettext_noop("Do NOT read resolv.conf."), NULL },
{ 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE }, { 'r', ARG_DUP, "<path>", gettext_noop("Specify path to resolv.conf (defaults to %s)."), RESOLVFILE },
{ LOPT_SERVERS_FILE, ARG_ONE, "<path>", gettext_noop("Specify path to file with server= options"), NULL },
{ 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL }, { 'S', ARG_DUP, "/<domain>/<ipaddr>", gettext_noop("Specify address(es) of upstream servers with optional domains."), NULL },
{ LOPT_REV_SERV, ARG_DUP, "<addr>/<prefix>,<ipaddr>", gettext_noop("Specify address of upstream servers for reverse address queries"), NULL },
{ LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL }, { LOPT_LOCAL, ARG_DUP, "/<domain>/", gettext_noop("Never forward queries to specified domains."), NULL },
{ 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL }, { 's', ARG_DUP, "<domain>[,<range>]", gettext_noop("Specify the domain to be assigned in DHCP leases."), NULL },
{ 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL }, { 't', ARG_ONE, "<host_name>", gettext_noop("Specify default target in an MX record."), NULL },
@@ -591,6 +597,7 @@ static int atoi_check16(char *a, int *res)
return 1; return 1;
} }
#ifdef HAVE_DNSSEC
static int atoi_check8(char *a, int *res) static int atoi_check8(char *a, int *res)
{ {
if (!(atoi_check(a, res)) || if (!(atoi_check(a, res)) ||
@@ -600,6 +607,7 @@ static int atoi_check8(char *a, int *res)
return 1; return 1;
} }
#endif
static void add_txt(char *name, char *txt) static void add_txt(char *name, char *txt)
{ {
@@ -684,6 +692,13 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
char *scope_id; char *scope_id;
#endif #endif
if (!arg || strlen(arg) == 0)
{
*flags |= SERV_NO_ADDR;
*interface = 0;
return NULL;
}
if ((source = split_chr(arg, '@')) && /* is there a source. */ if ((source = split_chr(arg, '@')) && /* is there a source. */
(portno = split_chr(source, '#')) && (portno = split_chr(source, '#')) &&
!atoi_check16(portno, &source_port)) !atoi_check16(portno, &source_port))
@@ -762,6 +777,54 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL; return NULL;
} }
static struct server *add_rev4(struct in_addr addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
in_addr_t a = ntohl(addr.s_addr) >> 8;
char *p;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
static struct server *add_rev6(struct in6_addr *addr, int msize)
{
struct server *serv = opt_malloc(sizeof(struct server));
char *p;
int i;
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)addr)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN;
serv->next = daemon->servers;
daemon->servers = serv;
return serv;
}
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
static int is_tag_prefix(char *arg) static int is_tag_prefix(char *arg)
@@ -1325,7 +1388,7 @@ void reset_option_bool(unsigned int opt)
daemon->options2 &= ~(1u << (opt - 32)); daemon->options2 &= ~(1u << (opt - 32));
} }
static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line) static int one_opt(int option, char *arg, char *errstr, char *gen_err, int command_line, int servers_only)
{ {
int i; int i;
char *comma; char *comma;
@@ -1529,6 +1592,10 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break; break;
} }
case LOPT_SERVERS_FILE:
daemon->servers_file = opt_string_alloc(arg);
break;
case 'm': /* --mx-host */ case 'm': /* --mx-host */
{ {
int pref = 1; int pref = 1;
@@ -1827,31 +1894,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* generate the equivalent of /* generate the equivalent of
local=/<domain>/ local=/<domain>/
local=/xxx.yyy.zzz.in-addr.arpa/ */ local=/xxx.yyy.zzz.in-addr.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server)); struct server *serv = add_rev4(new->start, msize);
in_addr_t a = ntohl(new->start.s_addr) >> 8; serv->flags |= SERV_NO_ADDR;
char *p;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(25); /* strlen("xxx.yyy.zzz.in-addr.arpa")+1 */
if (msize == 24)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
if (msize != 8)
p += sprintf(p, "%d.", a & 0xff);
a = a >> 8;
p += sprintf(p, "%d.in-addr.arpa", a & 0xff);
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
} }
} }
} }
@@ -1887,29 +1931,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* generate the equivalent of /* generate the equivalent of
local=/<domain>/ local=/<domain>/
local=/xxx.yyy.zzz.ip6.arpa/ */ local=/xxx.yyy.zzz.ip6.arpa/ */
struct server *serv = opt_malloc(sizeof(struct server)); struct server *serv = add_rev6(&new->start6, msize);
char *p; serv->flags |= SERV_NO_ADDR;
memset(serv, 0, sizeof(struct server));
serv->domain = d;
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
serv = opt_malloc(sizeof(struct server));
memset(serv, 0, sizeof(struct server));
p = serv->domain = opt_malloc(73); /* strlen("32*<n.>ip6.arpa")+1 */
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)&new->start6)[i>>3];
p += sprintf(p, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
}
p += sprintf(p, "ip6.arpa");
serv->flags = SERV_HAS_DOMAIN | SERV_NO_ADDR;
serv->next = daemon->servers;
daemon->servers = serv;
} }
} }
} }
@@ -2136,6 +2159,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
memset(newlist, 0, sizeof(struct server)); memset(newlist, 0, sizeof(struct server));
} }
if (servers_only && option == 'S')
newlist->flags |= SERV_FROM_FILE;
if (option == 'A') if (option == 'A')
{ {
newlist->flags |= SERV_LITERAL_ADDRESS; newlist->flags |= SERV_LITERAL_ADDRESS;
@@ -2180,6 +2206,40 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
break; break;
} }
case LOPT_REV_SERV: /* --rev-server */
{
char *string;
int size;
struct server *serv;
struct in_addr addr4;
#ifdef HAVE_IPV6
struct in6_addr addr6;
#endif
unhide_metas(arg);
if (!arg || !(comma=split(arg)) || !(string = split_chr(arg, '/')) || !atoi_check(string, &size))
ret_err(gen_err);
if (inet_pton(AF_INET, arg, &addr4))
serv = add_rev4(addr4, size);
#ifdef HAVE_IPV6
else if (inet_pton(AF_INET6, arg, &addr6))
serv = add_rev6(&addr6, size);
#endif
else
ret_err(gen_err);
string = parse_server(comma, &serv->addr, &serv->source_addr, serv->interface, &serv->flags);
if (string)
ret_err(string);
if (servers_only)
serv->flags |= SERV_FROM_FILE;
break;
}
case LOPT_IPSET: /* --ipset */ case LOPT_IPSET: /* --ipset */
#ifndef HAVE_IPSET #ifndef HAVE_IPSET
ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives")); ret_err(_("recompile with HAVE_IPSET defined to enable ipset directives"));
@@ -3754,12 +3814,13 @@ static void read_file(char *file, FILE *f, int hard_opt)
while (fgets(buff, MAXDNAME, f)) while (fgets(buff, MAXDNAME, f))
{ {
int white, i, option = hard_opt; int white, i;
volatile int option = (hard_opt == LOPT_REV_SERV) ? 0 : hard_opt;
char *errmess, *p, *arg = NULL, *start; char *errmess, *p, *arg = NULL, *start;
size_t len; size_t len;
/* Memory allocation failure longjmps here if mem_recover == 1 */ /* Memory allocation failure longjmps here if mem_recover == 1 */
if (option != 0) if (option != 0 || hard_opt == LOPT_REV_SERV)
{ {
if (setjmp(mem_jmp)) if (setjmp(mem_jmp))
continue; continue;
@@ -3860,13 +3921,15 @@ static void read_file(char *file, FILE *f, int hard_opt)
errmess = _("extraneous parameter"); errmess = _("extraneous parameter");
else if (opts[i].has_arg == 1 && !arg) else if (opts[i].has_arg == 1 && !arg)
errmess = _("missing parameter"); errmess = _("missing parameter");
else if (hard_opt == LOPT_REV_SERV && option != 'S' && option != LOPT_REV_SERV)
errmess = _("illegal option");
} }
oops: oops:
if (errmess) if (errmess)
strcpy(daemon->namebuff, errmess); strcpy(daemon->namebuff, errmess);
if (errmess || !one_opt(option, arg, buff, _("error"), 0)) if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
{ {
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file); sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
if (hard_opt != 0) if (hard_opt != 0)
@@ -4048,6 +4111,22 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
return list; return list;
} }
void read_servers_file(void)
{
FILE *f;
if (!(f = fopen(daemon->servers_file, "r")))
{
my_syslog(LOG_ERR, _("cannot read %s: %s"), daemon->servers_file, strerror(errno));
return;
}
mark_servers(SERV_FROM_FILE);
cleanup_servers();
read_file(daemon->servers_file, f, LOPT_REV_SERV);
}
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
void reread_dhcp(void) void reread_dhcp(void)
@@ -4241,9 +4320,9 @@ void read_opts(int argc, char **argv, char *compile_opts)
else else
{ {
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1)) if (!one_opt(option, arg, daemon->namebuff, _("try --help"), 1, 0))
#else #else
if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1)) if (!one_opt(option, arg, daemon->namebuff, _("try -w"), 1, 0))
#endif #endif
die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF); die(_("bad command line options: %s"), daemon->namebuff, EC_BADCONF);
} }
+1 -1
View File
@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
return ansp; return ansp;
} }
static unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen) unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen)
{ {
int i, rdlen; int i, rdlen;