mirror of
https://gitlab.nic.cz/knot/knot-dns.git
synced 2026-06-09 00:22:42 -04:00
conf: add %c and %l zonefile formatters
This commit is contained in:
parent
30738cdba1
commit
054694c0cc
4 changed files with 274 additions and 16 deletions
|
|
@ -526,6 +526,16 @@ A path to the zone file. Non absolute path is relative to
|
|||
\fI\%storage\fP\&. It is also possible to use the following formatters:
|
||||
.INDENT 0.0
|
||||
.IP \(bu 2
|
||||
\fB%c[\fP\fIN\fP\fB]\fP or \fB%c[\fP\fIN\fP\fB\-\fP\fIM\fP\fB]\fP – means the \fIN\fPth
|
||||
character or a sequence of characters beginning from the \fIN\fPth and ending
|
||||
with the \fIM\fPth character of the textual zone name (see \fB%s\fP). The
|
||||
indexes are counted from 0 from the left. If the character is not available,
|
||||
the formatter has no effect.
|
||||
.IP \(bu 2
|
||||
\fB%l[\fP\fIN\fP\fB]\fP – means the \fIN\fPth label of the textual zone name
|
||||
(see \fB%s\fP). The index is counted from 0 from the right (0 ~ TLD).
|
||||
If the label is not available, the formatter has no effect.
|
||||
.IP \(bu 2
|
||||
\fB%s\fP – means the current zone name in the textual representation (beware
|
||||
of special characters which are escaped or encoded in the \eDDD form where
|
||||
DDD is corresponding decimal ASCII code). The zone name doesn\(aqt include the
|
||||
|
|
|
|||
|
|
@ -609,6 +609,14 @@ file
|
|||
A path to the zone file. Non absolute path is relative to
|
||||
:ref:`storage<zone_storage>`. It is also possible to use the following formatters:
|
||||
|
||||
- ``%c[``\ *N*\ ``]`` or ``%c[``\ *N*\ ``-``\ *M*\ ``]`` – means the *N*\ th
|
||||
character or a sequence of characters beginning from the *N*\ th and ending
|
||||
with the *M*\ th character of the textual zone name (see ``%s``). The
|
||||
indexes are counted from 0 from the left. If the character is not available,
|
||||
the formatter has no effect.
|
||||
- ``%l[``\ *N*\ ``]`` – means the *N*\ th label of the textual zone name
|
||||
(see ``%s``). The index is counted from 0 from the right (0 ~ TLD).
|
||||
If the label is not available, the formatter has no effect.
|
||||
- ``%s`` – means the current zone name in the textual representation (beware
|
||||
of special characters which are escaped or encoded in the \\DDD form where
|
||||
DDD is corresponding decimal ASCII code). The zone name doesn't include the
|
||||
|
|
|
|||
|
|
@ -641,6 +641,151 @@ void conf_free_mod_id(
|
|||
free(mod_id);
|
||||
}
|
||||
|
||||
static int get_index(
|
||||
const char **start,
|
||||
const char *end,
|
||||
uint8_t *index1,
|
||||
uint8_t *index2)
|
||||
{
|
||||
const char *pos = *start;
|
||||
uint8_t i1, i2;
|
||||
|
||||
// At least [n] must fit into.
|
||||
if (end - pos < 3 || pos[0] != '[') {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
// Check for the variant [n] or [m-n].
|
||||
switch (pos[2]) {
|
||||
case ']':
|
||||
i1 = pos[1] - '0';
|
||||
i2 = i1;
|
||||
if (i1 > 9) {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
*start += 3;
|
||||
break;
|
||||
case '-':
|
||||
if (index2 == NULL || end - pos < 5 || pos[4] != ']') {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
i1 = pos[1] - '0';
|
||||
i2 = pos[3] - '0';
|
||||
if (i1 > 9 || i2 > 9 || i1 > i2) {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
*start += 5;
|
||||
break;
|
||||
default:
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
*index1 = i1;
|
||||
if (index2 != NULL) {
|
||||
*index2 = i2;
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static int str_char(
|
||||
const knot_dname_t *zone,
|
||||
char *buff,
|
||||
size_t buff_len,
|
||||
uint8_t index1,
|
||||
uint8_t index2)
|
||||
{
|
||||
if (knot_dname_to_str(buff, zone, buff_len) == NULL) {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
// Remove the trailing dot if not root zone.
|
||||
size_t zone_len = strlen(buff);
|
||||
if (zone_len > 1) {
|
||||
buff[zone_len--] = '\0';
|
||||
}
|
||||
|
||||
// Get the block length.
|
||||
size_t len = index2 - index1 + 1;
|
||||
|
||||
// Check for out of scope block.
|
||||
if (index1 > zone_len) {
|
||||
buff[0] = '\0';
|
||||
return KNOT_EOK;
|
||||
}
|
||||
// Check for partial block.
|
||||
if (index1 + len > zone_len) {
|
||||
len = zone_len - index1;
|
||||
}
|
||||
|
||||
// Copy the block.
|
||||
memmove(buff, buff + index1, len);
|
||||
buff[len] = '\0';
|
||||
|
||||
// Replace possible slashes with underscores.
|
||||
char *ch;
|
||||
for (ch = buff; *ch != '\0'; ch++) {
|
||||
if (*ch == '/') {
|
||||
*ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static int str_zone(
|
||||
const knot_dname_t *zone,
|
||||
char *buff,
|
||||
size_t buff_len)
|
||||
{
|
||||
if (knot_dname_to_str(buff, zone, buff_len) == NULL) {
|
||||
return KNOT_EINVAL;
|
||||
}
|
||||
|
||||
// Replace possible slashes with underscores.
|
||||
char *ch;
|
||||
for (ch = buff; *ch != '\0'; ch++) {
|
||||
if (*ch == '/') {
|
||||
*ch = '_';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove trailing dot if not root zone.
|
||||
if (ch - buff > 1) {
|
||||
*(--ch) = '\0';
|
||||
}
|
||||
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
static int str_label(
|
||||
const knot_dname_t *zone,
|
||||
char *buff,
|
||||
size_t buff_len,
|
||||
uint8_t index)
|
||||
{
|
||||
int labels = knot_dname_labels(zone, NULL);
|
||||
|
||||
// Check for root label.
|
||||
if (labels == 0 && index == 0) {
|
||||
return str_zone(zone, buff, buff_len);
|
||||
} else if (labels < 1 || labels < index + 1) {
|
||||
buff[0] = '\0';
|
||||
return KNOT_EOK;
|
||||
}
|
||||
|
||||
knot_dname_t label[1 + KNOT_DNAME_MAXLABELLEN + 1];
|
||||
|
||||
// Create a dname from the single label.
|
||||
int prefix = (index == labels - 1) ?
|
||||
0 : knot_dname_prefixlen(zone, labels - index - 1, NULL);
|
||||
uint8_t label_len = *(zone + prefix);
|
||||
memcpy(label, zone + prefix, 1 + label_len);
|
||||
label[1 + label_len] = '\0';
|
||||
|
||||
return str_zone(label, buff, buff_len);
|
||||
}
|
||||
|
||||
static char* get_filename(
|
||||
conf_t *conf,
|
||||
namedb_txn_t *txn,
|
||||
|
|
@ -659,6 +804,7 @@ static char* get_filename(
|
|||
// If no formatter, copy the rest of the name.
|
||||
if (pos == NULL) {
|
||||
if (strlcat(out, name, sizeof(out)) >= sizeof(out)) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
|
@ -668,6 +814,7 @@ static char* get_filename(
|
|||
char *block = strndup(name, pos - name);
|
||||
if (block == NULL ||
|
||||
strlcat(out, block, sizeof(out)) >= sizeof(out)) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
|
||||
return NULL;
|
||||
}
|
||||
free(block);
|
||||
|
|
@ -676,30 +823,35 @@ static char* get_filename(
|
|||
name = pos + 2;
|
||||
|
||||
char buff[512] = "";
|
||||
uint8_t idx1, idx2;
|
||||
|
||||
const char type = *(pos + 1);
|
||||
switch (type) {
|
||||
case '%':
|
||||
strlcat(buff, "%", sizeof(buff));
|
||||
break;
|
||||
case 's':
|
||||
if (knot_dname_to_str(buff, zone, sizeof(buff)) == NULL) {
|
||||
case 'c':
|
||||
if (get_index(&name, end, &idx1, &idx2) != KNOT_EOK ||
|
||||
str_char(zone, buff, sizeof(buff), idx1, idx2) != KNOT_EOK) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "failed to process "
|
||||
"zonefile formatter '%%%c'", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Replace possible slashes with underscores.
|
||||
char *ch;
|
||||
for (ch = buff; *ch != '\0'; ch++) {
|
||||
if (*ch == '/') {
|
||||
*ch = '_';
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (get_index(&name, end, &idx1, NULL) != KNOT_EOK ||
|
||||
str_label(zone, buff, sizeof(buff), idx1) != KNOT_EOK) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "failed to process "
|
||||
"zonefile formatter '%%%c'", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Remove trailing dot if not root zone.
|
||||
if (ch - buff > 1) {
|
||||
*(--ch) = '\0';
|
||||
break;
|
||||
case 's':
|
||||
if (str_zone(zone, buff, sizeof(buff)) != KNOT_EOK) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "failed to process "
|
||||
"zonefile formatter '%%%c'", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
case '\0':
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "ignoring missing "
|
||||
|
|
@ -712,6 +864,7 @@ static char* get_filename(
|
|||
}
|
||||
|
||||
if (strlcat(out, buff, sizeof(out)) >= sizeof(out)) {
|
||||
CONF_LOG_ZONE(LOG_WARNING, zone, "too long zonefile name");
|
||||
return NULL;
|
||||
}
|
||||
} while (name < end);
|
||||
|
|
|
|||
91
tests/conf.c
91
tests/conf.c
|
|
@ -16,11 +16,81 @@
|
|||
|
||||
#include <tap/basic.h>
|
||||
|
||||
#include "knot/conf/conf.c"
|
||||
#include "test_conf.h"
|
||||
|
||||
#define ZONE1 "0/25.2.0.192.in-addr.arpa."
|
||||
#define ZONE2 "."
|
||||
#define ZONE3 "x."
|
||||
#define ZONE4 "abc.ab.a."
|
||||
|
||||
static void check_name(const char *zone, const char *name, const char *ref)
|
||||
{
|
||||
knot_dname_t *z = knot_dname_from_str_alloc(zone);
|
||||
|
||||
char *file = get_filename(NULL, NULL, z, name);
|
||||
ok(file != NULL, "Get zonefile path for %s", zone);
|
||||
if (file != NULL) {
|
||||
ok(strcmp(file, ref) == 0, "Zonefile path compare %s", name);
|
||||
free(file);
|
||||
}
|
||||
|
||||
knot_dname_free(&z, NULL);
|
||||
}
|
||||
|
||||
static void check_name_err(const char *zone, const char *name)
|
||||
{
|
||||
knot_dname_t *z = knot_dname_from_str_alloc(zone);
|
||||
|
||||
ok(get_filename(NULL, NULL, z, name) == NULL, "Invalid name %s", name);
|
||||
|
||||
knot_dname_free(&z, NULL);
|
||||
}
|
||||
|
||||
static void test_get_filename(void)
|
||||
{
|
||||
// Char formatter.
|
||||
char *zone = "abc.def.gh";
|
||||
check_name(zone, "/%c[0]", "/a");
|
||||
check_name(zone, "/%c[9]", "/h");
|
||||
check_name(zone, "/%c[3]", "/.");
|
||||
check_name(zone, "/%c[0-1]", "/ab");
|
||||
check_name(zone, "/%c[1-1]", "/b");
|
||||
check_name(zone, "/%c[1-3]", "/bc.");
|
||||
check_name(zone, "/%c[1-4]", "/bc.d");
|
||||
check_name_err(zone, "/%c");
|
||||
check_name_err(zone, "/%cx");
|
||||
check_name_err(zone, "/%c[a]");
|
||||
check_name_err(zone, "/%c[:]");
|
||||
check_name_err(zone, "/%c[/]");
|
||||
check_name_err(zone, "/%c[12]");
|
||||
check_name_err(zone, "/%c[");
|
||||
check_name_err(zone, "/%c[1");
|
||||
check_name_err(zone, "/%c[1-");
|
||||
check_name_err(zone, "/%c[1-2");
|
||||
check_name_err(zone, "/%c[1-b]");
|
||||
check_name_err(zone, "/%c[9-0]");
|
||||
|
||||
zone = "abcd";
|
||||
check_name(zone, "/%c[2-9]", "/cd");
|
||||
|
||||
zone = ".";
|
||||
check_name(zone, "/%c[0]", "/.");
|
||||
check_name(zone, "/%c[1]", "/");
|
||||
|
||||
// Label formatter.
|
||||
zone = "abc.def.gh";
|
||||
check_name(zone, "/%l[0]", "/gh");
|
||||
check_name(zone, "/%l[1]", "/def");
|
||||
check_name(zone, "/%l[2]", "/abc");
|
||||
check_name(zone, "/%l[3]", "/");
|
||||
check_name(zone, "/%l[0]-%l[1]-%l[2]", "/gh-def-abc");
|
||||
check_name_err(zone, "/%l[0-1]");
|
||||
|
||||
zone = ".";
|
||||
check_name(zone, "/%l[0]", "/.");
|
||||
check_name(zone, "/%l[1]", "/");
|
||||
}
|
||||
|
||||
static void test_conf_zonefile(void)
|
||||
{
|
||||
|
|
@ -33,6 +103,8 @@ static void test_conf_zonefile(void)
|
|||
ok(zone2 != NULL, "create dname "ZONE2);
|
||||
knot_dname_t *zone3 = knot_dname_from_str_alloc(ZONE3);
|
||||
ok(zone3 != NULL, "create dname "ZONE3);
|
||||
knot_dname_t *zone4 = knot_dname_from_str_alloc(ZONE4);
|
||||
ok(zone4 != NULL, "create dname "ZONE4);
|
||||
|
||||
const char *conf_str =
|
||||
"template:\n"
|
||||
|
|
@ -45,7 +117,8 @@ static void test_conf_zonefile(void)
|
|||
" - domain: "ZONE2"\n"
|
||||
" file: /%s\n"
|
||||
" - domain: "ZONE3"\n"
|
||||
" file: /%s\n";
|
||||
" file: /%s\n"
|
||||
" - domain: "ZONE4"\n";
|
||||
|
||||
ret = test_conf(conf_str, NULL);
|
||||
ok(ret == KNOT_EOK, "Prepare configuration");
|
||||
|
|
@ -59,7 +132,7 @@ static void test_conf_zonefile(void)
|
|||
free(file);
|
||||
}
|
||||
|
||||
// Absolute path without formatters.
|
||||
// Absolute path without formatters - root zone.
|
||||
file = conf_zonefile(conf(), zone2);
|
||||
ok(file != NULL, "Get zonefile path for "ZONE2);
|
||||
if (file != NULL) {
|
||||
|
|
@ -77,16 +150,30 @@ static void test_conf_zonefile(void)
|
|||
free(file);
|
||||
}
|
||||
|
||||
// Default zonefile path.
|
||||
file = conf_zonefile(conf(), zone4);
|
||||
ok(file != NULL, "Get zonefile path for "ZONE4);
|
||||
if (file != NULL) {
|
||||
ok(strcmp(file, "/tmp/"ZONE4"zone") == 0,
|
||||
"Zonefile path compare for "ZONE4);
|
||||
free(file);
|
||||
}
|
||||
|
||||
conf_free(conf(), false);
|
||||
knot_dname_free(&zone1, NULL);
|
||||
knot_dname_free(&zone2, NULL);
|
||||
knot_dname_free(&zone3, NULL);
|
||||
knot_dname_free(&zone4, NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
plan_lazy();
|
||||
|
||||
diag("get_filename");
|
||||
test_get_filename();
|
||||
|
||||
diag("conf_zonefile");
|
||||
test_conf_zonefile();
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue