summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2021-09-20 00:05:42 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2021-09-20 00:05:42 +0100
commit35f93081dc9a52e64ac3b7196ad1f5c1106f8932 (patch)
tree3dfa0ecc2e1edf4943dc0d19edc748041eb9ec63
parentde372d6914ae20a1f9997815f258efbf3b14c39b (diff)
downloaddnsmasq-35f93081dc9a52e64ac3b7196ad1f5c1106f8932.tar.gz
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.
-rw-r--r--man/dnsmasq.84
-rw-r--r--src/network.c3
-rw-r--r--src/option.c203
3 files changed, 139 insertions, 71 deletions
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index 7ffccad..f04ae13 100644
--- a/man/dnsmasq.8
+++ b/man/dnsmasq.8
@@ -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
diff --git a/src/network.c b/src/network.c
index 3fc179d..296c7bd 100644
--- a/src/network.c
+++ b/src/network.c
@@ -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;
diff --git a/src/option.c b/src/option.c
index 11655fd..6777496 100644
--- a/src/option.c
+++ b/src/option.c
@@ -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;
- }
- sprintf(domain, "in-addr.arpa");
+ if (i != addrbytes || (((u8 *)addr4)[3-addrbytes] & ((1 << addrbits) - 1)) != 0)
+ return _("address part not zero");
- return 1;
+ 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");
+ }
+
+ 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 (!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 & 0x3;
+ addrbytes = (128 - size) >> 3;
+ addrbits = (128 - size) & 7;
+
+ if (size > 128 || size < 1)
+ return _("bad IPv6 prefix length");
- if (msize > 128 || msize%4)
- return 0;
+ 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;
- *domain = 0;
+ 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");
- 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);
+ 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;
}