mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
14
Commits
v2.86test7
...
v2.86rc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c83e33d608 | ||
|
|
7b80c75d9d | ||
|
|
79337f99ae | ||
|
|
a42ee397f3 | ||
|
|
e58f8bb8c1 | ||
|
|
58cf958e41 | ||
|
|
06d01f7ae4 | ||
|
|
1a33eec0ba | ||
|
|
82de7a1e96 | ||
|
|
8e9bde57c5 | ||
|
|
1bb70e08be | ||
|
|
9afcb7ae61 | ||
|
|
545c4955e6 | ||
|
|
2f2d59b35c |
No files matched your search
@@ -86,6 +86,12 @@ version 2.86
|
||||
conntrack is configured. Thanks to Yick Xie for spotting
|
||||
the lack of this.
|
||||
|
||||
When --dhcp-hostsfile --dhcp-optsfile and --addn-hosts are
|
||||
given a directory as argument, define the order in which
|
||||
files within that directory are read (alphabetical order
|
||||
of filename). Thanks to Ed Wildgoose for the initial patch
|
||||
and motivation for this.
|
||||
|
||||
|
||||
version 2.85
|
||||
Fix problem with DNS retries in 2.83/2.84.
|
||||
@@ -157,6 +163,9 @@ version 2.85
|
||||
Tweak TFTP code to check sender of all received packets, as
|
||||
specified in RFC 1350 para 4.
|
||||
|
||||
Support some wildcard matching of input tags to --tag-if.
|
||||
Thanks to Geoff Back for the idea and the patch.
|
||||
|
||||
|
||||
version 2.84
|
||||
Fix a problem, introduced in 2.83, which could see DNS replies
|
||||
|
||||
+14
-6
@@ -1,4 +1,4 @@
|
||||
.TH DNSMASQ 8 2020-04-05
|
||||
.TH DNSMASQ 8 2021-08-16
|
||||
.SH NAME
|
||||
dnsmasq \- A lightweight DHCP and caching DNS server.
|
||||
.SH SYNOPSIS
|
||||
@@ -55,7 +55,8 @@ Don't read the hostnames in /etc/hosts.
|
||||
.B \-H, --addn-hosts=<file>
|
||||
Additional hosts file. Read the specified file as well as /etc/hosts. If \fB--no-hosts\fP is given, read
|
||||
only the specified file. This option may be repeated for more than one
|
||||
additional hosts file. If a directory is given, then read all the files contained in that directory.
|
||||
additional hosts file. If a directory is given, then read all the files contained in that directory
|
||||
in alphabetical order.
|
||||
.TP
|
||||
.B --hostsdir=<path>
|
||||
Read all the hosts files contained in the directory. New or changed files
|
||||
@@ -905,7 +906,7 @@ compiled in and the kernel must have conntrack support
|
||||
included and configured. This option cannot be combined with
|
||||
.B --query-port.
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>][,<netmask>[,<broadcast>]][,<lease time>]
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-addr>[,<end-addr>|<mode>[,<netmask>[,<broadcast>]]][,<lease time>]
|
||||
.TP
|
||||
.B \-F, --dhcp-range=[tag:<tag>[,tag:<tag>],][set:<tag>,]<start-IPv6addr>[,<end-IPv6addr>|constructor:<interface>][,<mode>][,<prefix-len>][,<lease time>]
|
||||
|
||||
@@ -1165,7 +1166,7 @@ has both wired and wireless interfaces.
|
||||
.TP
|
||||
.B --dhcp-hostsfile=<path>
|
||||
Read DHCP host information from the specified file. If a directory
|
||||
is given, then read all the files contained in that directory. The file contains
|
||||
is given, then read all the files contained in that directory in alphabetical order. The file contains
|
||||
information about one host per line. The format of a line is the same
|
||||
as text to the right of '=' in \fB--dhcp-host\fP. The advantage of storing DHCP host information
|
||||
in this file is that it can be changed without re-starting dnsmasq:
|
||||
@@ -1173,7 +1174,7 @@ the file will be re-read when dnsmasq receives SIGHUP.
|
||||
.TP
|
||||
.B --dhcp-optsfile=<path>
|
||||
Read DHCP option information from the specified file. If a directory
|
||||
is given, then read all the files contained in that directory. The advantage of
|
||||
is given, then read all the files contained in that directory in alphabetical order. The advantage of
|
||||
using this option is the same as for \fB--dhcp-hostsfile\fP: the
|
||||
\fB--dhcp-optsfile\fP will be re-read when dnsmasq receives SIGHUP. Note that
|
||||
it is possible to encode the information in a
|
||||
@@ -1188,7 +1189,8 @@ directory, and not an individual file. Changed or new files within
|
||||
the directory are read automatically, without the need to send SIGHUP.
|
||||
If a file is deleted or changed after it has been read by dnsmasq, then the
|
||||
host record it contained will remain until dnsmasq receives a SIGHUP, or
|
||||
is restarted; ie host records are only added dynamically.
|
||||
is restarted; ie host records are only added dynamically. The order in which the
|
||||
files in a directory are read is not defined.
|
||||
.TP
|
||||
.B --dhcp-optsdir=<path>
|
||||
This is equivalent to \fB--dhcp-optsfile\fP, with the differences noted for \fB--dhcp-hostsdir\fP.
|
||||
@@ -1433,6 +1435,12 @@ Any number of set: and tag: forms may appear, in any order.
|
||||
tag set by another
|
||||
.B --tag-if,
|
||||
the line which sets the tag must precede the one which tests it.
|
||||
|
||||
As an extension, the tag:<tag> clauses support limited wildcard matching,
|
||||
similar to the matching in the \fB--interface\fP directive. This allows, for
|
||||
example, using \fB--tag-if=set:ppp,tag:ppp*\fP to set the tag 'ppp' for all requests
|
||||
received on any matching interface (ppp0, ppp1, etc). This can be used in conjunction
|
||||
with the tag:!<tag> format meaning that no tag matching the wildcard may be set.
|
||||
.TP
|
||||
.B \-J, --dhcp-ignore=tag:<tag>[,tag:<tag>]
|
||||
When all the given tags appear in the tag set ignore the host and do
|
||||
|
||||
+731
-579
File diff suppressed because it is too large.
Load diff
+12
-8
@@ -488,8 +488,6 @@ struct crec *cache_insert(char *name, union all_addr *addr, unsigned short class
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Don't log DNSSEC records here, done elsewhere */
|
||||
log_query(flags | F_UPSTREAM, name, addr, NULL);
|
||||
if (daemon->max_cache_ttl != 0 && daemon->max_cache_ttl < ttl)
|
||||
ttl = daemon->max_cache_ttl;
|
||||
if (daemon->min_cache_ttl != 0 && daemon->min_cache_ttl > ttl)
|
||||
@@ -1855,7 +1853,7 @@ char *querystr(char *desc, unsigned short type)
|
||||
if (types)
|
||||
sprintf(buff, "<%s>", types);
|
||||
else
|
||||
sprintf(buff, "type=%d", type);
|
||||
sprintf(buff, "<type=%d>", type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1897,17 +1895,24 @@ static char *edestr(int ede)
|
||||
|
||||
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
{
|
||||
char *source, *dest = daemon->addrbuff;
|
||||
char *source, *dest = arg;
|
||||
char *verb = "is";
|
||||
char *extra = "";
|
||||
|
||||
if (!option_bool(OPT_LOG))
|
||||
return;
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if ((flags & F_DNSSECOK) && option_bool(OPT_EXTRALOG))
|
||||
extra = " (DNSSEC signed)";
|
||||
#endif
|
||||
|
||||
name = sanitise(name);
|
||||
|
||||
if (addr)
|
||||
{
|
||||
dest = daemon->addrbuff;
|
||||
|
||||
if (flags & F_KEYTAG)
|
||||
sprintf(daemon->addrbuff, arg, addr->log.keytag, addr->log.algo, addr->log.digest);
|
||||
else if (flags & F_RCODE)
|
||||
@@ -1929,13 +1934,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
|
||||
sprintf(extra, " (EDE: %s)", edestr(addr->log.ede));
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (flags & (F_IPV4 | F_IPV6))
|
||||
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
|
||||
addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
|
||||
else
|
||||
dest = arg;
|
||||
}
|
||||
else
|
||||
dest = arg;
|
||||
|
||||
if (flags & F_REVERSE)
|
||||
{
|
||||
|
||||
+34
-1
@@ -79,13 +79,46 @@ ssize_t recv_dhcp_packet(int fd, struct msghdr *msg)
|
||||
return (msg->msg_flags & MSG_TRUNC) ? -1 : new_sz;
|
||||
}
|
||||
|
||||
/* like match_netid() except that the check can have a trailing * for wildcard */
|
||||
/* started as a direct copy of match_netid() */
|
||||
int match_netid_wild(struct dhcp_netid *check, struct dhcp_netid *pool)
|
||||
{
|
||||
struct dhcp_netid *tmp1;
|
||||
|
||||
for (; check; check = check->next)
|
||||
{
|
||||
const int check_len = strlen(check->net);
|
||||
const int is_wc = (check->net[check_len - 1] == '*');
|
||||
|
||||
/* '#' for not is for backwards compat. */
|
||||
if (check->net[0] != '!' && check->net[0] != '#')
|
||||
{
|
||||
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||
if (is_wc ? (strncmp(check->net, tmp1->net, check_len-1) == 0) :
|
||||
(strcmp(check->net, tmp1->net) == 0))
|
||||
break;
|
||||
if (!tmp1)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
for (tmp1 = pool; tmp1; tmp1 = tmp1->next)
|
||||
if (is_wc ? (strncmp((check->net)+1, tmp1->net, check_len-2) == 0) :
|
||||
(strcmp((check->net)+1, tmp1->net) == 0))
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct dhcp_netid *run_tag_if(struct dhcp_netid *tags)
|
||||
{
|
||||
struct tag_if *exprs;
|
||||
struct dhcp_netid_list *list;
|
||||
|
||||
/* this now uses match_netid_wild() above so that tag_if can
|
||||
* be used to set a 'group of interfaces' tag.
|
||||
*/
|
||||
for (exprs = daemon->tag_if; exprs; exprs = exprs->next)
|
||||
if (match_netid(exprs->tag, tags, 1))
|
||||
if (match_netid_wild(exprs->tag, tags))
|
||||
for (list = exprs->set; list; list = list->next)
|
||||
{
|
||||
list->list->next = tags;
|
||||
|
||||
@@ -76,9 +76,9 @@ unsigned char *hash_questions(struct dns_header *header, size_t plen, char *name
|
||||
|
||||
#else /* HAVE_DNSSEC || HAVE_CRYPTOHASH */
|
||||
|
||||
#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest
|
||||
typedef unsigned char BYTE; // 8-bit byte
|
||||
typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines
|
||||
#define SHA256_BLOCK_SIZE 32 /* SHA256 outputs a 32 byte digest */
|
||||
typedef unsigned char BYTE; /* 8-bit byte */
|
||||
typedef unsigned int WORD; /* 32-bit word, change to "long" for 16-bit machines */
|
||||
|
||||
typedef struct {
|
||||
BYTE data[64];
|
||||
@@ -238,7 +238,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
|
||||
i = ctx->datalen;
|
||||
|
||||
// Pad whatever data is left in the buffer.
|
||||
/* Pad whatever data is left in the buffer. */
|
||||
if (ctx->datalen < 56)
|
||||
{
|
||||
ctx->data[i++] = 0x80;
|
||||
@@ -254,7 +254,7 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
memset(ctx->data, 0, 56);
|
||||
}
|
||||
|
||||
// Append to the padding the total message's length in bits and transform.
|
||||
/* Append to the padding the total message's length in bits and transform. */
|
||||
ctx->bitlen += ctx->datalen * 8;
|
||||
ctx->data[63] = ctx->bitlen;
|
||||
ctx->data[62] = ctx->bitlen >> 8;
|
||||
@@ -266,8 +266,8 @@ static void sha256_final(SHA256_CTX *ctx, BYTE hash[])
|
||||
ctx->data[56] = ctx->bitlen >> 56;
|
||||
sha256_transform(ctx, ctx->data);
|
||||
|
||||
// Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
// reverse all the bytes when copying the final state to the output hash.
|
||||
/* Since this implementation uses little endian byte ordering and SHA uses big endian,
|
||||
reverse all the bytes when copying the final state to the output hash. */
|
||||
for (i = 0; i < 4; ++i)
|
||||
{
|
||||
hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff;
|
||||
|
||||
+45
-20
@@ -2700,11 +2700,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
{
|
||||
if ((err = parse_server(arg, &serv_addr, &source_addr, interface, &flags)))
|
||||
ret_err(err);
|
||||
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (strlen(domain) == 0 && lastdomain)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
|
||||
}
|
||||
|
||||
if (servers_only && option == 'S')
|
||||
@@ -2712,6 +2707,12 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
|
||||
while (1)
|
||||
{
|
||||
/* server=//1.2.3.4 is special. */
|
||||
if (strlen(domain) == 0 && lastdomain)
|
||||
flags |= SERV_FOR_NODOTS;
|
||||
else
|
||||
flags &= ~SERV_FOR_NODOTS;
|
||||
|
||||
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, &addr))
|
||||
ret_err(gen_err);
|
||||
|
||||
@@ -5027,15 +5028,33 @@ static int one_file(char *file, int hard_opt)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int file_filter(const struct dirent *ent)
|
||||
{
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
|
||||
/* 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] == '.')
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* expand any name which is a directory */
|
||||
struct hostsfile *expand_filelist(struct hostsfile *list)
|
||||
{
|
||||
unsigned int i;
|
||||
struct hostsfile *ah;
|
||||
int entcnt, n;
|
||||
struct hostsfile *ah, *last, *next, **up;
|
||||
struct dirent **namelist;
|
||||
|
||||
/* find largest used index */
|
||||
for (i = SRC_AH, ah = list; ah; ah = ah->next)
|
||||
{
|
||||
last = ah;
|
||||
|
||||
if (i <= ah->index)
|
||||
i = ah->index + 1;
|
||||
|
||||
@@ -5051,46 +5070,48 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
|
||||
struct stat buf;
|
||||
if (stat(ah->fname, &buf) != -1 && S_ISDIR(buf.st_mode))
|
||||
{
|
||||
DIR *dir_stream;
|
||||
struct dirent *ent;
|
||||
|
||||
/* don't read this as a file */
|
||||
ah->flags |= AH_INACTIVE;
|
||||
|
||||
if (!(dir_stream = opendir(ah->fname)))
|
||||
entcnt = scandir(ah->fname, &namelist, file_filter, alphasort);
|
||||
if (entcnt < 0)
|
||||
my_syslog(LOG_ERR, _("cannot access directory %s: %s"),
|
||||
ah->fname, strerror(errno));
|
||||
else
|
||||
{
|
||||
while ((ent = readdir(dir_stream)))
|
||||
for (n = 0; n < entcnt; n++)
|
||||
{
|
||||
ent = namelist[n];
|
||||
size_t lendir = strlen(ah->fname);
|
||||
size_t lenfile = strlen(ent->d_name);
|
||||
struct hostsfile *ah1;
|
||||
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;
|
||||
|
||||
/* see if we have an existing record.
|
||||
dir is ah->fname
|
||||
file is ent->d_name
|
||||
path to match is ah1->fname */
|
||||
|
||||
for (ah1 = list; ah1; ah1 = ah1->next)
|
||||
for (up = &list, ah1 = list; ah1; ah1 = next)
|
||||
{
|
||||
next = ah1->next;
|
||||
|
||||
if (lendir < strlen(ah1->fname) &&
|
||||
strstr(ah1->fname, ah->fname) == ah1->fname &&
|
||||
ah1->fname[lendir] == '/' &&
|
||||
strcmp(ah1->fname + lendir + 1, ent->d_name) == 0)
|
||||
{
|
||||
ah1->flags &= ~AH_INACTIVE;
|
||||
/* If found, remove from list to re-insert at the end.
|
||||
Unless it's already at the end. */
|
||||
if (last != ah1)
|
||||
*up = next;
|
||||
break;
|
||||
}
|
||||
|
||||
up = &ah1->next;
|
||||
}
|
||||
|
||||
/* make new record */
|
||||
@@ -5111,17 +5132,21 @@ struct hostsfile *expand_filelist(struct hostsfile *list)
|
||||
ah1->fname = path;
|
||||
ah1->index = i++;
|
||||
ah1->flags = AH_DIR;
|
||||
ah1->next = list;
|
||||
list = ah1;
|
||||
}
|
||||
|
||||
/* Edge case, may be the last in the list anyway */
|
||||
if (last != ah1)
|
||||
last->next = ah1;
|
||||
ah1->next = NULL;
|
||||
last = ah1;
|
||||
|
||||
/* inactivate record if not regular file */
|
||||
if ((ah1->flags & AH_DIR) && stat(ah1->fname, &buf) != -1 && !S_ISREG(buf.st_mode))
|
||||
ah1->flags |= AH_INACTIVE;
|
||||
|
||||
}
|
||||
closedir(dir_stream);
|
||||
}
|
||||
free(namelist);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-26
@@ -20,13 +20,13 @@
|
||||
|
||||
#define LOG(...) \
|
||||
do { \
|
||||
my_syslog(LOG_WARNING, __VA_ARGS__); \
|
||||
my_syslog(LOG_DEBUG, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT(condition) \
|
||||
do { \
|
||||
if (!(condition)) \
|
||||
LOG("[pattern.c:%d] Assertion failure: %s", __LINE__, #condition); \
|
||||
my_syslog(LOG_ERR, _("[pattern.c:%d] Assertion failure: %s"), __LINE__, #condition); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
@@ -66,8 +66,8 @@ static int is_string_matching_glob_pattern(
|
||||
pattern_character -= 'a' - 'A';
|
||||
if (pattern_character == '*')
|
||||
{
|
||||
// zero-or-more-character wildcard
|
||||
// Try to match at value_index, otherwise restart at value_index + 1 next.
|
||||
/* zero-or-more-character wildcard */
|
||||
/* Try to match at value_index, otherwise restart at value_index + 1 next. */
|
||||
next_pattern_index = pattern_index;
|
||||
pattern_index++;
|
||||
if (value_index < num_value_bytes)
|
||||
@@ -78,7 +78,7 @@ static int is_string_matching_glob_pattern(
|
||||
}
|
||||
else
|
||||
{
|
||||
// ordinary character
|
||||
/* ordinary character */
|
||||
if (value_index < num_value_bytes)
|
||||
{
|
||||
char value_character = value[value_index];
|
||||
@@ -139,7 +139,7 @@ int is_valid_dns_name(const char *value)
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name: Invalid character %c.", *c);
|
||||
LOG(_("Invalid DNS name: Invalid character %c."), *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c)
|
||||
@@ -148,12 +148,12 @@ int is_valid_dns_name(const char *value)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name: Empty label.");
|
||||
LOG(_("Invalid DNS name: Empty label."));
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label starts with hyphen.");
|
||||
LOG(_("Invalid DNS name: Label starts with hyphen."));
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
@@ -167,13 +167,13 @@ int is_valid_dns_name(const char *value)
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name: Label ends with hyphen.");
|
||||
LOG(_("Invalid DNS name: Label ends with hyphen."));
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label);
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name: Label is too long (%zu).", num_label_bytes);
|
||||
LOG(_("Invalid DNS name: Label is too long (%zu)."), num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
@@ -181,12 +181,12 @@ int is_valid_dns_name(const char *value)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name: Not enough labels (%zu).", num_labels);
|
||||
LOG(_("Invalid DNS name: Not enough labels (%zu)."), num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name: Final label is fully numeric.");
|
||||
LOG(_("Invalid DNS name: Final label is fully numeric."));
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
@@ -196,12 +196,12 @@ int is_valid_dns_name(const char *value)
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name: \"local\" pseudo-TLD.");
|
||||
LOG(_("Invalid DNS name: \"local\" pseudo-TLD."));
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name has invalid length (%zu).", num_bytes);
|
||||
LOG(_("DNS name has invalid length (%zu)."), num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -249,13 +249,13 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
for (const char *c = value;; c++)
|
||||
{
|
||||
if (*c &&
|
||||
*c != '*' && // Wildcard.
|
||||
*c != '*' && /* Wildcard. */
|
||||
*c != '-' && *c != '.' &&
|
||||
(*c < '0' || *c > '9') &&
|
||||
(*c < 'A' || *c > 'Z') &&
|
||||
(*c < 'a' || *c > 'z'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Invalid character %c.", *c);
|
||||
LOG(_("Invalid DNS name pattern: Invalid character %c."), *c);
|
||||
return 0;
|
||||
}
|
||||
if (*c && *c != '*')
|
||||
@@ -264,12 +264,12 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
if (!*c || *c == '.')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Empty label.");
|
||||
LOG(_("Invalid DNS name pattern: Empty label."));
|
||||
return 0;
|
||||
}
|
||||
if (*c == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label starts with hyphen.");
|
||||
LOG(_("Invalid DNS name pattern: Label starts with hyphen."));
|
||||
return 0;
|
||||
}
|
||||
label = c;
|
||||
@@ -282,7 +282,7 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
if (num_wildcards >= 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard character used more than twice per label.");
|
||||
LOG(_("Invalid DNS name pattern: Wildcard character used more than twice per label."));
|
||||
return 0;
|
||||
}
|
||||
num_wildcards++;
|
||||
@@ -292,13 +292,13 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
if (c[-1] == '-')
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label ends with hyphen.");
|
||||
LOG(_("Invalid DNS name pattern: Label ends with hyphen."));
|
||||
return 0;
|
||||
}
|
||||
size_t num_label_bytes = (size_t) (c - label) - num_wildcards;
|
||||
if (num_label_bytes > 63)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Label is too long (%zu).", num_label_bytes);
|
||||
LOG(_("Invalid DNS name pattern: Label is too long (%zu)."), num_label_bytes);
|
||||
return 0;
|
||||
}
|
||||
num_labels++;
|
||||
@@ -306,17 +306,17 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
{
|
||||
if (num_labels < 2)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Not enough labels (%zu).", num_labels);
|
||||
LOG(_("Invalid DNS name pattern: Not enough labels (%zu)."), num_labels);
|
||||
return 0;
|
||||
}
|
||||
if (num_wildcards != 0 || previous_label_has_wildcard)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Wildcard within final two labels.");
|
||||
LOG(_("Invalid DNS name pattern: Wildcard within final two labels."));
|
||||
return 0;
|
||||
}
|
||||
if (is_label_numeric)
|
||||
{
|
||||
LOG("Invalid DNS name pattern: Final label is fully numeric.");
|
||||
LOG(_("Invalid DNS name pattern: Final label is fully numeric."));
|
||||
return 0;
|
||||
}
|
||||
if (num_label_bytes == 5 &&
|
||||
@@ -326,12 +326,12 @@ int is_valid_dns_name_pattern(const char *value)
|
||||
(label[3] == 'a' || label[3] == 'A') &&
|
||||
(label[4] == 'l' || label[4] == 'L'))
|
||||
{
|
||||
LOG("Invalid DNS name pattern: \"local\" pseudo-TLD.");
|
||||
LOG(_("Invalid DNS name pattern: \"local\" pseudo-TLD."));
|
||||
return 0;
|
||||
}
|
||||
if (num_bytes < 1 || num_bytes > 253)
|
||||
{
|
||||
LOG("DNS name pattern has invalid length after removing wildcards (%zu).", num_bytes);
|
||||
LOG(_("DNS name pattern has invalid length after removing wildcards (%zu)."), num_bytes);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
|
||||
+323
-276
@@ -394,18 +394,13 @@ static int private_net6(struct in6_addr *a, int ban_localhost)
|
||||
((u32 *)a)[0] == htonl(0x20010db8); /* RFC 6303 4.6 */
|
||||
}
|
||||
|
||||
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, char *name, int *doctored)
|
||||
static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *header, size_t qlen, int *doctored)
|
||||
{
|
||||
int i, qtype, qclass, rdlen;
|
||||
|
||||
for (i = count; i != 0; i--)
|
||||
{
|
||||
if (name && option_bool(OPT_LOG))
|
||||
{
|
||||
if (!extract_name(header, qlen, &p, name, 1, 10))
|
||||
return 0;
|
||||
}
|
||||
else if (!(p = skip_name(p, header, qlen, 10)))
|
||||
if (!(p = skip_name(p, header, qlen, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
@@ -444,34 +439,6 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (qtype == T_TXT && name && option_bool(OPT_LOG))
|
||||
{
|
||||
unsigned char *p1 = p;
|
||||
if (!CHECK_LEN(header, p1, qlen, rdlen))
|
||||
return 0;
|
||||
while ((p1 - p) < rdlen)
|
||||
{
|
||||
unsigned int i, len = *p1;
|
||||
unsigned char *p2 = p1;
|
||||
if ((p1 + len - p) >= rdlen)
|
||||
return 0; /* bad packet */
|
||||
/* make counted string zero-term and sanitise */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!isprint((int)*(p2+1)))
|
||||
break;
|
||||
|
||||
*p2 = *(p2+1);
|
||||
p2++;
|
||||
}
|
||||
*p2 = 0;
|
||||
my_syslog(LOG_INFO, "reply %s is %s", name, p1);
|
||||
/* restore */
|
||||
memmove(p1 + 1, p1, i);
|
||||
*p1 = len;
|
||||
p1 += len+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ADD_RDLEN(header, p, qlen, rdlen))
|
||||
return 0; /* bad packet */
|
||||
@@ -480,7 +447,7 @@ static unsigned char *do_doctor(unsigned char *p, int count, struct dns_header *
|
||||
return p;
|
||||
}
|
||||
|
||||
static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doctored)
|
||||
static int find_soa(struct dns_header *header, size_t qlen, int *doctored)
|
||||
{
|
||||
unsigned char *p;
|
||||
int qtype, qclass, rdlen;
|
||||
@@ -489,7 +456,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
||||
|
||||
/* first move to NS section and find TTL from any SOA section */
|
||||
if (!(p = skip_questions(header, qlen)) ||
|
||||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, name, doctored)))
|
||||
!(p = do_doctor(p, ntohs(header->ancount), header, qlen, doctored)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
for (i = ntohs(header->nscount); i != 0; i--)
|
||||
@@ -525,7 +492,7 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
||||
}
|
||||
|
||||
/* rewrite addresses in additional section too */
|
||||
if (!do_doctor(p, ntohs(header->arcount), header, qlen, NULL, doctored))
|
||||
if (!do_doctor(p, ntohs(header->arcount), header, qlen, doctored))
|
||||
return 0;
|
||||
|
||||
if (!found_soa)
|
||||
@@ -534,6 +501,40 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
|
||||
return minttl;
|
||||
}
|
||||
|
||||
/* Print TXT reply to log */
|
||||
static int print_txt(struct dns_header *header, const size_t qlen, char *name,
|
||||
unsigned char *p, const int ardlen, int secflag)
|
||||
{
|
||||
unsigned char *p1 = p;
|
||||
if (!CHECK_LEN(header, p1, qlen, ardlen))
|
||||
return 0;
|
||||
/* Loop over TXT payload */
|
||||
while ((p1 - p) < ardlen)
|
||||
{
|
||||
unsigned int i, len = *p1;
|
||||
unsigned char *p3 = p1;
|
||||
if ((p1 + len - p) >= ardlen)
|
||||
return 0; /* bad packet */
|
||||
|
||||
/* make counted string zero-term and sanitise */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (!isprint((int)*(p3+1)))
|
||||
break;
|
||||
*p3 = *(p3+1);
|
||||
p3++;
|
||||
}
|
||||
|
||||
*p3 = 0;
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, (char*)p1);
|
||||
/* restore */
|
||||
memmove(p1 + 1, p1, i);
|
||||
*p1 = len;
|
||||
p1 += len+1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Note that the following code can create CNAME chains that don't point to a real record,
|
||||
either because of lack of memory, or lack of SOA records. These are treated by the cache code as
|
||||
expired and cleaned out that way.
|
||||
@@ -543,7 +544,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
int secure, int *doctored)
|
||||
{
|
||||
unsigned char *p, *p1, *endrr, *namep;
|
||||
int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||
int j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
|
||||
unsigned long ttl = 0;
|
||||
union all_addr addr;
|
||||
#ifdef HAVE_IPSET
|
||||
@@ -551,15 +552,21 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
#else
|
||||
(void)ipsets; /* unused */
|
||||
#endif
|
||||
|
||||
int found = 0, cname_count = CNAME_CHAIN;
|
||||
struct crec *cpp = NULL;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int cname_short = 0;
|
||||
#endif
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
cache_start_insert();
|
||||
|
||||
/* find_soa is needed for dns_doctor and logging side-effects, so don't call it lazily if there are any. */
|
||||
if (daemon->doctors || option_bool(OPT_LOG) || option_bool(OPT_DNSSEC_VALID))
|
||||
/* find_soa is needed for dns_doctor side effects, so don't call it lazily if there are any. */
|
||||
if (daemon->doctors || option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, name, doctored);
|
||||
ttl = find_soa(header, qlen, doctored);
|
||||
|
||||
if (*doctored)
|
||||
{
|
||||
@@ -567,159 +574,46 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
for (i = 0; i < ntohs(header->ancount); i++)
|
||||
if (daemon->rr_status[i] != 0)
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
if (daemon->rr_status[j] != 0)
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* go through the questions. */
|
||||
p = (unsigned char *)(header+1);
|
||||
namep = p = (unsigned char *)(header+1);
|
||||
|
||||
for (i = ntohs(header->qdcount); i != 0; i--)
|
||||
{
|
||||
int found = 0, cname_count = CNAME_CHAIN;
|
||||
struct crec *cpp = NULL;
|
||||
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
int cname_short = 0;
|
||||
#endif
|
||||
unsigned long cttl = ULONG_MAX, attl;
|
||||
|
||||
namep = p;
|
||||
if (!extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
if (ntohs(header->qdcount) != 1 || !extract_name(header, qlen, &p, name, 1, 4))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(qtype, p);
|
||||
GETSHORT(qclass, p);
|
||||
|
||||
if (qclass != C_IN)
|
||||
return 0;
|
||||
|
||||
/* PTRs: we chase CNAMEs here, since we have no way to
|
||||
represent them in the cache. */
|
||||
if (qtype == T_PTR)
|
||||
{
|
||||
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
|
||||
|
||||
if (qclass != C_IN)
|
||||
continue;
|
||||
|
||||
/* PTRs: we chase CNAMEs here, since we have no way to
|
||||
represent them in the cache. */
|
||||
if (qtype == T_PTR)
|
||||
{
|
||||
int name_encoding = in_arpa_name_2_addr(name, &addr);
|
||||
|
||||
if (!name_encoding)
|
||||
continue;
|
||||
|
||||
if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
unsigned char *tmp = namep;
|
||||
/* the loop body overwrites the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
|
||||
!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
/* TTL of record is minimum of CNAMES and PTR */
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
|
||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
||||
{
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
|
||||
{
|
||||
/* validated RR anywhere in CNAME chain, don't cache. */
|
||||
if (cname_short || aqtype == T_CNAME)
|
||||
return 0;
|
||||
|
||||
secflag = F_DNSSECOK;
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < cttl)
|
||||
cttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES, we can't cache. */
|
||||
#ifdef HAVE_DNSSEC
|
||||
cname_short = 1;
|
||||
#endif
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
found = 1;
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
{
|
||||
if (!searched_soa)
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, NULL, doctored);
|
||||
}
|
||||
if (ttl)
|
||||
cache_insert(NULL, &addr, C_IN, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
/* everything other than PTR */
|
||||
struct crec *newc;
|
||||
int addrlen = 0;
|
||||
|
||||
if (qtype == T_A)
|
||||
{
|
||||
addrlen = INADDRSZ;
|
||||
flags |= F_IPV4;
|
||||
}
|
||||
else if (qtype == T_AAAA)
|
||||
{
|
||||
addrlen = IN6ADDRSZ;
|
||||
flags |= F_IPV6;
|
||||
}
|
||||
else if (qtype == T_SRV)
|
||||
flags |= F_SRV;
|
||||
else
|
||||
continue;
|
||||
|
||||
cname_loop1:
|
||||
cname_loop:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
@@ -728,128 +622,281 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
|
||||
/* TTL of record is minimum of CNAMES and PTR */
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
|
||||
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
|
||||
{
|
||||
/* validated RR anywhere in CNAME chain, don't cache. */
|
||||
if (cname_short || aqtype == T_CNAME)
|
||||
insert = 0;
|
||||
|
||||
secflag = F_DNSSECOK;
|
||||
|
||||
/* limit TTl based on sig. */
|
||||
if (daemon->rr_status[j] < attl)
|
||||
attl = daemon->rr_status[j];
|
||||
/* limit TTL based on signature. */
|
||||
if (daemon->rr_status[j] < cttl)
|
||||
cttl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL);
|
||||
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
if (cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
}
|
||||
|
||||
cpp = newc;
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
|
||||
goto cname_loop1;
|
||||
}
|
||||
else if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
found = 1;
|
||||
|
||||
if (flags & F_SRV)
|
||||
{
|
||||
unsigned char *tmp = namep;
|
||||
|
||||
if (!CHECK_LEN(header, p1, qlen, 6))
|
||||
return 0; /* bad packet */
|
||||
GETSHORT(addr.srv.priority, p1);
|
||||
GETSHORT(addr.srv.weight, p1);
|
||||
GETSHORT(addr.srv.srvport, p1);
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
||||
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
||||
return 0;
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* copy address into aligned storage */
|
||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||
return 0; /* bad packet */
|
||||
memcpy(&addr, p1, addrlen);
|
||||
|
||||
/* check for returned address in private space */
|
||||
if (check_rebind)
|
||||
{
|
||||
if ((flags & F_IPV4) &&
|
||||
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
|
||||
if ((flags & F_IPV6) &&
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
{
|
||||
ipsets_cur = ipsets;
|
||||
while (*ipsets_cur)
|
||||
{
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
|
||||
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
|
||||
}
|
||||
}
|
||||
return 0; /* looped CNAMES, we can't cache. */
|
||||
#ifdef HAVE_DNSSEC
|
||||
cname_short = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
goto cname_loop;
|
||||
}
|
||||
|
||||
found = 1;
|
||||
|
||||
if (!name_encoding)
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype));
|
||||
else
|
||||
{
|
||||
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL);
|
||||
if (insert)
|
||||
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
|
||||
}
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
}
|
||||
}
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
{
|
||||
if (!searched_soa)
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, doctored);
|
||||
}
|
||||
|
||||
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
|
||||
if (name_encoding && ttl)
|
||||
{
|
||||
flags |= F_REVERSE | name_encoding;
|
||||
cache_insert(NULL, &addr, C_IN, now, ttl, flags);
|
||||
}
|
||||
|
||||
log_query(flags | F_UPSTREAM, name, &addr, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* everything other than PTR */
|
||||
struct crec *newc;
|
||||
int addrlen = 0, insert = 1;
|
||||
|
||||
if (qtype == T_A)
|
||||
{
|
||||
addrlen = INADDRSZ;
|
||||
flags |= F_IPV4;
|
||||
}
|
||||
else if (qtype == T_AAAA)
|
||||
{
|
||||
addrlen = IN6ADDRSZ;
|
||||
flags |= F_IPV6;
|
||||
}
|
||||
else if (qtype == T_SRV)
|
||||
flags |= F_SRV;
|
||||
else
|
||||
insert = 0; /* NOTE: do not cache data from CNAME queries. */
|
||||
|
||||
cname_loop1:
|
||||
if (!(p1 = skip_questions(header, qlen)))
|
||||
return 0;
|
||||
|
||||
for (j = 0; j < ntohs(header->ancount); j++)
|
||||
{
|
||||
int secflag = 0;
|
||||
|
||||
if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
|
||||
return 0; /* bad packet */
|
||||
|
||||
GETSHORT(aqtype, p1);
|
||||
GETSHORT(aqclass, p1);
|
||||
GETLONG(attl, p1);
|
||||
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
|
||||
{
|
||||
(p1) -= 4;
|
||||
PUTLONG(daemon->max_ttl, p1);
|
||||
}
|
||||
GETSHORT(ardlen, p1);
|
||||
endrr = p1+ardlen;
|
||||
|
||||
/* Not what we're looking for? */
|
||||
if (aqclass != C_IN || res == 2)
|
||||
{
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID) && daemon->rr_status[j] != 0)
|
||||
{
|
||||
secflag = F_DNSSECOK;
|
||||
|
||||
/* limit TTl based on sig. */
|
||||
if (daemon->rr_status[j] < attl)
|
||||
attl = daemon->rr_status[j];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (aqtype == T_CNAME)
|
||||
{
|
||||
if (!cname_count--)
|
||||
return 0; /* looped CNAMES */
|
||||
|
||||
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL);
|
||||
|
||||
if (insert)
|
||||
{
|
||||
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
|
||||
{
|
||||
newc->addr.cname.target.cache = NULL;
|
||||
newc->addr.cname.is_name_ptr = 0;
|
||||
if (cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
cpp = NULL;
|
||||
}
|
||||
|
||||
cpp = newc;
|
||||
if (attl < cttl)
|
||||
cttl = attl;
|
||||
}
|
||||
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
namep = p1;
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
|
||||
goto cname_loop1;
|
||||
}
|
||||
else if (aqtype != qtype)
|
||||
{
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (!option_bool(OPT_DNSSEC_VALID) || aqtype != T_RRSIG)
|
||||
#endif
|
||||
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, querystr(NULL, aqtype));
|
||||
}
|
||||
else if (!(flags & F_NXDOMAIN))
|
||||
{
|
||||
found = 1;
|
||||
|
||||
if (flags & F_SRV)
|
||||
{
|
||||
unsigned char *tmp = namep;
|
||||
|
||||
if (!CHECK_LEN(header, p1, qlen, 6))
|
||||
return 0; /* bad packet */
|
||||
GETSHORT(addr.srv.priority, p1);
|
||||
GETSHORT(addr.srv.weight, p1);
|
||||
GETSHORT(addr.srv.srvport, p1);
|
||||
if (!extract_name(header, qlen, &p1, name, 1, 0))
|
||||
return 0;
|
||||
addr.srv.targetlen = strlen(name) + 1; /* include terminating zero */
|
||||
if (!(addr.srv.target = blockdata_alloc(name, addr.srv.targetlen)))
|
||||
return 0;
|
||||
|
||||
/* we overwrote the original name, so get it back here. */
|
||||
if (!extract_name(header, qlen, &tmp, name, 1, 0))
|
||||
return 0;
|
||||
}
|
||||
else if (flags & (F_IPV4 | F_IPV6))
|
||||
{
|
||||
/* copy address into aligned storage */
|
||||
if (!CHECK_LEN(header, p1, qlen, addrlen))
|
||||
return 0; /* bad packet */
|
||||
memcpy(&addr, p1, addrlen);
|
||||
|
||||
/* check for returned address in private space */
|
||||
if (check_rebind)
|
||||
{
|
||||
if ((flags & F_IPV4) &&
|
||||
private_net(addr.addr4, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
|
||||
if ((flags & F_IPV6) &&
|
||||
private_net6(&addr.addr6, !option_bool(OPT_LOCAL_REBIND)))
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_IPSET
|
||||
if (ipsets && (flags & (F_IPV4 | F_IPV6)))
|
||||
{
|
||||
ipsets_cur = ipsets;
|
||||
while (*ipsets_cur)
|
||||
{
|
||||
log_query((flags & (F_IPV4 | F_IPV6)) | F_IPSET, name, &addr, *ipsets_cur);
|
||||
add_to_ipset(*ipsets_cur++, &addr, flags, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (insert)
|
||||
{
|
||||
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
cpp->addr.cname.target.cache = newc;
|
||||
cpp->addr.cname.uid = newc->uid;
|
||||
}
|
||||
cpp = NULL;
|
||||
}
|
||||
|
||||
if (aqtype == T_TXT)
|
||||
{
|
||||
if (!print_txt(header, qlen, name, p1, ardlen, secflag))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
log_query(flags | F_FORWARD | secflag | F_UPSTREAM, name, &addr, querystr(NULL, aqtype));
|
||||
}
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
p1 = endrr;
|
||||
if (!CHECK_LEN(header, p1, qlen, 0))
|
||||
return 0; /* bad packet */
|
||||
}
|
||||
|
||||
if (!found && !option_bool(OPT_NO_NEG))
|
||||
{
|
||||
if (!searched_soa)
|
||||
{
|
||||
if (!searched_soa)
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, doctored);
|
||||
}
|
||||
|
||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
if (ttl == 0)
|
||||
ttl = cttl;
|
||||
|
||||
log_query(F_UPSTREAM | F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0), name, NULL, NULL);
|
||||
|
||||
if (insert)
|
||||
{
|
||||
searched_soa = 1;
|
||||
ttl = find_soa(header, qlen, NULL, doctored);
|
||||
}
|
||||
/* If there's no SOA to get the TTL from, but there is a CNAME
|
||||
pointing at this, inherit its TTL */
|
||||
if (ttl || cpp)
|
||||
{
|
||||
newc = cache_insert(name, NULL, C_IN, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
newc = cache_insert(name, NULL, C_IN, now, ttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));
|
||||
if (newc && cpp)
|
||||
{
|
||||
next_uid(newc);
|
||||
|
||||
+45
-29
@@ -84,7 +84,7 @@ static void ubus_destroy(struct ubus_context *ubus)
|
||||
ubus_free(ubus);
|
||||
daemon->ubus = NULL;
|
||||
|
||||
// Forces re-initialization when we're reusing the same definitions later on.
|
||||
/* Forces re-initialization when we're reusing the same definitions later on. */
|
||||
ubus_object.id = 0;
|
||||
ubus_object_type.id = 0;
|
||||
}
|
||||
@@ -171,6 +171,16 @@ void check_ubus_listeners()
|
||||
}
|
||||
}
|
||||
|
||||
#define CHECK(stmt) \
|
||||
do { \
|
||||
int e = (stmt); \
|
||||
if (e) \
|
||||
{ \
|
||||
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||
return (UBUS_STATUS_UNKNOWN_ERROR); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method,
|
||||
struct blob_attr *msg)
|
||||
@@ -181,12 +191,13 @@ static int ubus_handle_metrics(struct ubus_context *ctx, struct ubus_object *obj
|
||||
(void)method;
|
||||
(void)msg;
|
||||
|
||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
||||
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||
|
||||
for (i=0; i < __METRIC_MAX; i++)
|
||||
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
|
||||
CHECK(blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]));
|
||||
|
||||
return ubus_send_reply(ctx, req, b.head);
|
||||
CHECK(ubus_send_reply(ctx, req, b.head));
|
||||
return UBUS_STATUS_OK;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
@@ -305,66 +316,71 @@ fail:
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CHECK
|
||||
|
||||
#define CHECK(stmt) \
|
||||
do { \
|
||||
int e = (stmt); \
|
||||
if (e) \
|
||||
{ \
|
||||
my_syslog(LOG_ERR, _("UBus command failed: %d (%s)"), e, #stmt); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, BLOBMSG_TYPE_TABLE);
|
||||
CHECK(blob_buf_init(&b, BLOBMSG_TYPE_TABLE));
|
||||
if (mac)
|
||||
blobmsg_add_string(&b, "mac", mac);
|
||||
CHECK(blobmsg_add_string(&b, "mac", mac));
|
||||
if (ip)
|
||||
blobmsg_add_string(&b, "ip", ip);
|
||||
CHECK(blobmsg_add_string(&b, "ip", ip));
|
||||
if (name)
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
if (interface)
|
||||
blobmsg_add_string(&b, "interface", interface);
|
||||
CHECK(blobmsg_add_string(&b, "interface", interface));
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
CHECK(ubus_notify(ubus, &ubus_object, type, b.head, -1));
|
||||
}
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
void ubus_event_bcast_connmark_allowlist_refused(u32 mark, const char *name)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
CHECK(blob_buf_init(&b, 0));
|
||||
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.refused", b.head, -1));
|
||||
}
|
||||
|
||||
void ubus_event_bcast_connmark_allowlist_resolved(u32 mark, const char *name, const char *value, u32 ttl)
|
||||
{
|
||||
struct ubus_context *ubus = (struct ubus_context *)daemon->ubus;
|
||||
int ret;
|
||||
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_u32(&b, "mark", mark);
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
blobmsg_add_string(&b, "value", value);
|
||||
blobmsg_add_u32(&b, "ttl", ttl);
|
||||
CHECK(blob_buf_init(&b, 0));
|
||||
CHECK(blobmsg_add_u32(&b, "mark", mark));
|
||||
CHECK(blobmsg_add_string(&b, "name", name));
|
||||
CHECK(blobmsg_add_string(&b, "value", value));
|
||||
CHECK(blobmsg_add_u32(&b, "ttl", ttl));
|
||||
|
||||
ret = ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000);
|
||||
if (ret)
|
||||
my_syslog(LOG_ERR, _("Failed to send UBus event: %s"), ubus_strerror(ret));
|
||||
/* Set timeout to allow UBus subscriber to configure firewall rules before returning. */
|
||||
CHECK(ubus_notify(ubus, &ubus_object, "connmark-allowlist.resolved", b.head, /* timeout: */ 1000));
|
||||
}
|
||||
#endif
|
||||
|
||||
#undef CHECK
|
||||
|
||||
#endif /* HAVE_UBUS */
|
||||
Reference in new issue
Block a user