Add --dynamic-host option.

A and AAAA records which take their
network part from the network of a local interface. Useful
for routers with dynamically prefixes.
This commit is contained in:
Simon Kelley
2021-03-04 16:54:14 +00:00
parent 14e3f6ba19
commit b7cf754f6f
5 changed files with 170 additions and 44 deletions

View File

@@ -14,13 +14,18 @@ version 2.85
sorts before v2.83test1. This fixes the problem which lead
to 2.84 announcing itself as 2.84rc2.
Avoid treating a --dhcp-host which has an IPv6 address
Avoid treating a --dhcp-host which has an IPv6 address
as eligable for use with DHCPv4 on the grounds that it has
no address, and vice-versa. Thanks to Viktor Papp for
spotting the problem. (This bug was fixed was back in 2.67, and
then regessed in 2.81).
Add --dynamic-host option: A and AAAA records which take their
network part from the network of a local interface. Useful
for routers with dynamically prefixes. Thanks
to Fred F for the suggestion.
version 2.84
Fix a problem, introduced in 2.83, which could see DNS replies
being sent via the wrong socket. On machines running both

View File

@@ -296,7 +296,7 @@ option requires non-standard networking APIs and it is only available
under Linux. On other platforms it falls-back to \fB--bind-interfaces\fP mode.
.TP
.B \-y, --localise-queries
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP which depend on the interface over which the query was
Return answers to DNS queries from /etc/hosts and \fB--interface-name\fP and \fB--dynamic-host\fP which depend on the interface over which the query was
received. If a name has more than one address associated with
it, and at least one of those addresses is on the same subnet as the
interface to which the query was sent, then return only the
@@ -591,6 +591,12 @@ If the time-to-live is given, it overrides the default, which is zero
or the value of \fB--local-ttl\fP. The value is a positive integer and gives
the time-to-live in seconds.
.TP
.B --dynamic-host=<name>,[IPv4-address],[IPv6-address],<interface>
Add A, AAAA and PTR records to the DNS in the same subnet as the specified interface. The address is derived from the network part of each address associated with the interface, and the host part from the specified address. For example
.B --dynamic-host=example.com,0.0.0.8,eth0
will, when eth0 has the address 192.168.78.x and netmask 255.255.255.0 give the
name example.com an A record for 192.168.78.8. The same principle applies to IPv6 addresses. Note that if an interface has more than one address, more than one A or AAAA record will be created. The TTL of the records is always zero, and any changes to interface addresses will be immediately reflected in them.
.TP
.B \-Y, --txt-record=<name>[[,<text>],<text>]
Return a TXT DNS record. The value of TXT record is a set of strings,
so any number may be included, delimited by commas; use quotes to put
@@ -2380,6 +2386,8 @@ IPv4 and IPv6 addresses from /etc/hosts (and
.B --host-record
and
.B --interface-name
and
.B ---dynamic-host
provided the address falls into one of the subnets specified in the
.B --auth-zone.
.PP

View File

@@ -423,10 +423,17 @@ struct host_record {
struct host_record *next;
};
#define IN4 1
#define IN6 2
#define INP4 4
#define INP6 8
struct interface_name {
char *name; /* domain name */
char *intr; /* interface name */
int family; /* AF_INET, AF_INET6 or zero for both */
int flags;
struct in_addr proto4;
struct in6_addr proto6;
struct addrlist *addr;
struct interface_name *next;
};

View File

@@ -348,36 +348,109 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label,
/* Update addresses from interface_names. These are a set independent
of the set we're listening on. */
for (int_name = daemon->int_names; int_name; int_name = int_name->next)
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0 &&
(addr->sa.sa_family == int_name->family || int_name->family == 0))
if (strncmp(label, int_name->intr, IF_NAMESIZE) == 0)
{
if (param->spare)
struct addrlist *lp;
al = NULL;
if (addr->sa.sa_family == AF_INET && (int_name->flags & (IN4 | INP4)))
{
al = param->spare;
param->spare = al->next;
struct in_addr newaddr = addr->in.sin_addr;
if (int_name->flags & INP4)
{
if (netmask.s_addr == 0xffff)
continue;
newaddr.s_addr = (addr->in.sin_addr.s_addr & netmask.s_addr) |
(int_name->proto4.s_addr & ~netmask.s_addr);
}
/* check for duplicates. */
for (lp = int_name->addr; lp; lp = lp->next)
if (lp->flags == 0 && lp->addr.addr4.s_addr == newaddr.s_addr)
break;
if (!lp)
{
if (param->spare)
{
al = param->spare;
param->spare = al->next;
}
else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{
al->flags = 0;
al->addr.addr4 = newaddr;
}
}
}
if (addr->sa.sa_family == AF_INET6 && (int_name->flags & (IN6 | INP6)))
{
struct in6_addr newaddr = addr->in6.sin6_addr;
if (int_name->flags & INP6)
{
int i;
/* No sense in doing /128. */
if (prefixlen == 128)
continue;
for (i = 0; i < 16; i++)
{
int bits = ((i+1)*8) - prefixlen;
if (bits >= 8)
newaddr.s6_addr[i] = int_name->proto6.s6_addr[i];
else if (bits >= 0)
{
unsigned char mask = 0xff << bits;
newaddr.s6_addr[i] =
(addr->in6.sin6_addr.s6_addr[i] & mask) |
(int_name->proto6.s6_addr[i] & ~mask);
}
}
}
/* check for duplicates. */
for (lp = int_name->addr; lp; lp = lp->next)
if ((lp->flags & ADDRLIST_IPV6) &&
IN6_ARE_ADDR_EQUAL(&lp->addr.addr6, &newaddr))
break;
if (!lp)
{
if (param->spare)
{
al = param->spare;
param->spare = al->next;
}
else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{
al->flags = ADDRLIST_IPV6;
al->addr.addr6 = newaddr;
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
don't appear in forward queries, but will in reverse ones. */
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
al->flags |= ADDRLIST_REVONLY;
}
}
}
else
al = whine_malloc(sizeof(struct addrlist));
if (al)
{
al->next = int_name->addr;
int_name->addr = al;
if (addr->sa.sa_family == AF_INET)
{
al->addr.addr4 = addr->in.sin_addr;
al->flags = 0;
}
else
{
al->addr.addr6 = addr->in6.sin6_addr;
al->flags = ADDRLIST_IPV6;
/* Privacy addresses and addresses still undergoing DAD and deprecated addresses
don't appear in forward queries, but will in reverse ones. */
if (!(iface_flags & IFACE_PERMANENT) || (iface_flags & (IFACE_DEPRECATED | IFACE_TENTATIVE)))
al->flags |= ADDRLIST_REVONLY;
}
}
}
}

View File

@@ -168,6 +168,7 @@ struct myoption {
#define LOPT_SINGLE_PORT 359
#define LOPT_SCRIPT_TIME 360
#define LOPT_PXE_VENDOR 361
#define LOPT_DYNHOST 362
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -341,6 +342,7 @@ static const struct myoption opts[] =
{ "dumpfile", 1, 0, LOPT_DUMPFILE },
{ "dumpmask", 1, 0, LOPT_DUMPMASK },
{ "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID },
{ "dynamic-host", 1, 0, LOPT_DYNHOST },
{ NULL, 0, 0, 0 }
};
@@ -491,6 +493,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_DYNHOST, ARG_DUP, "<name>,[<IPv4>][,<IPv6>],<interface-name>", gettext_noop("Specify host record in interface subnet"), 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 },
@@ -4075,36 +4078,66 @@ err:
}
case LOPT_INTNAME: /* --interface-name */
case LOPT_DYNHOST: /* --dynamic-host */
{
struct interface_name *new, **up;
char *domain = NULL;
comma = split(arg);
char *domain = arg;
if (!comma || !(domain = canonicalise_opt(arg)))
ret_err(_("bad interface name"));
arg = split(arg);
new = opt_malloc(sizeof(struct interface_name));
new->next = NULL;
new->addr = NULL;
memset(new, 0, sizeof(struct interface_name));
new->flags = IN4 | IN6;
/* Add to the end of the list, so that first name
of an interface is used for PTR lookups. */
for (up = &daemon->int_names; *up; up = &((*up)->next));
*up = new;
new->name = domain;
new->family = 0;
arg = split_chr(comma, '/');
if (arg)
while ((comma = split(arg)))
{
if (strcmp(arg, "4") == 0)
new->family = AF_INET;
else if (strcmp(arg, "6") == 0)
new->family = AF_INET6;
if (inet_pton(AF_INET, arg, &new->proto4))
new->flags |= INP4;
else if (inet_pton(AF_INET6, arg, &new->proto6))
new->flags |= INP6;
else
break;
arg = comma;
}
if ((comma = split_chr(arg, '/')))
{
if (strcmp(comma, "4") == 0)
new->flags &= ~IN6;
else if (strcmp(comma, "6") == 0)
new->flags &= ~IN4;
else
ret_err_free(gen_err, new);
}
new->intr = opt_string_alloc(comma);
}
new->intr = opt_string_alloc(arg);
if (option == LOPT_DYNHOST)
{
if (!(new->flags & (INP4 | INP6)))
ret_err(_("missing address in dynamic host"));
if (!(new->flags & IN4) || !(new->flags & IN6))
arg = NULL; /* provoke error below */
new->flags &= ~(IN4 | IN6);
}
else
{
if (new->flags & (INP4 | INP6))
arg = NULL; /* provoke error below */
}
if (!domain || !arg || !(new->name = canonicalise_opt(domain)))
ret_err(option == LOPT_DYNHOST ?
_("bad dynamic host") : _("bad interface name"));
break;
}