mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-04 13:48:30 +00:00
Compare commits
20
Commits
v2.80test3
...
v2.80test5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da8b6517de | ||
|
|
c5db8f93ec | ||
|
|
974a6d087a | ||
|
|
b758b67c37 | ||
|
|
9bafdc62b7 | ||
|
|
97f876b64c | ||
|
|
cbfbd173c4 | ||
|
|
b6f926fbef | ||
|
|
c822620967 | ||
|
|
397c0502e2 | ||
|
|
1682d15a74 | ||
|
|
dd33e98da0 | ||
|
|
c16d966ad3 | ||
|
|
1dfed16071 | ||
|
|
6f835ed6c8 | ||
|
|
9d6fd1727e | ||
|
|
8c1b6a5fd7 | ||
|
|
8dcdb33be9 | ||
|
|
aba8bbb6e3 | ||
|
|
caf4d571e6 |
No files matched your search
@@ -41,8 +41,25 @@ version 2.80
|
||||
Fix missing fatal errors with some malformed options
|
||||
(server, local, address, rebind-domain-ok, ipset, alias).
|
||||
Thanks to Eugene Lozovoy for spotting the problem.
|
||||
|
||||
Fix crash on startup with a --synth-domain which has no prefix.
|
||||
Introduced in 2.79. Thanks to Andreas Engel for the bug report.
|
||||
|
||||
Fix missing EDNS0 section in some replies generated by local
|
||||
DNS configuration which confused systemd-resolvd. Thanks to
|
||||
Steve Dodd for characterising the problem.
|
||||
|
||||
Add --dhcp-name-match config option.
|
||||
|
||||
Add --caa-record config option.
|
||||
|
||||
Implement --address=/example.com/# as (more efficient) syntactic
|
||||
sugar for --address=/example.com/0.0.0.0 and
|
||||
--address=/example.com/::
|
||||
Returning null addresses is a useful technique for ad-blocking.
|
||||
Thanks to Peter Russell for the suggestion.
|
||||
|
||||
|
||||
|
||||
version 2.79
|
||||
Fix parsing of CNAME arguments, which are confused by extra spaces.
|
||||
Thanks to Diego Aguirre for spotting the bug.
|
||||
|
||||
@@ -53,6 +53,7 @@ top?=$(CURDIR)
|
||||
|
||||
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
|
||||
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
|
||||
ubus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_UBUS $(PKG_CONFIG) --copy -lubox -lubus`
|
||||
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
|
||||
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
|
||||
idn2_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LIBIDN2 $(PKG_CONFIG) --cflags libidn2`
|
||||
@@ -76,16 +77,16 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \
|
||||
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
|
||||
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
|
||||
domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \
|
||||
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o
|
||||
poll.o rrfilter.o edns0.o arp.o crypto.o dump.o ubus.o metrics.o
|
||||
|
||||
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
|
||||
dns-protocol.h radv-protocol.h ip6addr.h
|
||||
dns-protocol.h radv-protocol.h ip6addr.h metrics.h
|
||||
|
||||
all : $(BUILDDIR)
|
||||
@cd $(BUILDDIR) && $(MAKE) \
|
||||
top="$(top)" \
|
||||
build_cflags="$(version) $(dbus_cflags) $(idn2_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs)" \
|
||||
build_libs="$(dbus_libs) $(idn2_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs) $(gmp_libs) $(ubus_libs)" \
|
||||
-f $(top)/Makefile dnsmasq
|
||||
|
||||
mostly_clean :
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
|
||||
radv.c slaac.c auth.c ipset.c domain.c \
|
||||
dnssec.c dnssec-openssl.c blockdata.c tables.c \
|
||||
loop.c inotify.c poll.c rrfilter.c edns0.c arp.c \
|
||||
crypto.c dump.c
|
||||
crypto.c dump.c ubus.c
|
||||
|
||||
LOCAL_MODULE := dnsmasq
|
||||
|
||||
|
||||
@@ -243,6 +243,10 @@ IPv4 or IPv6 address of the lease to remove.
|
||||
Note that this function will trigger the DhcpLeaseRemoved signal and the
|
||||
configured DHCP lease script will be run with the "del" action.
|
||||
|
||||
GetMetrics
|
||||
----------
|
||||
|
||||
Returns an array with various metrics for DNS and DHCP.
|
||||
|
||||
|
||||
2. SIGNALS
|
||||
|
||||
+19
-3
@@ -366,6 +366,11 @@ been built with DBus support. If the service name is given, dnsmasq
|
||||
provides service at that name, rather than the default which is
|
||||
.B uk.org.thekelleys.dnsmasq
|
||||
.TP
|
||||
.B --enable-ubus
|
||||
Enable dnsmasq UBus interface. It sends notifications via UBus on
|
||||
DHCPACK and DHCPRELEASE events. Furthermore it offers metrics.
|
||||
Requires that dnsmasq has been built with UBus support.
|
||||
.TP
|
||||
.B \-o, --strict-order
|
||||
By default, dnsmasq will send queries to any of the upstream servers
|
||||
it knows about and tries to favour servers that are known to
|
||||
@@ -505,7 +510,12 @@ upstream nameserver by a more specific \fB--server\fP directive. As for
|
||||
\fB--server\fP, one or more domains with no address returns a
|
||||
no-such-domain answer, so \fB--address=/example.com/\fP is equivalent to
|
||||
\fB--server=/example.com/\fP and returns NXDOMAIN for example.com and
|
||||
all its subdomains.
|
||||
all its subdomains. An address specified as '#' translates to the NULL
|
||||
address of 0.0.0.0 and its IPv6 equivalent of :: so
|
||||
\fB--address=/example.com/#\fP will return NULL addresses for example.com and
|
||||
its subdomains. This is partly syntactic sugar for \fB--address=/example.com/0.0.0.0\fP
|
||||
and \fB--address=/example.com/::\fP but is also more efficient than including both
|
||||
as seperate configuration lines.
|
||||
.TP
|
||||
.B --ipset=/<domain>[/<domain>...]/<ipset>[,<ipset>...]
|
||||
Places the resolved IP addresses of queries for one or more domains in
|
||||
@@ -588,6 +598,9 @@ Return a PTR DNS record.
|
||||
.B --naptr-record=<name>,<order>,<preference>,<flags>,<service>,<regexp>[,<replacement>]
|
||||
Return an NAPTR DNS record, as specified in RFC3403.
|
||||
.TP
|
||||
.B --caa-record=<name>,<flags>,<tag>,<value>
|
||||
Return a CAA DNS record, as specified in RFC6844.
|
||||
.TP
|
||||
.B --cname=<cname>,[<cname>,]<target>[,<TTL>]
|
||||
Return a CNAME record which indicates that <cname> is really
|
||||
<target>. There are significant limitations on the target; it must be a
|
||||
@@ -1324,6 +1337,9 @@ The special form with vi-encap:<enterprise number> matches against
|
||||
vendor-identifying vendor classes for the specified enterprise. Please
|
||||
see RFC 3925 for more details of these rare and interesting beasts.
|
||||
.TP
|
||||
.B --dhcp-name-match=set:<tag>,<name>[*]
|
||||
Set the tag if the given name is supplied by a dhcp client. There may be a single trailing wildcard *, which has the usual meaning. Combined with dhcp-ignore or dhcp-ignore-names this gives the ability to ignore certain clients by name, or disallow certain hostnames from being claimed by a client.
|
||||
.TP
|
||||
.B --tag-if=set:<tag>[,set:<tag>[,tag:<tag>[,tag:<tag>]]]
|
||||
Perform boolean operations on tags. Any tag appearing as set:<tag> is set if
|
||||
all the tags which appear as tag:<tag> are set, (or unset when tag:!<tag> is used)
|
||||
@@ -2254,8 +2270,8 @@ secondary servers for reverse lookups.
|
||||
When dnsmasq is configured to act as an authoritative server, the
|
||||
following data is used to populate the authoritative zone.
|
||||
.PP
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record
|
||||
, as long as the record names are in the authoritative domain.
|
||||
.B --mx-host, --srv-host, --dns-rr, --txt-record, --naptr-record, --caa-record,
|
||||
as long as the record names are in the authoritative domain.
|
||||
.PP
|
||||
.B --cname
|
||||
as long as the record name is in the authoritative domain. If the
|
||||
|
||||
+16
-14
@@ -103,7 +103,7 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
{
|
||||
char *name = daemon->namebuff;
|
||||
unsigned char *p, *ansp;
|
||||
int qtype, qclass;
|
||||
int qtype, qclass, rc;
|
||||
int nameoffset, axfroffset = 0;
|
||||
int q, anscount = 0, authcount = 0;
|
||||
struct crec *crecp;
|
||||
@@ -283,11 +283,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
for (rec = daemon->mxnames; rec; rec = rec->next)
|
||||
if (!rec->issrv && hostname_isequal(name, rec->name))
|
||||
if (!rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
if (qtype == T_MX)
|
||||
if (rc == 2 && qtype == T_MX)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<MX>");
|
||||
@@ -298,11 +298,11 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
for (move = NULL, up = &daemon->mxnames, rec = daemon->mxnames; rec; rec = rec->next)
|
||||
if (rec->issrv && hostname_isequal(name, rec->name))
|
||||
if (rec->issrv && (rc = hostname_issubdomain(name, rec->name)))
|
||||
{
|
||||
nxdomain = 0;
|
||||
|
||||
if (qtype == T_SRV)
|
||||
if (rc == 2 && qtype == T_SRV)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<SRV>");
|
||||
@@ -333,13 +333,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
for (txt = daemon->rr; txt; txt = txt->next)
|
||||
if (hostname_isequal(name, txt->name))
|
||||
if ((rc = hostname_issubdomain(name, txt->name)))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (txt->class == qtype)
|
||||
if (rc == 2 && txt->class == qtype)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, txt->class));
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, daemon->auth_ttl,
|
||||
NULL, txt->class, C_IN, "t", txt->len, txt->txt))
|
||||
anscount++;
|
||||
@@ -347,10 +347,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
for (txt = daemon->txt; txt; txt = txt->next)
|
||||
if (txt->class == C_IN && hostname_isequal(name, txt->name))
|
||||
if (txt->class == C_IN && (rc = hostname_issubdomain(name, txt->name)))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (qtype == T_TXT)
|
||||
if (rc == 2 && qtype == T_TXT)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<TXT>");
|
||||
@@ -361,10 +361,10 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
}
|
||||
|
||||
for (na = daemon->naptr; na; na = na->next)
|
||||
if (hostname_isequal(name, na->name))
|
||||
if ((rc = hostname_issubdomain(name, na->name)))
|
||||
{
|
||||
nxdomain = 0;
|
||||
if (qtype == T_NAPTR)
|
||||
if (rc == 2 && qtype == T_NAPTR)
|
||||
{
|
||||
found = 1;
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<NAPTR>");
|
||||
@@ -384,13 +384,13 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
#endif
|
||||
|
||||
for (intr = daemon->int_names; intr; intr = intr->next)
|
||||
if (hostname_isequal(name, intr->name))
|
||||
if ((rc = hostname_issubdomain(name, intr->name)))
|
||||
{
|
||||
struct addrlist *addrlist;
|
||||
|
||||
nxdomain = 0;
|
||||
|
||||
if (flag)
|
||||
if (rc == 2 && flag)
|
||||
for (addrlist = intr->addr; addrlist; addrlist = addrlist->next)
|
||||
if (((addrlist->flags & ADDRLIST_IPV6) ? T_AAAA : T_A) == qtype &&
|
||||
(local_query || filter_zone(zone, flag, &addrlist->addr)))
|
||||
@@ -567,6 +567,8 @@ size_t answer_auth(struct dns_header *header, char *limit, size_t qlen, time_t n
|
||||
|
||||
goto cname_restart;
|
||||
}
|
||||
else if (cache_find_non_terminal(name, now))
|
||||
nxdomain = 0;
|
||||
|
||||
log_query(flag | F_NEG | (nxdomain ? F_NXDOMAIN : 0) | F_FORWARD | F_AUTH, name, NULL, NULL);
|
||||
}
|
||||
|
||||
+151
-21
@@ -21,10 +21,12 @@ static struct crec *cache_head = NULL, *cache_tail = NULL, **hash_table = NULL;
|
||||
static struct crec *dhcp_spare = NULL;
|
||||
#endif
|
||||
static struct crec *new_chain = NULL;
|
||||
static int cache_inserted = 0, cache_live_freed = 0, insert_error;
|
||||
static int insert_error;
|
||||
static union bigname *big_free = NULL;
|
||||
static int bignames_left, hash_size;
|
||||
|
||||
static void make_non_terminals(struct crec *source);
|
||||
|
||||
/* type->string mapping: this is also used by the name-hash function as a mixing table. */
|
||||
static const struct {
|
||||
unsigned int type;
|
||||
@@ -68,7 +70,8 @@ static const struct {
|
||||
{ 252, "AXFR" },
|
||||
{ 253, "MAILB" },
|
||||
{ 254, "MAILA" },
|
||||
{ 255, "ANY" }
|
||||
{ 255, "ANY" },
|
||||
{ 257, "CAA" }
|
||||
};
|
||||
|
||||
static void cache_free(struct crec *crecp);
|
||||
@@ -556,7 +559,7 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
|
||||
|
||||
free_avail = 1; /* Must be free space now. */
|
||||
cache_scan_free(cache_get_name(new), &free_addr, now, new->flags, NULL, NULL);
|
||||
cache_live_freed++;
|
||||
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -641,13 +644,27 @@ void cache_end_insert(void)
|
||||
{
|
||||
cache_hash(new_chain);
|
||||
cache_link(new_chain);
|
||||
cache_inserted++;
|
||||
daemon->metrics[METRIC_DNS_CACHE_INSERTED]++;
|
||||
}
|
||||
new_chain = tmp;
|
||||
}
|
||||
new_chain = NULL;
|
||||
}
|
||||
|
||||
int cache_find_non_terminal(char *name, time_t now)
|
||||
{
|
||||
struct crec *crecp;
|
||||
|
||||
for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
|
||||
if (!is_outdated_cname_pointer(crecp) &&
|
||||
!is_expired(now, crecp) &&
|
||||
(crecp->flags & F_FORWARD) &&
|
||||
hostname_isequal(name, cache_get_name(crecp)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct crec *cache_find_by_name(struct crec *crecp, char *name, time_t now, unsigned int prot)
|
||||
{
|
||||
struct crec *ans;
|
||||
@@ -818,6 +835,8 @@ static void add_hosts_cname(struct crec *target)
|
||||
crec->addr.cname.uid = target->uid;
|
||||
crec->uid = UID_NONE;
|
||||
cache_hash(crec);
|
||||
make_non_terminals(crec);
|
||||
|
||||
add_hosts_cname(crec); /* handle chains */
|
||||
}
|
||||
}
|
||||
@@ -889,6 +908,7 @@ static void add_hosts_entry(struct crec *cache, struct all_addr *addr, int addrl
|
||||
cache->uid = index;
|
||||
memcpy(&cache->addr.addr, addr, addrlen);
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
|
||||
/* don't need to do alias stuff for second and subsequent addresses. */
|
||||
if (!nameexists)
|
||||
@@ -1059,7 +1079,8 @@ void cache_reload(void)
|
||||
struct ds_config *ds;
|
||||
#endif
|
||||
|
||||
cache_inserted = cache_live_freed = 0;
|
||||
daemon->metrics[METRIC_DNS_CACHE_INSERTED] = 0;
|
||||
daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED] = 0;
|
||||
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i], up = &hash_table[i]; cache; cache = tmp)
|
||||
@@ -1101,6 +1122,7 @@ void cache_reload(void)
|
||||
cache->addr.cname.uid = SRC_INTERFACE;
|
||||
cache->uid = UID_NONE;
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
add_hosts_cname(cache); /* handle chains */
|
||||
}
|
||||
|
||||
@@ -1118,6 +1140,7 @@ void cache_reload(void)
|
||||
cache->addr.ds.digest = ds->digest_type;
|
||||
cache->uid = ds->class;
|
||||
cache_hash(cache);
|
||||
make_non_terminals(cache);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1233,6 +1256,7 @@ static void add_dhcp_cname(struct crec *target, time_t ttd)
|
||||
aliasc->addr.cname.uid = target->uid;
|
||||
aliasc->uid = UID_NONE;
|
||||
cache_hash(aliasc);
|
||||
make_non_terminals(aliasc);
|
||||
add_dhcp_cname(aliasc, ttd);
|
||||
}
|
||||
}
|
||||
@@ -1321,12 +1345,104 @@ void cache_add_dhcp_entry(char *host_name, int prot,
|
||||
crec->name.namep = host_name;
|
||||
crec->uid = UID_NONE;
|
||||
cache_hash(crec);
|
||||
make_non_terminals(crec);
|
||||
|
||||
add_dhcp_cname(crec, ttd);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Called when we put a local or DHCP name into the cache.
|
||||
Creates empty cache entries for subnames (ie,
|
||||
for three.two.one, for two.one and one), without
|
||||
F_IPV4 or F_IPV6 or F_CNAME set. These convert
|
||||
NXDOMAIN answers to NoData ones. */
|
||||
static void make_non_terminals(struct crec *source)
|
||||
{
|
||||
char *name = cache_get_name(source);
|
||||
struct crec* crecp, *tmp, **up;
|
||||
int type = F_HOSTS | F_CONFIG;
|
||||
#ifdef HAVE_DHCP
|
||||
if (source->flags & F_DHCP)
|
||||
type = F_DHCP;
|
||||
#endif
|
||||
|
||||
/* First delete any empty entries for our new real name. Note that
|
||||
we only delete empty entries deriving from DHCP for a new DHCP-derived
|
||||
entry and vice-versa for HOSTS and CONFIG. This ensures that
|
||||
non-terminals from DHCP go when we reload DHCP and
|
||||
for HOSTS/CONFIG when we re-read. */
|
||||
for (up = hash_bucket(name), crecp = *up; crecp; crecp = tmp)
|
||||
{
|
||||
tmp = crecp->hash_next;
|
||||
|
||||
if (!is_outdated_cname_pointer(crecp) &&
|
||||
(crecp->flags & F_FORWARD) &&
|
||||
(crecp->flags & type) &&
|
||||
!(crecp->flags & (F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS)) &&
|
||||
hostname_isequal(name, cache_get_name(crecp)))
|
||||
{
|
||||
*up = crecp->hash_next;
|
||||
#ifdef HAVE_DHCP
|
||||
if (type & F_DHCP)
|
||||
{
|
||||
crecp->next = dhcp_spare;
|
||||
dhcp_spare = crecp;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
free(crecp);
|
||||
break;
|
||||
}
|
||||
else
|
||||
up = &crecp->hash_next;
|
||||
}
|
||||
|
||||
while ((name = strchr(name, '.')))
|
||||
{
|
||||
name++;
|
||||
|
||||
/* Look for one existing, don't need another */
|
||||
for (crecp = *hash_bucket(name); crecp; crecp = crecp->hash_next)
|
||||
if (!is_outdated_cname_pointer(crecp) &&
|
||||
(crecp->flags & F_FORWARD) &&
|
||||
(crecp->flags & type) &&
|
||||
hostname_isequal(name, cache_get_name(crecp)))
|
||||
break;
|
||||
|
||||
if (crecp)
|
||||
{
|
||||
/* If the new name expires later, transfer that time to
|
||||
empty non-terminal entry. */
|
||||
if (!(crecp->flags & F_IMMORTAL))
|
||||
{
|
||||
if (source->flags & F_IMMORTAL)
|
||||
crecp->flags |= F_IMMORTAL;
|
||||
else if (difftime(crecp->ttd, source->ttd) < 0)
|
||||
crecp->ttd = source->ttd;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if ((source->flags & F_DHCP) && dhcp_spare)
|
||||
{
|
||||
crecp = dhcp_spare;
|
||||
dhcp_spare = dhcp_spare->next;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
crecp = whine_malloc(sizeof(struct crec));
|
||||
|
||||
*crecp = *source;
|
||||
crecp->flags &= ~(F_IPV4 | F_IPV6 | F_CNAME | F_DNSKEY | F_DS | F_REVERSE);
|
||||
crecp->flags |= F_NAMEP;
|
||||
crecp->name.namep = name;
|
||||
|
||||
cache_hash(crecp);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NO_ID
|
||||
int cache_make_stat(struct txt_record *t)
|
||||
{
|
||||
@@ -1348,24 +1464,24 @@ int cache_make_stat(struct txt_record *t)
|
||||
break;
|
||||
|
||||
case TXT_STAT_INSERTS:
|
||||
sprintf(buff+1, "%d", cache_inserted);
|
||||
sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
|
||||
break;
|
||||
|
||||
case TXT_STAT_EVICTIONS:
|
||||
sprintf(buff+1, "%d", cache_live_freed);
|
||||
sprintf(buff+1, "%d", daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED]);
|
||||
break;
|
||||
|
||||
case TXT_STAT_MISSES:
|
||||
sprintf(buff+1, "%u", daemon->queries_forwarded);
|
||||
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]);
|
||||
break;
|
||||
|
||||
case TXT_STAT_HITS:
|
||||
sprintf(buff+1, "%u", daemon->local_answer);
|
||||
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
|
||||
break;
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
case TXT_STAT_AUTH:
|
||||
sprintf(buff+1, "%u", daemon->auth_answer);
|
||||
sprintf(buff+1, "%u", daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -1442,15 +1558,14 @@ static char *sanitise(char *name)
|
||||
void dump_cache(time_t now)
|
||||
{
|
||||
struct server *serv, *serv1;
|
||||
char *t = "";
|
||||
|
||||
my_syslog(LOG_INFO, _("time %lu"), (unsigned long)now);
|
||||
my_syslog(LOG_INFO, _("cache size %d, %d/%d cache insertions re-used unexpired cache entries."),
|
||||
daemon->cachesize, cache_live_freed, cache_inserted);
|
||||
daemon->cachesize, daemon->metrics[METRIC_DNS_CACHE_LIVE_FREED], daemon->metrics[METRIC_DNS_CACHE_INSERTED]);
|
||||
my_syslog(LOG_INFO, _("queries forwarded %u, queries answered locally %u"),
|
||||
daemon->queries_forwarded, daemon->local_answer);
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED], daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]);
|
||||
#ifdef HAVE_AUTH
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->auth_answer);
|
||||
my_syslog(LOG_INFO, _("queries for authoritative zones %u"), daemon->metrics[METRIC_DNS_AUTH_ANSWERED]);
|
||||
#endif
|
||||
#ifdef HAVE_DNSSEC
|
||||
blockdata_report();
|
||||
@@ -1488,6 +1603,7 @@ void dump_cache(time_t now)
|
||||
for (i=0; i<hash_size; i++)
|
||||
for (cache = hash_table[i]; cache; cache = cache->hash_next)
|
||||
{
|
||||
char *t = " ";
|
||||
char *a = daemon->addrbuff, *p = daemon->namebuff, *n = cache_get_name(cache);
|
||||
*a = 0;
|
||||
if (strlen(n) == 0 && !(cache->flags & F_REVERSE))
|
||||
@@ -1588,9 +1704,13 @@ char *querystr(char *desc, unsigned short type)
|
||||
break;
|
||||
}
|
||||
|
||||
len += 3; /* braces, terminator */
|
||||
len += strlen(desc);
|
||||
|
||||
if (desc)
|
||||
{
|
||||
len += 2; /* braces */
|
||||
len += strlen(desc);
|
||||
}
|
||||
len++; /* terminator */
|
||||
|
||||
if (!buff || bufflen < len)
|
||||
{
|
||||
if (buff)
|
||||
@@ -1604,12 +1724,22 @@ char *querystr(char *desc, unsigned short type)
|
||||
|
||||
if (buff)
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "%s[%s]", desc, types);
|
||||
if (desc)
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "%s[%s]", desc, types);
|
||||
else
|
||||
sprintf(buff, "%s[type=%d]", desc, type);
|
||||
}
|
||||
else
|
||||
sprintf(buff, "%s[type=%d]", desc, type);
|
||||
{
|
||||
if (types)
|
||||
sprintf(buff, "<%s>", types);
|
||||
else
|
||||
sprintf(buff, "type=%d", type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return buff ? buff : "";
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ HAVE_DBUS
|
||||
support some methods to allow (re)configuration of the upstream DNS
|
||||
servers via DBus.
|
||||
|
||||
HAVE_UBUS
|
||||
define this if you want to link against libubus
|
||||
|
||||
HAVE_IDN
|
||||
define this if you want international domain name 2003 support.
|
||||
|
||||
|
||||
+31
@@ -85,6 +85,9 @@ const char* introspection_xml_template =
|
||||
" <arg name=\"success\" type=\"b\" direction=\"out\"/>\n"
|
||||
" </method>\n"
|
||||
#endif
|
||||
" <method name=\"GetMetrics\">\n"
|
||||
" <arg name=\"metrics\" direction=\"out\" type=\"a{su}\"/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
"</node>\n";
|
||||
|
||||
@@ -613,6 +616,30 @@ static DBusMessage *dbus_del_lease(DBusMessage* message)
|
||||
}
|
||||
#endif
|
||||
|
||||
static DBusMessage *dbus_get_metrics(DBusMessage* message)
|
||||
{
|
||||
DBusMessage *reply = dbus_message_new_method_return(message);
|
||||
DBusMessageIter array, dict, iter;
|
||||
int i;
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{su}", &array);
|
||||
|
||||
for (i = 0; i < __METRIC_MAX; i++) {
|
||||
const char *key = get_metric_name(i);
|
||||
dbus_uint32_t value = daemon->metrics[i];
|
||||
|
||||
dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &key);
|
||||
dbus_message_iter_append_basic(&dict, DBUS_TYPE_UINT32, &value);
|
||||
dbus_message_iter_close_container(&array, &dict);
|
||||
}
|
||||
|
||||
dbus_message_iter_close_container(&iter, &array);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
DBusMessage *message,
|
||||
void *user_data)
|
||||
@@ -680,6 +707,10 @@ DBusHandlerResult message_handler(DBusConnection *connection,
|
||||
reply = dbus_del_lease(message);
|
||||
}
|
||||
#endif
|
||||
else if (strcmp(method, "GetMetrics") == 0)
|
||||
{
|
||||
reply = dbus_get_metrics(message);
|
||||
}
|
||||
else if (strcmp(method, "ClearCache") == 0)
|
||||
clear_cache = 1;
|
||||
else
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
#define T_AXFR 252
|
||||
#define T_MAILB 253
|
||||
#define T_ANY 255
|
||||
#define T_CAA 257
|
||||
|
||||
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
|
||||
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
|
||||
|
||||
+20
-5
@@ -216,7 +216,7 @@ int main (int argc, char **argv)
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_AUTH
|
||||
if (daemon->authserver)
|
||||
if (daemon->authserver || daemon->auth_zones)
|
||||
die(_("authoritative DNS not available: set HAVE_AUTH in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
@@ -225,13 +225,18 @@ int main (int argc, char **argv)
|
||||
die(_("loop detection not available: set HAVE_LOOP in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_UBUS
|
||||
if (option_bool(OPT_UBUS))
|
||||
die(_("Ubus not available: set HAVE_UBUS in src/config.h"), NULL, EC_BADCONF);
|
||||
#endif
|
||||
|
||||
if (daemon->max_port < daemon->min_port)
|
||||
die(_("max_port cannot be smaller than min_port"), NULL, EC_BADCONF);
|
||||
|
||||
now = dnsmasq_time();
|
||||
|
||||
/* Create a serial at startup if not configured. */
|
||||
if (daemon->authinterface && daemon->soa_sn == 0)
|
||||
if (daemon->auth_zones && daemon->soa_sn == 0)
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
die(_("zone serial must be configured in --auth-soa"), NULL, EC_BADCONF);
|
||||
#else
|
||||
@@ -947,8 +952,13 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef HAVE_DBUS
|
||||
set_dbus_listeners();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (option_bool(OPT_UBUS))
|
||||
set_ubus_listeners();
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DHCP
|
||||
if (daemon->dhcp || daemon->relay4)
|
||||
{
|
||||
@@ -1078,7 +1088,12 @@ int main (int argc, char **argv)
|
||||
}
|
||||
check_dbus_listeners();
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (option_bool(OPT_UBUS))
|
||||
check_ubus_listeners();
|
||||
#endif
|
||||
|
||||
check_dns_listeners(now);
|
||||
|
||||
#ifdef HAVE_TFTP
|
||||
|
||||
+21
-2
@@ -57,6 +57,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "ip6addr.h"
|
||||
#include "metrics.h"
|
||||
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
@@ -254,7 +255,8 @@ struct event_desc {
|
||||
#define OPT_MAC_HEX 55
|
||||
#define OPT_TFTP_APREF_MAC 56
|
||||
#define OPT_RAPID_COMMIT 57
|
||||
#define OPT_LAST 58
|
||||
#define OPT_UBUS 58
|
||||
#define OPT_LAST 59
|
||||
|
||||
/* extra flags for my_syslog, we use a couple of facilities since they are known
|
||||
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
|
||||
@@ -820,6 +822,13 @@ struct dhcp_boot {
|
||||
struct dhcp_boot *next;
|
||||
};
|
||||
|
||||
struct dhcp_match_name {
|
||||
char *name;
|
||||
int wildcard;
|
||||
struct dhcp_netid *netid;
|
||||
struct dhcp_match_name *next;
|
||||
};
|
||||
|
||||
struct pxe_service {
|
||||
unsigned short CSA, type;
|
||||
char *menu, *basename, *sname;
|
||||
@@ -1012,6 +1021,7 @@ extern struct daemon {
|
||||
struct ra_interface *ra_interfaces;
|
||||
struct dhcp_config *dhcp_conf;
|
||||
struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
|
||||
struct dhcp_match_name *dhcp_name_match;
|
||||
struct dhcp_vendor *dhcp_vendors;
|
||||
struct dhcp_mac *dhcp_macs;
|
||||
struct dhcp_boot *boot_config;
|
||||
@@ -1040,6 +1050,7 @@ extern struct daemon {
|
||||
char *dump_file;
|
||||
int dump_mask;
|
||||
unsigned long soa_sn, soa_refresh, soa_retry, soa_expiry;
|
||||
u32 metrics[__METRIC_MAX];
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
struct prefix_class *prefix_classes;
|
||||
#endif
|
||||
@@ -1060,7 +1071,6 @@ extern struct daemon {
|
||||
int dnssec_no_time_check;
|
||||
int back_to_the_future;
|
||||
#endif
|
||||
unsigned int local_answer, queries_forwarded, auth_answer;
|
||||
struct frec *frec_list;
|
||||
struct serverfd *sfds;
|
||||
struct irec *interfaces;
|
||||
@@ -1125,6 +1135,7 @@ void next_uid(struct crec *crecp);
|
||||
void log_query(unsigned int flags, char *name, struct all_addr *addr, char *arg);
|
||||
char *record_source(unsigned int index);
|
||||
char *querystr(char *desc, unsigned short type);
|
||||
int cache_find_non_terminal(char *name, time_t now);
|
||||
struct crec *cache_find_by_addr(struct crec *crecp,
|
||||
struct all_addr *addr, time_t now,
|
||||
unsigned int prot);
|
||||
@@ -1240,6 +1251,7 @@ void *whine_malloc(size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(union mysockaddr *s1, union mysockaddr *s2);
|
||||
int hostname_isequal(const char *a, const char *b);
|
||||
int hostname_issubdomain(char *a, char *b);
|
||||
time_t dnsmasq_time(void);
|
||||
int netmask_length(struct in_addr mask);
|
||||
int is_same_net(struct in_addr a, struct in_addr b, struct in_addr mask);
|
||||
@@ -1445,6 +1457,13 @@ void emit_dbus_signal(int action, struct dhcp_lease *lease, char *hostname);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* ubus.c */
|
||||
#ifdef HAVE_UBUS
|
||||
void set_ubus_listeners(void);
|
||||
void check_ubus_listeners(void);
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface);
|
||||
#endif
|
||||
|
||||
/* ipset.c */
|
||||
#ifdef HAVE_IPSET
|
||||
void ipset_init(void);
|
||||
|
||||
+32
-12
@@ -118,6 +118,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
|
||||
unsigned int matchlen = 0;
|
||||
struct server *serv;
|
||||
unsigned int flags = 0;
|
||||
static struct all_addr zero;
|
||||
|
||||
for (serv = daemon->servers; serv; serv=serv->next)
|
||||
if (qtype == F_DNSSECOK && !(serv->flags & SERV_DO_DNSSEC))
|
||||
@@ -129,9 +130,16 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
|
||||
*type = SERV_FOR_NODOTS;
|
||||
if (serv->flags & SERV_NO_ADDR)
|
||||
flags = F_NXDOMAIN;
|
||||
else if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
else if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (sflag & qtype)
|
||||
/* literal address = '#' -> return all-zero address for IPv4 and IPv6 */
|
||||
if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4)))
|
||||
{
|
||||
memset(&zero, 0, sizeof(zero));
|
||||
flags = qtype;
|
||||
*addrpp = &zero;
|
||||
}
|
||||
else if (sflag & qtype)
|
||||
{
|
||||
flags = sflag;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
@@ -184,7 +192,14 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp, unsigne
|
||||
flags = F_NXDOMAIN;
|
||||
else if (serv->flags & SERV_LITERAL_ADDRESS)
|
||||
{
|
||||
if (sflag & qtype)
|
||||
/* literal address = '#' -> return all-zero address for IPv4 and IPv6 */
|
||||
if ((serv->flags & SERV_USE_RESOLV) && (qtype & (F_IPV6 | F_IPV4)))
|
||||
{
|
||||
memset(&zero, 0, sizeof(zero));
|
||||
flags = qtype;
|
||||
*addrpp = &zero;
|
||||
}
|
||||
else if (sflag & qtype)
|
||||
{
|
||||
flags = sflag;
|
||||
if (serv->addr.sa.sa_family == AF_INET)
|
||||
@@ -246,9 +261,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
unsigned int crc = questions_crc(header, plen, daemon->namebuff);
|
||||
void *hash = &crc;
|
||||
#endif
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
|
||||
(void)do_bit;
|
||||
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
|
||||
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
|
||||
(void)do_bit;
|
||||
|
||||
/* may be no servers available. */
|
||||
if (forward || (hash && (forward = lookup_frec_by_sender(ntohs(header->id), udpaddr, hash))))
|
||||
@@ -399,7 +414,6 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
struct server *firstsentto = start;
|
||||
int subnet, forwarded = 0;
|
||||
size_t edns0_len;
|
||||
unsigned char *oph = find_pseudoheader(header, plen, NULL, NULL, NULL, NULL);
|
||||
unsigned char *pheader;
|
||||
|
||||
/* If a query is retried, use the log_id for the retry when logging the answer. */
|
||||
@@ -554,6 +568,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
|
||||
if (udpfd != -1)
|
||||
{
|
||||
plen = setup_reply(header, plen, addrp, flags, daemon->local_ttl);
|
||||
if (oph)
|
||||
plen = add_pseudoheader(header, plen, ((unsigned char *) header) + PACKETSZ, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
send_from(udpfd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, plen, udpaddr, dst_addr, dst_iface);
|
||||
}
|
||||
|
||||
@@ -1553,7 +1569,7 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||
daemon->auth_answer++;
|
||||
daemon->metrics[METRIC_DNS_AUTH_ANSWERED]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1571,13 +1587,13 @@ void receive_query(struct listener *listen, time_t now)
|
||||
{
|
||||
send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND),
|
||||
(char *)header, m, &source_addr, &dst_addr, if_index);
|
||||
daemon->local_answer++;
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
}
|
||||
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
|
||||
header, (size_t)n, now, NULL, ad_reqd, do_bit))
|
||||
daemon->queries_forwarded++;
|
||||
daemon->metrics[METRIC_DNS_QUERIES_FORWARDED]++;
|
||||
else
|
||||
daemon->local_answer++;
|
||||
daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2104,7 +2120,11 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
|
||||
{
|
||||
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
|
||||
if (have_pseudoheader)
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -555,7 +555,9 @@ void lease_prune(struct dhcp_lease *target, time_t now)
|
||||
file_dirty = 1;
|
||||
if (lease->hostname)
|
||||
dns_dirty = 1;
|
||||
|
||||
|
||||
daemon->metrics[lease->addr.s_addr ? METRIC_LEASES_PRUNED_4 : METRIC_LEASES_PRUNED_6]++;
|
||||
|
||||
*up = lease->next; /* unlink */
|
||||
|
||||
/* Put on old_leases list 'till we
|
||||
@@ -773,7 +775,10 @@ struct dhcp_lease *lease4_allocate(struct in_addr addr)
|
||||
{
|
||||
struct dhcp_lease *lease = lease_allocate();
|
||||
if (lease)
|
||||
lease->addr = addr;
|
||||
{
|
||||
lease->addr = addr;
|
||||
daemon->metrics[METRIC_LEASES_ALLOCATED_4]++;
|
||||
}
|
||||
|
||||
return lease;
|
||||
}
|
||||
@@ -788,6 +793,8 @@ struct dhcp_lease *lease6_allocate(struct in6_addr *addrp, int lease_type)
|
||||
lease->addr6 = *addrp;
|
||||
lease->flags |= lease_type;
|
||||
lease->iaid = 0;
|
||||
|
||||
daemon->metrics[METRIC_LEASES_ALLOCATED_6]++;
|
||||
}
|
||||
|
||||
return lease;
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
const char * metric_names[] = {
|
||||
"dns_cache_inserted",
|
||||
"dns_cache_live_freed",
|
||||
"dns_queries_forwarded",
|
||||
"dns_auth_answered",
|
||||
"dns_local_answered",
|
||||
"bootp",
|
||||
"pxe",
|
||||
"dhcp_ack",
|
||||
"dhcp_decline",
|
||||
"dhcp_discover",
|
||||
"dhcp_inform",
|
||||
"dhcp_nak",
|
||||
"dhcp_offer",
|
||||
"dhcp_release",
|
||||
"dhcp_request",
|
||||
"noanswer",
|
||||
"leases_allocated_4",
|
||||
"leases_pruned_4",
|
||||
"leases_allocated_6",
|
||||
"leases_pruned_6",
|
||||
};
|
||||
|
||||
const char* get_metric_name(int i) {
|
||||
return metric_names[i];
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* If you modify this list, please keep the labels in metrics.c in sync. */
|
||||
enum {
|
||||
METRIC_DNS_CACHE_INSERTED,
|
||||
METRIC_DNS_CACHE_LIVE_FREED,
|
||||
METRIC_DNS_QUERIES_FORWARDED,
|
||||
METRIC_DNS_AUTH_ANSWERED,
|
||||
METRIC_DNS_LOCAL_ANSWERED,
|
||||
METRIC_BOOTP,
|
||||
METRIC_PXE,
|
||||
METRIC_DHCPACK,
|
||||
METRIC_DHCPDECLINE,
|
||||
METRIC_DHCPDISCOVER,
|
||||
METRIC_DHCPINFORM,
|
||||
METRIC_DHCPNAK,
|
||||
METRIC_DHCPOFFER,
|
||||
METRIC_DHCPRELEASE,
|
||||
METRIC_DHCPREQUEST,
|
||||
METRIC_NOANSWER,
|
||||
METRIC_LEASES_ALLOCATED_4,
|
||||
METRIC_LEASES_PRUNED_4,
|
||||
METRIC_LEASES_ALLOCATED_6,
|
||||
METRIC_LEASES_PRUNED_6,
|
||||
|
||||
__METRIC_MAX,
|
||||
};
|
||||
|
||||
const char* get_metric_name(int);
|
||||
+75
-14
@@ -163,6 +163,9 @@ struct myoption {
|
||||
#define LOPT_RAPID_COMMIT 351
|
||||
#define LOPT_DUMPFILE 352
|
||||
#define LOPT_DUMPMASK 353
|
||||
#define LOPT_UBUS 354
|
||||
#define LOPT_NAME_MATCH 355
|
||||
#define LOPT_CAA 356
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -231,8 +234,10 @@ static const struct myoption opts[] =
|
||||
{ "srv-host", 1, 0, 'W' },
|
||||
{ "localise-queries", 0, 0, 'y' },
|
||||
{ "txt-record", 1, 0, 'Y' },
|
||||
{ "caa-record", 1, 0 , LOPT_CAA },
|
||||
{ "dns-rr", 1, 0, LOPT_RR },
|
||||
{ "enable-dbus", 2, 0, '1' },
|
||||
{ "enable-ubus", 0, 0, LOPT_UBUS },
|
||||
{ "bootp-dynamic", 2, 0, '3' },
|
||||
{ "dhcp-mac", 1, 0, '4' },
|
||||
{ "no-ping", 0, 0, '5' },
|
||||
@@ -271,7 +276,8 @@ static const struct myoption opts[] =
|
||||
{ "stop-dns-rebind", 0, 0, LOPT_REBIND },
|
||||
{ "rebind-domain-ok", 1, 0, LOPT_NO_REBIND },
|
||||
{ "all-servers", 0, 0, LOPT_NOLAST },
|
||||
{ "dhcp-match", 1, 0, LOPT_MATCH },
|
||||
{ "dhcp-match", 1, 0, LOPT_MATCH },
|
||||
{ "dhcp-name-match", 1, 0, LOPT_NAME_MATCH },
|
||||
{ "dhcp-broadcast", 2, 0, LOPT_BROADCAST },
|
||||
{ "neg-ttl", 1, 0, LOPT_NEGTTL },
|
||||
{ "max-ttl", 1, 0, LOPT_MAXTTL },
|
||||
@@ -420,6 +426,7 @@ static struct {
|
||||
{ 'z', OPT_NOWILD, NULL, gettext_noop("Bind only to interfaces in use."), NULL },
|
||||
{ 'Z', OPT_ETHERS, NULL, gettext_noop("Read DHCP static host information from %s."), ETHERSFILE },
|
||||
{ '1', ARG_ONE, "[=<busname>]", gettext_noop("Enable the DBus interface for setting upstream servers, etc."), NULL },
|
||||
{ LOPT_UBUS, OPT_UBUS, NULL, gettext_noop("Enable the UBus interface."), NULL },
|
||||
{ '2', ARG_DUP, "<interface>", gettext_noop("Do not provide DHCP on this interface, only provide DNS."), NULL },
|
||||
{ '3', ARG_DUP, "[=tag:<tag>]...", gettext_noop("Enable dynamic address allocation for bootp."), NULL },
|
||||
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
||||
@@ -453,6 +460,7 @@ static struct {
|
||||
{ LOPT_NO_REBIND, ARG_DUP, "/<domain>/", gettext_noop("Inhibit DNS-rebind protection on this domain."), NULL },
|
||||
{ LOPT_NOLAST, OPT_ALL_SERVERS, NULL, gettext_noop("Always perform DNS queries to all servers."), NULL },
|
||||
{ LOPT_MATCH, ARG_DUP, "set:<tag>,<optspec>", gettext_noop("Set tag if client includes matching option in request."), NULL },
|
||||
{ LOPT_NAME_MATCH, ARG_DUP, "set:<tag>,<string>[*]", gettext_noop("Set tag if client provides given name."), NULL },
|
||||
{ LOPT_ALTPORT, ARG_ONE, "[=<ports>]", gettext_noop("Use alternative ports for DHCP."), NULL },
|
||||
{ LOPT_NAPTR, ARG_DUP, "<name>,<naptr>", gettext_noop("Specify NAPTR DNS record."), NULL },
|
||||
{ LOPT_MINPORT, ARG_ONE, "<port>", gettext_noop("Specify lowest port available for DNS query transmission."), NULL },
|
||||
@@ -475,6 +483,7 @@ static struct {
|
||||
{ LOPT_RA, OPT_RA, NULL, gettext_noop("Send router-advertisements for interfaces doing DHCPv6"), NULL },
|
||||
{ LOPT_DUID, ARG_ONE, "<enterprise>,<duid>", gettext_noop("Specify DUID_EN-type DHCPv6 server DUID"), NULL },
|
||||
{ LOPT_HOST_REC, ARG_DUP, "<name>,<address>[,<ttl>]", gettext_noop("Specify host (A/AAAA and PTR) records"), NULL },
|
||||
{ LOPT_CAA, ARG_DUP, "<name>,<flags>,<tag>,<value>", gettext_noop("Specify certification authority authorization record"), NULL },
|
||||
{ LOPT_RR, ARG_DUP, "<name>,<RR-number>,[<data>]", gettext_noop("Specify arbitrary DNS resource record"), NULL },
|
||||
{ LOPT_CLVERBIND, OPT_CLEVERBIND, NULL, gettext_noop("Bind to interfaces in use - check for new interfaces"), NULL },
|
||||
{ LOPT_AUTHSERV, ARG_ONE, "<NS>,<interface>", gettext_noop("Export local names to global DNS"), NULL },
|
||||
@@ -1746,7 +1755,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case 'x': /* --pid-file */
|
||||
daemon->runfile = opt_string_alloc(arg);
|
||||
break;
|
||||
@@ -1892,8 +1901,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_AUTH
|
||||
case LOPT_AUTHSERV: /* --auth-server */
|
||||
if (!(comma = split(arg)))
|
||||
ret_err(gen_err);
|
||||
@@ -2068,7 +2075,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 's': /* --domain */
|
||||
case LOPT_SYNTH: /* --synth-domain */
|
||||
@@ -2229,7 +2235,9 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
char *star;
|
||||
new->next = daemon->synth_domains;
|
||||
daemon->synth_domains = new;
|
||||
if ((star = strrchr(new->prefix, '*')) && *(star+1) == 0)
|
||||
if (new->prefix &&
|
||||
(star = strrchr(new->prefix, '*'))
|
||||
&& *(star+1) == 0)
|
||||
{
|
||||
*star = 0;
|
||||
new->indexed = 1;
|
||||
@@ -2459,11 +2467,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
}
|
||||
|
||||
else if (strcmp(arg, "#") == 0)
|
||||
{
|
||||
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
|
||||
if (newlist->flags & SERV_LITERAL_ADDRESS)
|
||||
ret_err(gen_err);
|
||||
}
|
||||
newlist->flags |= SERV_USE_RESOLV; /* treat in ordinary way */
|
||||
else
|
||||
{
|
||||
char *err = parse_server(arg, &newlist->addr, &newlist->source_addr, newlist->interface, &newlist->flags);
|
||||
@@ -3284,7 +3288,33 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
|
||||
option == LOPT_FORCE ? DHOPT_FORCE :
|
||||
(option == LOPT_MATCH ? DHOPT_MATCH :
|
||||
(option == LOPT_OPTS ? DHOPT_BANK : 0)));
|
||||
|
||||
|
||||
case LOPT_NAME_MATCH: /* --dhcp-name-match */
|
||||
{
|
||||
struct dhcp_match_name *new = opt_malloc(sizeof(struct dhcp_match_name));
|
||||
struct dhcp_netid *id = opt_malloc(sizeof(struct dhcp_netid));
|
||||
ssize_t len;
|
||||
|
||||
if (!(comma = split(arg)) || (len = strlen(comma)) == 0)
|
||||
ret_err(gen_err);
|
||||
|
||||
new->wildcard = 0;
|
||||
new->netid = id;
|
||||
id->net = opt_string_alloc(set_prefix(arg));
|
||||
|
||||
if (comma[len-1] == '*')
|
||||
{
|
||||
comma[len-1] = 0;
|
||||
new->wildcard = 1;
|
||||
}
|
||||
new->name = opt_string_alloc(comma);
|
||||
|
||||
new->next = daemon->dhcp_name_match;
|
||||
daemon->dhcp_name_match = new;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'M': /* --dhcp-boot */
|
||||
{
|
||||
struct dhcp_netid *id = NULL;
|
||||
@@ -3971,7 +4001,7 @@ err:
|
||||
|
||||
if (data)
|
||||
{
|
||||
new->txt=opt_malloc(len);
|
||||
new->txt = opt_malloc(len);
|
||||
new->len = len;
|
||||
memcpy(new->txt, data, len);
|
||||
}
|
||||
@@ -3979,6 +4009,37 @@ err:
|
||||
break;
|
||||
}
|
||||
|
||||
case LOPT_CAA: /* --caa-record */
|
||||
{
|
||||
struct txt_record *new;
|
||||
char *tag, *value;
|
||||
int flags;
|
||||
|
||||
comma = split(arg);
|
||||
tag = split(comma);
|
||||
value = split(tag);
|
||||
|
||||
new = opt_malloc(sizeof(struct txt_record));
|
||||
new->next = daemon->rr;
|
||||
daemon->rr = new;
|
||||
|
||||
if (!atoi_check(comma, &flags) || !tag || !value || !(new->name = canonicalise_opt(arg)))
|
||||
ret_err(_("bad CAA record"));
|
||||
|
||||
unhide_metas(tag);
|
||||
unhide_metas(value);
|
||||
|
||||
new->len = strlen(tag) + strlen(value) + 2;
|
||||
new->txt = opt_malloc(new->len);
|
||||
new->txt[0] = flags;
|
||||
new->txt[1] = strlen(tag);
|
||||
memcpy(&new->txt[2], tag, strlen(tag));
|
||||
memcpy(&new->txt[2 + strlen(tag)], value, strlen(value));
|
||||
new->class = T_CAA;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Y': /* --txt-record */
|
||||
{
|
||||
struct txt_record *new;
|
||||
@@ -4336,7 +4397,7 @@ static void read_file(char *file, FILE *f, int hard_opt)
|
||||
if (errmess)
|
||||
strcpy(daemon->namebuff, errmess);
|
||||
|
||||
if (errmess || !one_opt(option, arg, buff, _("error"), 0, hard_opt == LOPT_REV_SERV))
|
||||
if (errmess || !one_opt(option, arg, daemon->namebuff, _("error"), 0, hard_opt == LOPT_REV_SERV))
|
||||
{
|
||||
sprintf(daemon->namebuff + strlen(daemon->namebuff), _(" at line %d of %s"), lineno, file);
|
||||
if (hard_opt != 0)
|
||||
|
||||
+11
-16
@@ -986,40 +986,35 @@ size_t setup_reply(struct dns_header *header, size_t qlen,
|
||||
/* check if name matches local names ie from /etc/hosts or DHCP or local mx names. */
|
||||
int check_for_local_domain(char *name, time_t now)
|
||||
{
|
||||
struct crec *crecp;
|
||||
struct mx_srv_record *mx;
|
||||
struct txt_record *txt;
|
||||
struct interface_name *intr;
|
||||
struct ptr_record *ptr;
|
||||
struct naptr *naptr;
|
||||
|
||||
/* Note: the call to cache_find_by_name is intended to find any record which matches
|
||||
ie A, AAAA, CNAME. */
|
||||
|
||||
if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_NO_RR)) &&
|
||||
(crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
|
||||
return 1;
|
||||
|
||||
for (naptr = daemon->naptr; naptr; naptr = naptr->next)
|
||||
if (hostname_isequal(name, naptr->name))
|
||||
if (hostname_issubdomain(name, naptr->name))
|
||||
return 1;
|
||||
|
||||
for (mx = daemon->mxnames; mx; mx = mx->next)
|
||||
if (hostname_isequal(name, mx->name))
|
||||
if (hostname_issubdomain(name, mx->name))
|
||||
return 1;
|
||||
|
||||
for (txt = daemon->txt; txt; txt = txt->next)
|
||||
if (hostname_isequal(name, txt->name))
|
||||
if (hostname_issubdomain(name, txt->name))
|
||||
return 1;
|
||||
|
||||
for (intr = daemon->int_names; intr; intr = intr->next)
|
||||
if (hostname_isequal(name, intr->name))
|
||||
if (hostname_issubdomain(name, intr->name))
|
||||
return 1;
|
||||
|
||||
for (ptr = daemon->ptr; ptr; ptr = ptr->next)
|
||||
if (hostname_isequal(name, ptr->name))
|
||||
if (hostname_issubdomain(name, ptr->name))
|
||||
return 1;
|
||||
|
||||
|
||||
if (cache_find_non_terminal(name, now))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1382,11 +1377,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
sec_data = 0;
|
||||
if (!dryrun)
|
||||
{
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, "<RR>");
|
||||
log_query(F_CONFIG | F_RRNAME, name, NULL, querystr(NULL, t->class));
|
||||
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
|
||||
daemon->local_ttl, NULL,
|
||||
t->class, C_IN, "t", t->len, t->txt))
|
||||
anscount ++;
|
||||
anscount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+51
-2
@@ -626,6 +626,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
}
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_BOOTP]++;
|
||||
log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
|
||||
|
||||
return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
|
||||
@@ -699,8 +700,39 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
client_hostname = daemon->dhcp_buff;
|
||||
}
|
||||
|
||||
if (client_hostname && option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
|
||||
if (client_hostname)
|
||||
{
|
||||
struct dhcp_match_name *m;
|
||||
size_t nl = strlen(client_hostname);
|
||||
|
||||
if (option_bool(OPT_LOG_OPTS))
|
||||
my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), ntohl(mess->xid), client_hostname);
|
||||
|
||||
|
||||
for (m = daemon->dhcp_name_match; m; m = m->next)
|
||||
{
|
||||
size_t ml = strlen(m->name);
|
||||
char save = 0;
|
||||
|
||||
if (nl < ml)
|
||||
continue;
|
||||
if (nl > ml)
|
||||
{
|
||||
save = client_hostname[ml];
|
||||
client_hostname[ml] = 0;
|
||||
}
|
||||
|
||||
if (hostname_isequal(client_hostname, m->name) &&
|
||||
(save == 0 || m->wildcard))
|
||||
{
|
||||
m->netid->next = netid;
|
||||
netid = m->netid;
|
||||
}
|
||||
|
||||
if (save != 0)
|
||||
client_hostname[ml] = save;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_config(config, CONFIG_NAME))
|
||||
{
|
||||
@@ -928,6 +960,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if ((pxe && !workaround) || !redirect4011)
|
||||
do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
|
||||
|
||||
daemon->metrics[METRIC_PXE]++;
|
||||
log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
if (!ignore)
|
||||
@@ -962,6 +995,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
|
||||
return 0;
|
||||
|
||||
daemon->metrics[METRIC_DHCPDECLINE]++;
|
||||
log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
|
||||
|
||||
if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
|
||||
@@ -994,6 +1028,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
else
|
||||
message = _("unknown lease");
|
||||
|
||||
daemon->metrics[METRIC_DHCPRELEASE]++;
|
||||
log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
|
||||
|
||||
return 0;
|
||||
@@ -1060,6 +1095,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
message = _("no address available");
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DHCPDISCOVER]++;
|
||||
log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid);
|
||||
|
||||
if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
|
||||
@@ -1080,6 +1116,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
goto rapid_commit;
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DHCPOFFER]++;
|
||||
log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
|
||||
|
||||
time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
|
||||
@@ -1192,6 +1229,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
mess->yiaddr = mess->ciaddr;
|
||||
}
|
||||
|
||||
daemon->metrics[METRIC_DHCPREQUEST]++;
|
||||
log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
|
||||
|
||||
rapid_commit:
|
||||
@@ -1265,6 +1303,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
if (message)
|
||||
{
|
||||
daemon->metrics[rapid_commit ? METRIC_NOANSWER : METRIC_DHCPNAK]++;
|
||||
log_packet(rapid_commit ? "NOANSWER" : "DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
|
||||
|
||||
/* rapid commit case: lease allocate failed but don't send DHCPNAK */
|
||||
@@ -1426,6 +1465,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
else
|
||||
override = lease->override;
|
||||
|
||||
daemon->metrics[METRIC_DHCPACK]++;
|
||||
log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
|
||||
|
||||
clear_packet(mess, end);
|
||||
@@ -1444,6 +1484,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
if (ignore || have_config(config, CONFIG_DISABLE))
|
||||
message = _("ignored");
|
||||
|
||||
daemon->metrics[METRIC_DHCPINFORM]++;
|
||||
log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
|
||||
|
||||
if (message || mess->ciaddr.s_addr == 0)
|
||||
@@ -1470,6 +1511,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||
|
||||
log_tags(tagif_netid, ntohl(mess->xid));
|
||||
|
||||
daemon->metrics[METRIC_DHCPACK]++;
|
||||
log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
|
||||
|
||||
if (lease)
|
||||
@@ -1636,6 +1678,13 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
||||
daemon->namebuff,
|
||||
string ? string : "",
|
||||
err ? err : "");
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
if (!strcmp(type, "DHCPACK"))
|
||||
ubus_event_bcast("dhcp.ack", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
else if (!strcmp(type, "DHCPRELEASE"))
|
||||
ubus_event_bcast("dhcp.release", daemon->namebuff, addr ? inet_ntoa(a) : NULL, string ? string : NULL, interface);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void log_options(unsigned char *start, u32 xid)
|
||||
|
||||
+61
-43
@@ -639,9 +639,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
int plain_range = 1;
|
||||
u32 lease_time;
|
||||
struct dhcp_lease *ltmp;
|
||||
struct in6_addr *req_addr;
|
||||
struct in6_addr addr;
|
||||
|
||||
struct in6_addr req_addr, addr;
|
||||
|
||||
if (!check_ia(state, opt, &ia_end, &ia_option))
|
||||
continue;
|
||||
|
||||
@@ -709,9 +708,10 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
req_addr = opt6_ptr(ia_option, 0);
|
||||
/* worry about alignment here. */
|
||||
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if ((c = address6_valid(state->context, req_addr, solicit_tags, plain_range)))
|
||||
if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range)))
|
||||
{
|
||||
lease_time = c->lease_time;
|
||||
/* If the client asks for an address on the same network as a configured address,
|
||||
@@ -719,14 +719,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
addresses automatic. */
|
||||
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(state, &addr))
|
||||
{
|
||||
req_addr = &addr;
|
||||
req_addr = addr;
|
||||
mark_config_used(c, &addr);
|
||||
if (have_config(config, CONFIG_TIME))
|
||||
lease_time = config->lease_time;
|
||||
}
|
||||
else if (!(c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
|
||||
else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
|
||||
continue; /* not an address we're allowed */
|
||||
else if (!check_address(state, req_addr))
|
||||
else if (!check_address(state, &req_addr))
|
||||
continue; /* address leased elsewhere */
|
||||
|
||||
/* add address to output packet */
|
||||
@@ -734,8 +734,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
|
||||
state->send_prefix_class = prefix_class_from_context(c);
|
||||
#endif
|
||||
add_address(state, c, lease_time, ia_option, &min_time, req_addr, now);
|
||||
mark_context_used(state, req_addr);
|
||||
add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
|
||||
mark_context_used(state, &req_addr);
|
||||
get_context_tag(state, c);
|
||||
address_assigned = 1;
|
||||
}
|
||||
@@ -768,15 +768,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
ltmp = NULL;
|
||||
while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
|
||||
{
|
||||
req_addr = <mp->addr6;
|
||||
if ((c = address6_available(state->context, req_addr, solicit_tags, plain_range)))
|
||||
req_addr = ltmp->addr6;
|
||||
if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
|
||||
{
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
|
||||
state->send_prefix_class = prefix_class_from_context(c);
|
||||
#endif
|
||||
add_address(state, c, c->lease_time, NULL, &min_time, req_addr, now);
|
||||
mark_context_used(state, req_addr);
|
||||
add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
|
||||
mark_context_used(state, &req_addr);
|
||||
get_context_tag(state, c);
|
||||
address_assigned = 1;
|
||||
}
|
||||
@@ -892,16 +892,19 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
|
||||
for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
|
||||
struct in6_addr req_addr;
|
||||
struct dhcp_context *dynamic, *c;
|
||||
unsigned int lease_time;
|
||||
struct in6_addr addr;
|
||||
int config_ok = 0;
|
||||
|
||||
/* align. */
|
||||
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if ((c = address6_valid(state->context, req_addr, tagif, 1)))
|
||||
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);
|
||||
if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr);
|
||||
|
||||
if ((dynamic = address6_available(state->context, req_addr, tagif, 1)) || c)
|
||||
if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
|
||||
{
|
||||
if (!dynamic && !config_ok)
|
||||
{
|
||||
@@ -911,7 +914,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
put_opt6_string(_("address unavailable"));
|
||||
end_opt6(o1);
|
||||
}
|
||||
else if (!check_address(state, req_addr))
|
||||
else if (!check_address(state, &req_addr))
|
||||
{
|
||||
/* Address leased to another DUID/IAID */
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
@@ -933,7 +936,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
if (dump_all_prefix_classes && state->ia_type == OPTION6_IA_NA)
|
||||
state->send_prefix_class = prefix_class_from_context(c);
|
||||
#endif
|
||||
add_address(state, dynamic, lease_time, ia_option, &min_time, req_addr, now);
|
||||
add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now);
|
||||
get_context_tag(state, dynamic);
|
||||
address_assigned = 1;
|
||||
}
|
||||
@@ -996,15 +999,17 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
struct dhcp_lease *lease = NULL;
|
||||
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
|
||||
struct in6_addr req_addr;
|
||||
unsigned int preferred_time = opt6_uint(ia_option, 16, 4);
|
||||
unsigned int valid_time = opt6_uint(ia_option, 20, 4);
|
||||
char *message = NULL;
|
||||
struct dhcp_context *this_context;
|
||||
|
||||
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if (!(lease = lease6_find(state->clid, state->clid_len,
|
||||
state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
|
||||
state->iaid, req_addr)))
|
||||
state->iaid, &req_addr)))
|
||||
{
|
||||
/* If the server cannot find a client entry for the IA the server
|
||||
returns the IA containing no addresses with a Status Code option set
|
||||
@@ -1012,7 +1017,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
save_counter(iacntr);
|
||||
t1cntr = 0;
|
||||
|
||||
log6_packet(state, "DHCPREPLY", req_addr, _("lease not found"));
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
|
||||
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOBINDING);
|
||||
@@ -1024,15 +1029,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
}
|
||||
|
||||
|
||||
if ((this_context = address6_available(state->context, req_addr, tagif, 1)) ||
|
||||
(this_context = address6_valid(state->context, req_addr, tagif, 1)))
|
||||
if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
|
||||
(this_context = address6_valid(state->context, &req_addr, tagif, 1)))
|
||||
{
|
||||
struct in6_addr addr;
|
||||
unsigned int lease_time;
|
||||
|
||||
get_context_tag(state, this_context);
|
||||
|
||||
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))
|
||||
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, &req_addr) && have_config(config, CONFIG_TIME))
|
||||
lease_time = config->lease_time;
|
||||
else
|
||||
lease_time = this_context->lease_time;
|
||||
@@ -1045,7 +1050,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
|
||||
if (state->ia_type == OPTION6_IA_NA && state->hostname)
|
||||
{
|
||||
char *addr_domain = get_domain6(req_addr);
|
||||
char *addr_domain = get_domain6(&req_addr);
|
||||
if (!state->send_domain)
|
||||
state->send_domain = addr_domain;
|
||||
lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain);
|
||||
@@ -1063,12 +1068,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
}
|
||||
|
||||
if (message && (message != state->hostname))
|
||||
log6_packet(state, "DHCPREPLY", req_addr, message);
|
||||
log6_packet(state, "DHCPREPLY", &req_addr, message);
|
||||
else
|
||||
log6_quiet(state, "DHCPREPLY", req_addr, message);
|
||||
log6_quiet(state, "DHCPREPLY", &req_addr, message);
|
||||
|
||||
o1 = new_opt6(OPTION6_IAADDR);
|
||||
put_opt6(req_addr, sizeof(*req_addr));
|
||||
put_opt6(&req_addr, sizeof(req_addr));
|
||||
put_opt6_long(preferred_time);
|
||||
put_opt6_long(valid_time);
|
||||
end_opt6(o1);
|
||||
@@ -1100,20 +1105,23 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
ia_option;
|
||||
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
|
||||
struct in6_addr req_addr;
|
||||
|
||||
/* alignment */
|
||||
memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if (!address6_valid(state->context, req_addr, tagif, 1))
|
||||
if (!address6_valid(state->context, &req_addr, tagif, 1))
|
||||
{
|
||||
o1 = new_opt6(OPTION6_STATUS_CODE);
|
||||
put_opt6_short(DHCP6NOTONLINK);
|
||||
put_opt6_string(_("confirm failed"));
|
||||
end_opt6(o1);
|
||||
log6_quiet(state, "DHCPREPLY", req_addr, _("confirm failed"));
|
||||
log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
good_addr = 1;
|
||||
log6_quiet(state, "DHCPREPLY", req_addr, state->hostname);
|
||||
log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1172,9 +1180,12 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
|
||||
struct in6_addr addr;
|
||||
|
||||
/* align */
|
||||
memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
|
||||
state->iaid, opt6_ptr(ia_option, 0))))
|
||||
state->iaid, &addr)))
|
||||
lease_prune(lease, now);
|
||||
else
|
||||
{
|
||||
@@ -1191,7 +1202,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
}
|
||||
|
||||
o1 = new_opt6(OPTION6_IAADDR);
|
||||
put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
put_opt6(&addr, IN6ADDRSZ);
|
||||
put_opt6_long(0);
|
||||
put_opt6_long(0);
|
||||
end_opt6(o1);
|
||||
@@ -1234,12 +1245,15 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
|
||||
{
|
||||
struct dhcp_lease *lease;
|
||||
struct in6_addr *addrp = opt6_ptr(ia_option, 0);
|
||||
struct in6_addr addr;
|
||||
|
||||
if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp))
|
||||
/* align */
|
||||
memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
|
||||
if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, &addr))
|
||||
{
|
||||
prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
|
||||
inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);
|
||||
inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),
|
||||
daemon->addrbuff, daemon->dhcp_buff3);
|
||||
config->flags |= CONFIG_DECLINED;
|
||||
@@ -1251,7 +1265,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
context_tmp->addr_epoch++;
|
||||
|
||||
if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
|
||||
state->iaid, opt6_ptr(ia_option, 0))))
|
||||
state->iaid, &addr)))
|
||||
lease_prune(lease, now);
|
||||
else
|
||||
{
|
||||
@@ -1268,7 +1282,7 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_
|
||||
}
|
||||
|
||||
o1 = new_opt6(OPTION6_IAADDR);
|
||||
put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);
|
||||
put_opt6(&addr, IN6ADDRSZ);
|
||||
put_opt6_long(0);
|
||||
put_opt6_long(0);
|
||||
end_opt6(o1);
|
||||
@@ -1936,7 +1950,11 @@ static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_op
|
||||
}
|
||||
else if (type == OPTION6_IAADDR)
|
||||
{
|
||||
inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);
|
||||
struct in6_addr addr;
|
||||
|
||||
/* align */
|
||||
memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ);
|
||||
inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
|
||||
sprintf(daemon->namebuff, "%s PL=%u VL=%u",
|
||||
daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
|
||||
optname = "iaaddr";
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 dated June, 1991, or
|
||||
(at your option) version 3 dated 29 June, 2007.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "dnsmasq.h"
|
||||
|
||||
#ifdef HAVE_UBUS
|
||||
|
||||
#include <libubus.h>
|
||||
|
||||
static struct ubus_context *ubus = NULL;
|
||||
static struct blob_buf b;
|
||||
|
||||
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);
|
||||
static struct ubus_method ubus_object_methods[] = {
|
||||
{.name = "metrics", .handler = ubus_handle_metrics},
|
||||
};
|
||||
|
||||
static struct ubus_object_type ubus_object_type = UBUS_OBJECT_TYPE("dnsmasq", ubus_object_methods);
|
||||
|
||||
static struct ubus_object ubus_object = {
|
||||
.name = "dnsmasq",
|
||||
.type = &ubus_object_type,
|
||||
.methods = ubus_object_methods,
|
||||
.n_methods = ARRAY_SIZE(ubus_object_methods),
|
||||
};
|
||||
|
||||
void set_ubus_listeners()
|
||||
{
|
||||
if (!ubus)
|
||||
return;
|
||||
|
||||
poll_listen(ubus->sock.fd, POLLIN);
|
||||
poll_listen(ubus->sock.fd, POLLERR);
|
||||
poll_listen(ubus->sock.fd, POLLHUP);
|
||||
}
|
||||
|
||||
void check_ubus_listeners()
|
||||
{
|
||||
if (!ubus)
|
||||
{
|
||||
ubus = ubus_connect(NULL);
|
||||
if (!ubus)
|
||||
return;
|
||||
ubus_add_object(ubus, &ubus_object);
|
||||
}
|
||||
|
||||
if (poll_check(ubus->sock.fd, POLLIN))
|
||||
ubus_handle_event(ubus);
|
||||
|
||||
if (poll_check(ubus->sock.fd, POLLHUP))
|
||||
{
|
||||
ubus_free(ubus);
|
||||
ubus = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
blob_buf_init(&b, 0);
|
||||
|
||||
for(i=0; i < __METRIC_MAX; i++)
|
||||
blobmsg_add_u32(&b, get_metric_name(i), daemon->metrics[i]);
|
||||
|
||||
ubus_send_reply(ctx, req, b.head);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ubus_event_bcast(const char *type, const char *mac, const char *ip, const char *name, const char *interface)
|
||||
{
|
||||
if (!ubus || !ubus_object.has_subscribers)
|
||||
return;
|
||||
|
||||
blob_buf_init(&b, 0);
|
||||
if (mac)
|
||||
blobmsg_add_string(&b, "mac", mac);
|
||||
if (ip)
|
||||
blobmsg_add_string(&b, "ip", ip);
|
||||
if (name)
|
||||
blobmsg_add_string(&b, "name", name);
|
||||
if (interface)
|
||||
blobmsg_add_string(&b, "interface", interface);
|
||||
|
||||
ubus_notify(ubus, &ubus_object, type, b.head, -1);
|
||||
}
|
||||
|
||||
|
||||
#endif /* HAVE_UBUS */
|
||||
+38
@@ -355,6 +355,44 @@ int hostname_isequal(const char *a, const char *b)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* is b equal to or a subdomain of a return 2 for equal, 1 for subdomain */
|
||||
int hostname_issubdomain(char *a, char *b)
|
||||
{
|
||||
char *ap, *bp;
|
||||
unsigned int c1, c2;
|
||||
|
||||
/* move to the end */
|
||||
for (ap = a; *ap; ap++);
|
||||
for (bp = b; *bp; bp++);
|
||||
|
||||
/* a shorter than b or a empty. */
|
||||
if ((bp - b) < (ap - a) || ap == a)
|
||||
return 0;
|
||||
|
||||
do
|
||||
{
|
||||
c1 = (unsigned char) *(--ap);
|
||||
c2 = (unsigned char) *(--bp);
|
||||
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
} while (ap != a);
|
||||
|
||||
if (bp == b)
|
||||
return 2;
|
||||
|
||||
if (*(--bp) == '.')
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
time_t dnsmasq_time(void)
|
||||
{
|
||||
#ifdef HAVE_BROKEN_RTC
|
||||
|
||||
Reference in new issue
Block a user