Commit 054694c0 authored by Daniel Salzman's avatar Daniel Salzman

conf: add %c and %l zonefile formatters

parent 30738cdb
......@@ -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);
......
......@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment