diff options
-rw-r--r-- | daemon/lua/kres-gen-30.lua | 2 | ||||
-rw-r--r-- | daemon/lua/kres-gen-31.lua | 2 | ||||
-rw-r--r-- | daemon/lua/kres-gen-32.lua | 2 | ||||
-rwxr-xr-x | daemon/lua/kres-gen.sh | 2 | ||||
-rw-r--r-- | daemon/main.c | 3 | ||||
-rw-r--r-- | lib/meson.build | 1 | ||||
-rw-r--r-- | lib/rules/api.h | 26 | ||||
-rw-r--r-- | lib/rules/local-addr.c | 236 | ||||
-rw-r--r-- | modules/hints/hints.c | 219 |
9 files changed, 273 insertions, 220 deletions
diff --git a/daemon/lua/kres-gen-30.lua b/daemon/lua/kres-gen-30.lua index 76318389..9d2d846e 100644 --- a/daemon/lua/kres-gen-30.lua +++ b/daemon/lua/kres-gen-30.lua @@ -498,6 +498,8 @@ int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t); int kr_rule_local_data_nxdomain(const knot_dname_t *, kr_rule_tags_t); int kr_rule_zonefile(const struct kr_rule_zonefile_config *); int kr_rule_forward(const knot_dname_t *, kr_rule_fwd_flags_t, const struct sockaddr **); +int kr_rule_local_address(const char *, const char *, _Bool, uint32_t, kr_rule_tags_t); +int kr_rule_local_hosts(const char *, _Bool, uint32_t, kr_rule_tags_t); typedef struct { int sock_type; _Bool tls; diff --git a/daemon/lua/kres-gen-31.lua b/daemon/lua/kres-gen-31.lua index 1e9e1e3d..f1004930 100644 --- a/daemon/lua/kres-gen-31.lua +++ b/daemon/lua/kres-gen-31.lua @@ -498,6 +498,8 @@ int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t); int kr_rule_local_data_nxdomain(const knot_dname_t *, kr_rule_tags_t); int kr_rule_zonefile(const struct kr_rule_zonefile_config *); int kr_rule_forward(const knot_dname_t *, kr_rule_fwd_flags_t, const struct sockaddr **); +int kr_rule_local_address(const char *, const char *, _Bool, uint32_t, kr_rule_tags_t); +int kr_rule_local_hosts(const char *, _Bool, uint32_t, kr_rule_tags_t); typedef struct { int sock_type; _Bool tls; diff --git a/daemon/lua/kres-gen-32.lua b/daemon/lua/kres-gen-32.lua index cc37af74..993b092c 100644 --- a/daemon/lua/kres-gen-32.lua +++ b/daemon/lua/kres-gen-32.lua @@ -499,6 +499,8 @@ int kr_rule_local_data_emptyzone(const knot_dname_t *, kr_rule_tags_t); int kr_rule_local_data_nxdomain(const knot_dname_t *, kr_rule_tags_t); int kr_rule_zonefile(const struct kr_rule_zonefile_config *); int kr_rule_forward(const knot_dname_t *, kr_rule_fwd_flags_t, const struct sockaddr **); +int kr_rule_local_address(const char *, const char *, _Bool, uint32_t, kr_rule_tags_t); +int kr_rule_local_hosts(const char *, _Bool, uint32_t, kr_rule_tags_t); typedef struct { int sock_type; _Bool tls; diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh index 00dae4ff..0672dbcc 100755 --- a/daemon/lua/kres-gen.sh +++ b/daemon/lua/kres-gen.sh @@ -297,6 +297,8 @@ ${CDEFS} ${LIBKRES} functions <<-EOF kr_rule_local_data_nxdomain kr_rule_zonefile kr_rule_forward + kr_rule_local_address + kr_rule_local_hosts EOF diff --git a/daemon/main.c b/daemon/main.c index efbe8e55..53ecb3e8 100644 --- a/daemon/main.c +++ b/daemon/main.c @@ -71,10 +71,11 @@ KR_EXPORT const char *malloc_conf = "narenas:1"; #define TCP_BACKLOG_DEFAULT 128 #endif -/** I don't know why linker is dropping this _zonefile function otherwise. TODO: revisit. */ +/** I don't know why linker is dropping these functions otherwise. TODO: revisit. */ KR_EXPORT void kr_misc_unused(void) { kr_rule_zonefile(NULL); + kr_rule_local_address(NULL, NULL, false, 0, 0); } struct args the_args_value; /** Static allocation for the_args singleton. */ diff --git a/lib/meson.build b/lib/meson.build index 48185e17..d8cbf1fa 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -26,6 +26,7 @@ libkres_src = files([ 'rules/api.c', 'rules/defaults.c', 'rules/forward.c', + 'rules/local-addr.c', 'rules/zonefile.c', 'module.c', 'resolve.c', diff --git a/lib/rules/api.h b/lib/rules/api.h index e4995582..d791d296 100644 --- a/lib/rules/api.h +++ b/lib/rules/api.h @@ -80,6 +80,32 @@ int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_r KR_EXPORT int kr_rule_local_data_merge(const knot_rrset_t *rrs, kr_rule_tags_t tags); +/** Add a name-address pair into rules. + * + * - both forward and reverse mapping is added + * - merging is used; see kr_rule_local_data_merge() + * - NODATA is optionally inserted + */ +KR_EXPORT +int kr_rule_local_address(const char *name, const char *addr, + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags); + +/** For a given name, remove one address ##or all of them (if == NULL). + * + * Also remove the corresponding reverse record and (optionally) NODATA mark. + * Bug: it removes the whole forward RRset. + */ +KR_EXPORT +int kr_rule_local_address_del(const char *name, const char *addr, + bool use_nodata, kr_rule_tags_t tags); + +/** Load name-address pairs into rules from a hosts-like file. + * + * Same as kr_rule_data_address() but from a file. + */ +KR_EXPORT +int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule_tags_t tags); + /** Remove a local data rule. * * \return the number of deleted rules or error < 0 diff --git a/lib/rules/local-addr.c b/lib/rules/local-addr.c new file mode 100644 index 00000000..787639df --- /dev/null +++ b/lib/rules/local-addr.c @@ -0,0 +1,236 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "lib/rules/api.h" +#include "lib/rules/impl.h" + +#include "contrib/cleanup.h" + +#include <stdio.h> + +static int parse_addr_str(union kr_sockaddr *sa, const char *addr) +{ + int family = strchr(addr, ':') ? AF_INET6 : AF_INET; + memset(sa, 0, sizeof(*sa)); + sa->ip.sa_family = family; + char *addr_bytes = (/*const*/char *)kr_inaddr(&sa->ip); + if (inet_pton(family, addr, addr_bytes) != 1) { + return kr_error(EILSEQ); + } + return 0; +} + +static int add_pair(const char *name, const char *addr, + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +{ + /* Build key */ + knot_dname_t key[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(key, name, sizeof(key))) { + return kr_error(EINVAL); + } + knot_dname_to_lower(key); + + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { + return kr_error(EINVAL); + } + + uint16_t rrtype = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; + knot_rrset_t rrs; + knot_rrset_init(&rrs, key, rrtype, KNOT_CLASS_IN, ttl); + int ret; + if (ia.ip.sa_family == AF_INET6) { + ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip6.sin6_addr, 16, NULL); + } else { + ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip4.sin_addr, 4, NULL); + } + if (!ret) ret = kr_rule_local_data_merge(&rrs, tags); + if (!ret && use_nodata) { + rrs.type = KNOT_RRTYPE_CNAME; + rrs.rrs.count = 0; + rrs.rrs.size = 0; + // no point in the _merge() variant here + ret = kr_rule_local_data_ins(&rrs, NULL, tags); + } + + knot_rdataset_clear(&rrs.rrs, NULL); + return ret; +} + +/** @warning _NOT_ thread-safe; returns a pointer to static data! */ +static const knot_dname_t * raw_addr2reverse(const uint8_t *raw_addr, int family) +{ + #define REV_MAXLEN (4*16 + 16 /* the suffix, terminator, etc. */) + char reverse_addr[REV_MAXLEN]; + static knot_dname_t dname[REV_MAXLEN]; + #undef REV_MAXLEN + + if (family == AF_INET) { + snprintf(reverse_addr, sizeof(reverse_addr), + "%d.%d.%d.%d.in-addr.arpa.", + raw_addr[3], raw_addr[2], raw_addr[1], raw_addr[0]); + } else if (family == AF_INET6) { + char *ra_it = reverse_addr; + for (int i = 15; i >= 0; --i) { + ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; + int written = snprintf(ra_it, free_space, "%x.%x.", + raw_addr[i] & 0x0f, raw_addr[i] >> 4); + if (kr_fails_assert(written < free_space)) + return NULL; + ra_it += written; + } + ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; + if (snprintf(ra_it, free_space, "ip6.arpa.") >= free_space) { + return NULL; + } + } else { + return NULL; + } + + if (!knot_dname_from_str(dname, reverse_addr, sizeof(dname))) { + return NULL; + } + return dname; +} +static const knot_dname_t * addr2reverse(const char *addr) +{ + /* Parse address string */ + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) { + return NULL; + } + return raw_addr2reverse((const /*sign*/uint8_t *)kr_inaddr(&ia.ip), + kr_inaddr_family(&ia.ip)); +} + +static int add_reverse_pair(const char *name, const char *addr, + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +{ + const knot_dname_t *key = addr2reverse(addr); + if (!key) + return kr_error(EINVAL); + knot_rrset_t rrs; + knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)key, + KNOT_RRTYPE_PTR, KNOT_CLASS_IN, ttl); + knot_dname_t ptr_name[KNOT_DNAME_MAXLEN]; + if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) + return kr_error(EINVAL); + int ret = knot_rrset_add_rdata(&rrs, ptr_name, knot_dname_size(ptr_name), NULL); + if (!ret) { + // We use _merge(). Using multiple PTR RRs is not recommended generally, + // but here it seems better than choosing any "arbitrarily". + ret = kr_rule_local_data_merge(&rrs, tags); + knot_rdataset_clear(&rrs.rrs, NULL); + } + return ret; +} + +int kr_rule_local_address(const char *name, const char *addr, + bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +{ + int ret = add_reverse_pair(name, addr, use_nodata, ttl, tags); + if (ret) return ret; + return add_pair(name, addr, use_nodata, ttl, tags); +} + +int kr_rule_local_address_del(const char *name, const char *addr, + bool use_nodata, kr_rule_tags_t tags) +{ + // Parse addr + if (!addr) + return kr_error(ENOSYS); + union kr_sockaddr ia; + if (parse_addr_str(&ia, addr) != 0) + return kr_error(EINVAL); + + // Remove the PTR + const knot_dname_t *reverse_key = addr2reverse(addr); + knot_rrset_t rrs; + knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)reverse_key, + KNOT_RRTYPE_PTR, KNOT_CLASS_IN, 0); + int ret = kr_rule_local_data_del(&rrs, tags); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair PTR for %s; error: %s\n", addr, kr_strerror(ret)); + if (ret != 1 && ret != kr_error(ENOENT)) // ignore ENOENT for PTR (duplicities) + return ret; + + // Remove the forward entry + knot_dname_t key_buf[KNOT_DNAME_MAXLEN]; + rrs.owner = knot_dname_from_str(key_buf, name, sizeof(key_buf)); + if (!rrs.owner) + return kr_error(EINVAL); + rrs.type = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; + ret = kr_rule_local_data_del(&rrs, tags); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair for %s; error: %s\n", name, kr_strerror(ret)); + + // Remove the NODATA entry; again, not perfect matching, + // but we don't care much about this dynamic hints API. + if (ret == 1 && use_nodata) { + rrs.type = KNOT_RRTYPE_CNAME; + ret = kr_rule_local_data_del(&rrs, tags); + if (ret != 1) + VERBOSE_MSG(NULL, "del_pair for NODATA %s; error: %s\n", + name, kr_strerror(ret)); + } + return ret < 0 ? ret : kr_ok(); +} + +int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) +{ + auto_fclose FILE *fp = fopen(path, "r"); + if (fp == NULL) { + kr_log_error(RULES, "reading '%s' failed: %s\n", path, strerror(errno)); + return kr_error(errno); + } else { + VERBOSE_MSG(NULL, "reading '%s'\n", path); + } + + /* Load file to map */ + size_t line_len_unused = 0; + size_t count = 0; + size_t line_count = 0; + auto_free char *line = NULL; + int ret = kr_ok(); + + while (getline(&line, &line_len_unused, fp) > 0) { + ++line_count; + /* Ingore #comments as described in man hosts.5 */ + char *comm = strchr(line, '#'); + if (comm) { + *comm = '\0'; + } + + char *saveptr = NULL; + const char *addr = strtok_r(line, " \t\n", &saveptr); + if (addr == NULL || strlen(addr) == 0) { + continue; + } + const char *canonical_name = strtok_r(NULL, " \t\n", &saveptr); + if (canonical_name == NULL) { + ret = kr_error(EINVAL); + goto error; + } + const char *name_tok; + while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) { + ret = add_pair(name_tok, addr, use_nodata, ttl, tags); + if (ret) + goto error; + count += 1; + } + ret = add_pair(canonical_name, addr, use_nodata, ttl, tags); + if (!ret) // PTR only to the canonical name + ret = add_reverse_pair(canonical_name, addr, use_nodata, ttl, tags); + if (ret) + goto error; + count += 1; + } +error: + if (ret) { + ret = kr_error(ret); + kr_log_error(RULES, "%s:%zu: invalid syntax\n", path, line_count); + } + VERBOSE_MSG(NULL, "loaded %zu hints\n", count); + return ret; +} diff --git a/modules/hints/hints.c b/modules/hints/hints.c index 1326938f..c422bce0 100644 --- a/modules/hints/hints.c +++ b/modules/hints/hints.c @@ -56,53 +56,6 @@ static int parse_addr_str(union kr_sockaddr *sa, const char *addr) return 0; } -/** @warning _NOT_ thread-safe; returns a pointer to static data! */ -static const knot_dname_t * raw_addr2reverse(const uint8_t *raw_addr, int family) -{ - #define REV_MAXLEN (4*16 + 16 /* the suffix, terminator, etc. */) - char reverse_addr[REV_MAXLEN]; - static knot_dname_t dname[REV_MAXLEN]; - #undef REV_MAXLEN - - if (family == AF_INET) { - snprintf(reverse_addr, sizeof(reverse_addr), - "%d.%d.%d.%d.in-addr.arpa.", - raw_addr[3], raw_addr[2], raw_addr[1], raw_addr[0]); - } else if (family == AF_INET6) { - char *ra_it = reverse_addr; - for (int i = 15; i >= 0; --i) { - ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; - int written = snprintf(ra_it, free_space, "%x.%x.", - raw_addr[i] & 0x0f, raw_addr[i] >> 4); - if (kr_fails_assert(written < free_space)) - return NULL; - ra_it += written; - } - ssize_t free_space = reverse_addr + sizeof(reverse_addr) - ra_it; - if (snprintf(ra_it, free_space, "ip6.arpa.") >= free_space) { - return NULL; - } - } else { - return NULL; - } - - if (!knot_dname_from_str(dname, reverse_addr, sizeof(dname))) { - return NULL; - } - return dname; -} - -static const knot_dname_t * addr2reverse(const char *addr) -{ - /* Parse address string */ - union kr_sockaddr ia; - if (parse_addr_str(&ia, addr) != 0) { - return NULL; - } - return raw_addr2reverse((const /*sign*/uint8_t *)kr_inaddr(&ia.ip), - kr_inaddr_family(&ia.ip)); -} - static int add_pair_root(struct kr_zonecut *hints, const char *name, const char *addr) { /* Build key */ @@ -119,171 +72,6 @@ static int add_pair_root(struct kr_zonecut *hints, const char *name, const char return kr_zonecut_add(hints, key, kr_inaddr(&ia.ip), kr_inaddr_len(&ia.ip)); } -static int add_pair(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) -{ - /* Build key */ - knot_dname_t key[KNOT_DNAME_MAXLEN]; - if (!knot_dname_from_str(key, name, sizeof(key))) { - return kr_error(EINVAL); - } - knot_dname_to_lower(key); - - union kr_sockaddr ia; - if (parse_addr_str(&ia, addr) != 0) { - return kr_error(EINVAL); - } - - uint16_t rrtype = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; - knot_rrset_t rrs; - knot_rrset_init(&rrs, key, rrtype, KNOT_CLASS_IN, ttl); - int ret; - if (ia.ip.sa_family == AF_INET6) { - ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip6.sin6_addr, 16, NULL); - } else { - ret = knot_rrset_add_rdata(&rrs, (const uint8_t *)&ia.ip4.sin_addr, 4, NULL); - } - if (!ret) ret = kr_rule_local_data_merge(&rrs, tags); - if (!ret && use_nodata) { - rrs.type = KNOT_RRTYPE_CNAME; - rrs.rrs.count = 0; - rrs.rrs.size = 0; - // no point in the _merge() variant here - ret = kr_rule_local_data_ins(&rrs, NULL, tags); - } - - knot_rdataset_clear(&rrs.rrs, NULL); - return ret; -} - -static int add_reverse_pair(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) -{ - const knot_dname_t *key = addr2reverse(addr); - if (!key) - return kr_error(EINVAL); - knot_rrset_t rrs; - knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)key, - KNOT_RRTYPE_PTR, KNOT_CLASS_IN, ttl); - knot_dname_t ptr_name[KNOT_DNAME_MAXLEN]; - if (!knot_dname_from_str(ptr_name, name, sizeof(ptr_name))) - return kr_error(EINVAL); - int ret = knot_rrset_add_rdata(&rrs, ptr_name, knot_dname_size(ptr_name), NULL); - if (!ret) { - // We use _merge(). Using multiple PTR RRs is not recommended generally, - // but here it seems better than choosing any "arbitrarily". - ret = kr_rule_local_data_merge(&rrs, tags); - knot_rdataset_clear(&rrs.rrs, NULL); - } - return ret; -} - -/** For a given name, remove either one address ##or all of them (if == NULL). - * - * Also remove the corresponding reverse records. - * Bug: it removes the whole forward RRset. - */ -int kr_rule_local_address_del(const char *name, const char *addr, - bool use_nodata, kr_rule_tags_t tags) -{ - // Parse addr - if (!addr) - return kr_error(ENOSYS); - union kr_sockaddr ia; - if (parse_addr_str(&ia, addr) != 0) - return kr_error(EINVAL); - - // Remove the PTR - const knot_dname_t *reverse_key = addr2reverse(addr); - knot_rrset_t rrs; - knot_rrset_init(&rrs, /*const-cast*/(knot_dname_t *)reverse_key, - KNOT_RRTYPE_PTR, KNOT_CLASS_IN, 0); - int ret = kr_rule_local_data_del(&rrs, tags); - if (ret != 1) - VERBOSE_MSG(NULL, "del_pair PTR for %s; error: %s\n", addr, kr_strerror(ret)); - if (ret != 1 && ret != kr_error(ENOENT)) // ignore ENOENT for PTR (duplicities) - return ret; - - // Remove the forward entry - knot_dname_t key_buf[KNOT_DNAME_MAXLEN]; - rrs.owner = knot_dname_from_str(key_buf, name, sizeof(key_buf)); - if (!rrs.owner) - return kr_error(EINVAL); - rrs.type = ia.ip.sa_family == AF_INET6 ? KNOT_RRTYPE_AAAA : KNOT_RRTYPE_A; - ret = kr_rule_local_data_del(&rrs, tags); - if (ret != 1) - VERBOSE_MSG(NULL, "del_pair for %s; error: %s\n", name, kr_strerror(ret)); - - // Remove the NODATA entry; again, not perfect matching, - // but we don't care much about this dynamic hints API. - if (ret == 1 && use_nodata) { - rrs.type = KNOT_RRTYPE_CNAME; - ret = kr_rule_local_data_del(&rrs, tags); - if (ret != 1) - VERBOSE_MSG(NULL, "del_pair for NODATA %s; error: %s\n", - name, kr_strerror(ret)); - } - return ret < 0 ? ret : kr_ok(); -} - -int kr_rule_local_hosts(const char *path, bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) -{ - auto_fclose FILE *fp = fopen(path, "r"); - if (fp == NULL) { - ERR_MSG("reading '%s' failed: %s\n", path, strerror(errno)); - return kr_error(errno); - } else { - VERBOSE_MSG(NULL, "reading '%s'\n", path); - } - - /* Load file to map */ - size_t line_len_unused = 0; - size_t count = 0; - size_t line_count = 0; - auto_free char *line = NULL; - int ret = kr_ok(); - - while (getline(&line, &line_len_unused, fp) > 0) { - ++line_count; - /* Ingore #comments as described in man hosts.5 */ - char *comm = strchr(line, '#'); - if (comm) { - *comm = '\0'; - } - - char *saveptr = NULL; - const char *addr = strtok_r(line, " \t\n", &saveptr); - if (addr == NULL || strlen(addr) == 0) { - continue; - } - const char *canonical_name = strtok_r(NULL, " \t\n", &saveptr); - if (canonical_name == NULL) { - ret = kr_error(EINVAL); - goto error; - } - const char *name_tok; - while ((name_tok = strtok_r(NULL, " \t\n", &saveptr)) != NULL) { - ret = add_pair(name_tok, addr, use_nodata, ttl, tags); - if (ret) - goto error; - count += 1; - } - ret = add_pair(canonical_name, addr, use_nodata, ttl, tags); - if (!ret) // PTR only to the canonical name - ret = add_reverse_pair(canonical_name, addr, use_nodata, ttl, tags); - if (ret) - goto error; - count += 1; - } -error: - if (ret) { - ret = kr_error(ret); - ERR_MSG("%s:%zu: invalid syntax\n", path, line_count); - } - VERBOSE_MSG(NULL, "loaded %zu hints\n", count); - return ret; -} - static char* hint_add_hosts(void *env, struct kr_module *module, const char *args) { if (!args) @@ -293,13 +81,6 @@ static char* hint_add_hosts(void *env, struct kr_module *module, const char *arg return bool2jsonstr(err == kr_ok()); } -int kr_rule_local_address(const char *name, const char *addr, - bool use_nodata, uint32_t ttl, kr_rule_tags_t tags) -{ - int ret = add_reverse_pair(name, addr, use_nodata, ttl, KR_RULE_TAGS_ALL); - if (ret) return ret; - return add_pair(name, addr, use_nodata, ttl, KR_RULE_TAGS_ALL); -} /** * Set name => address hint. * |