Add support for arbitrary prefix lengths in --rev-server and --domain=....,local

Previously, the prefix was limited to [8,16,24,32] for IPv4 and
to multiples of 4 for IPv6. This patch also makes the prefix-length optional
for --rev-server.

Inspired by a patch from DL6ER <dl6er@dl6er.de>, but completely
re-written by srk. All bugs are his.
This commit is contained in:
Simon Kelley
2021-09-20 00:05:42 +01:00
parent de372d6914
commit 35f93081dc
3 changed files with 140 additions and 72 deletions

View File

@@ -505,13 +505,14 @@ source address specified but the port may be specified directly as
part of the source address. Forcing queries to an interface is not
implemented on all platforms supported by dnsmasq.
.TP
.B --rev-server=<ip-address>/<prefix-len>[,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
.B --rev-server=<ip-address>[/<prefix-len>][,<ipaddr>][#<port>][@<interface>][@<source-ip>[#<port>]]
This is functionally the same as
.B --server,
but provides some syntactic sugar to make specifying address-to-name queries easier. For example
.B --rev-server=1.2.3.0/24,192.168.0.1
is exactly equivalent to
.B --server=/3.2.1.in-addr.arpa/192.168.0.1
Allowed prefix lengths are 1-32 (IPv4) and 1-128 (IPv6). If the prefix length is omitted, dnsmasq substitutes either 32 (IPv4) or 128 (IPv6).
.TP
.B \-A, --address=/<domain>[/<domain>...]/[<ipaddr>]
Specify an IP address to return for any host in the given domains.
@@ -1923,7 +1924,6 @@ additional flag "local" may be supplied which has the effect of adding
is identical to
.B --domain=thekelleys.org.uk,192.168.0.0/24
.B --local=/thekelleys.org.uk/ --local=/0.168.192.in-addr.arpa/
The network size must be 8, 16 or 24 for this to be legal.
.TP
.B --dhcp-fqdn
In the default mode, dnsmasq inserts the unqualified names of

View File

@@ -1586,6 +1586,9 @@ void check_servers(int no_loop_check)
if (serv->sfd)
serv->sfd->used = 1;
if (count == SERVERS_LOGGED)
my_syslog(LOG_INFO, _("more servers are defined but not logged"));
if (++count > SERVERS_LOGGED)
continue;

View File

@@ -935,52 +935,132 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a
return NULL;
}
static int domain_rev4(char *domain, struct in_addr addr, int msize)
static char *domain_rev4(int from_file, char *server, struct in_addr *addr4, int size)
{
in_addr_t a = ntohl(addr.s_addr);
*domain = 0;
int i, j;
char *string;
int msize;
u16 flags = 0;
char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
switch (msize)
{
case 32:
domain += sprintf(domain, "%u.", a & 0xff);
/* fall through */
case 24:
domain += sprintf(domain, "%d.", (a >> 8) & 0xff);
/* fall through */
case 16:
domain += sprintf(domain, "%d.", (a >> 16) & 0xff);
/* fall through */
case 8:
domain += sprintf(domain, "%d.", (a >> 24) & 0xff);
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
return string;
if (from_file)
flags |= SERV_FROM_FILE;
rem = size & 0x7;
addrbytes = (32 - size) >> 3;
addrbits = (32 - size) & 7;
if (size > 32 | size < 1)
return _("bad IPv4 prefix length");
for (i = 0; i < addrbytes; i++)
if (((u8 *)addr4)[3-i] != 0)
break;
default:
return 0;
if (i != addrbytes || (((u8 *)addr4)[3-addrbytes] & ((1 << addrbits) - 1)) != 0)
return _("address part not zero");
size = size & ~0x7;
if (rem != 0)
count = 1 << (8 - rem);
for (i = 0; i < count; i++)
{
*domain = 0;
string = domain;
msize = size/8;
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
{
int dig = ((unsigned char *)addr4)[j];
if (j == msize)
dig += i;
string += sprintf(string, "%d.", dig);
}
sprintf(string, "in-addr.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
sprintf(domain, "in-addr.arpa");
return 1;
return NULL;
}
static int domain_rev6(char *domain, struct in6_addr *addr, int msize)
static char *domain_rev6(int from_file, char *server, struct in6_addr *addr6, int size)
{
int i;
int i, j;
char *string;
int msize;
u16 flags = 0;
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
int count = 1, rem, addrbytes, addrbits;
if (msize > 128 || msize%4)
return 0;
if (!server)
flags = SERV_LITERAL_ADDRESS;
else if ((string = parse_server(server, &serv_addr, &source_addr, interface, &flags)))
return string;
if (from_file)
flags |= SERV_FROM_FILE;
*domain = 0;
rem = size & 0x3;
addrbytes = (128 - size) >> 3;
addrbits = (128 - size) & 7;
if (size > 128 || size < 1)
return _("bad IPv6 prefix length");
for (i = msize-1; i >= 0; i -= 4)
{
int dig = ((unsigned char *)addr)[i>>3];
domain += sprintf(domain, "%.1x.", (i>>2) & 1 ? dig & 15 : dig >> 4);
for (i = 0; i < addrbytes; i++)
if (addr6->s6_addr[15-i] != 0)
break;
if (i != addrbytes || (addr6->s6_addr[15-addrbytes] & ((1 << addrbits) - 1)) != 0)
return _("address part not zero");
size = size & ~0x3;
if (rem != 0)
count = 1 << (4 - rem);
for (i = 0; i < count; i++)
{
*domain = 0;
string = domain;
msize = size/4;
for (j = (rem == 0) ? msize-1 : msize; j >= 0; j--)
{
int dig = ((unsigned char *)addr6)[j>>1];
dig = j & 1 ? dig & 15 : dig >> 4;
if (j == msize)
dig += i;
string += sprintf(string, "%.1x.", dig);
}
sprintf(string, "ip6.arpa");
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
return _("error");
}
sprintf(domain, "ip6.arpa");
return 1;
return NULL;
}
#ifdef HAVE_DHCP
@@ -2308,17 +2388,13 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
strlen(new->prefix) > MAXLABEL - INET_ADDRSTRLEN)
ret_err_free(_("bad prefix"), new);
}
else if (strcmp(arg, "local") != 0 ||
(msize != 8 && msize != 16 && msize != 24))
else if (strcmp(arg, "local") != 0)
ret_err_free(gen_err, new);
else
{
char domain[29]; /* strlen("xxx.yyy.zzz.ttt.in-addr.arpa")+1 */
/* local=/xxx.yyy.zzz.in-addr.arpa/ */
/* domain_rev4 can't fail here, msize checked above. */
domain_rev4(domain, new->start, msize);
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
domain_rev4(0, NULL, &new->start, msize);
/* local=/<domain>/ */
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
@@ -2353,16 +2429,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
strlen(new->prefix) > MAXLABEL - INET6_ADDRSTRLEN)
ret_err_free(_("bad prefix"), new);
}
else if (strcmp(arg, "local") != 0 || ((msize & 4) != 0))
else if (strcmp(arg, "local") != 0)
ret_err_free(gen_err, new);
else
{
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
/* generate the equivalent of
local=/xxx.yyy.zzz.ip6.arpa/ */
domain_rev6(domain, &new->start6, msize);
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, domain, NULL);
domain_rev6(0, NULL, &new->start6, msize);
/* local=/<domain>/ */
/* d_raw can't failed to canonicalise here, checked above. */
add_update_server(SERV_LITERAL_ADDRESS, NULL, NULL, NULL, d_raw, NULL);
@@ -2735,46 +2809,37 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
{
char *string;
int size;
u16 flags = 0;
char domain[73]; /* strlen("32*<n.>ip6.arpa")+1 */
struct in_addr addr4;
struct in6_addr addr6;
union mysockaddr serv_addr, source_addr;
char interface[IF_NAMESIZE+1];
unhide_metas(arg);
if (!arg)
ret_err(gen_err);
comma=split(arg);
if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
ret_err(gen_err);
if (!(string = split_chr(arg, '/')) || !atoi_check(string, &size))
size = -1;
if (inet_pton(AF_INET, arg, &addr4))
{
if (!domain_rev4(domain, addr4, size))
ret_err(_("bad IPv4 prefix"));
if (size == -1)
size = 32;
if ((string = domain_rev4(servers_only, comma, &addr4, size)))
ret_err(string);
}
else if (inet_pton(AF_INET6, arg, &addr6))
{
if (!domain_rev6(domain, &addr6, size))
ret_err(_("bad IPv6 prefix"));
if (size == -1)
size = 128;
if ((string = domain_rev6(servers_only, comma, &addr6, size)))
ret_err(string);
}
else
ret_err(gen_err);
if (!comma)
flags |= SERV_LITERAL_ADDRESS;
else if ((string = parse_server(comma, &serv_addr, &source_addr, interface, &flags)))
ret_err(string);
if (servers_only)
flags |= SERV_FROM_FILE;
if (!add_update_server(flags, &serv_addr, &source_addr, interface, domain, NULL))
ret_err(gen_err);
break;
}