1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include "lib/defines.h"
#include "lib/proto.h"
struct kr_query;
struct kr_request;
struct knot_pkt;
struct sockaddr;
#include <syslog.h>
#include <lib/utils.h>
#include <libknot/db/db.h>
/// Storage for a tag-set. It's a bitmap, so 64 tags are supported now.
typedef uint64_t kr_rule_tags_t;
#define KR_RULE_TAGS_ALL ((kr_rule_tags_t)0)
/// Tags "capacity", i.e. numbered from 0 to _CAP - 1.
#define KR_RULE_TAGS_CAP (sizeof(kr_rule_tags_t) * 8)
/// Extra options for a rule (not for forwarding)
struct kr_rule_opts {
/// Degree of severity for the rule; FIXME: granularity, defaults, etc.
uint8_t score : 4;
bool log_ip : 1, log_name : 1;
// +maybe log rule/QNAME/something
/// Log level: 0 = debug, 1 = info, ...
uint8_t log_level : 2;
/** Maybe 2 bits: (unset), blocked, censored, filtered
https://www.rfc-editor.org/rfc/rfc8914.html#name-extended-dns-error-code-15-
*/
uint8_t ede_code : 2;
/** Maybe 3 bits: (unset), Malware, Phishing, ... from
https://datatracker.ietf.org/doc/html/draft-ietf-dnsop-structured-dns-error#name-new-registry-for-dns-sub-er
*/
uint8_t ede_sub : 3;
};
typedef struct kr_rule_opts kr_rule_opts_t;
static_assert(sizeof(kr_rule_opts_t) == 2, "kr_rule_opts_t size changed unexpectedly");
/// Default opts; in particular used for the RFC-mandated special-use names
KR_EXPORT extern const kr_rule_opts_t KR_RULE_OPTS_DEFAULT;
enum { // Default minimal score of a rule to log/apply it.
KR_RULE_SCORE_LOG = 3,
KR_RULE_SCORE_APPLY = 6,
KR_RULE_SCORE_DEFAULT = 10,
};
static inline int map_log_level(uint8_t ll)
{
switch (ll) {
case 0: return LOG_DEBUG;
case 1: return LOG_INFO;
case 2: return LOG_NOTICE;
case 3: return LOG_WARNING;
}
return LOG_DEBUG; // shouldn't happen
}
/** Open the rule DB.
*
* You can call this to override the path or size (NULL/0 -> default)
* or choose not to overwrite the DB with just the defaults.
*
* \return error code. Not allowed if already open (EINVAL),
* so this optional call has to come before writing anything into the DB. */
KR_EXPORT
int kr_rules_init(const char *path, size_t maxsize, bool overwrite);
/** kr_rules_init() but OK if already open, and not allowing to override defaults. */
KR_EXPORT
int kr_rules_init_ensure(void);
KR_EXPORT
void kr_rules_deinit(void);
/** Commit or abort changes done to the rule DB so far.
*
* Normally commit happens only on successfully loading a config file.
* However, an advanced user may get in trouble e.g. if calling resolve() from there,
* causing even an assertion failure. In that case they might want to commit explicitly.
*
* If only read-only transaction is open, this will NOT reset it to the newest data.
*/
KR_EXPORT
int kr_rules_commit(bool accept);
/** Reset to the latest version of rules committed in the DB.
*
* Note that this is not always a good idea. For example, the `forward` rules
* now use data from both the DB and lua config, so reloading only the DB
* may lead to weird behavior in some cases.
* (Modifications will also do this, as you can only modify the latest DB.)
*/
KR_EXPORT
int kr_rules_reset(void);
/** Try answering the query from local data; WIP: otherwise determine data source overrides.
*
* \return kr_error() on errors, >0 if answered, 0 otherwise (also when forwarding)
*
* FIXME: we probably want to ensure AA flags in answer as appropriate.
* Perhaps approach it like AD? Tweak flags in ranked_rr_array_entry
* and at the end decide whether to set AA=1?
*/
int kr_rule_local_data_answer(struct kr_query *qry, struct knot_pkt *pkt);
/** Set up nameserver+cut if overridden by policy. \return kr_error() */
int kr_rule_data_src_check(struct kr_query *qry, struct knot_pkt *pkt);
/** Select the view action to perform.
*
* \param selected The action string from kr_view_insert_action()
* \return 0 or negative error code, in particular kr_error(ENOENT)
*/
KR_EXPORT
int kr_view_select_action(const struct kr_request *req, knot_db_val_t *selected);
/** Default TTL for answers from local data rules.
*
* This applies to rules defined by the user, not the default rules.
* Some types of rules save space when using this default.
* This definition exists mainly for usage from lua.
*/
KR_EXPORT extern
const uint32_t KR_RULE_TTL_DEFAULT;
/* APIs to modify the rule DB.
*
* FIXME:
* - overwriting semantics; often even the API docs is wrong here ATM
* - a way to read/modify a rule?
*/
/** Add a local data rule.
*
* Into the default rule-set ATM.
* Special NODATA case: use a type with zero records (TTL matters).
* You can do that either for particular types or for CNAME
* (meaning all types unspecified by other exact rule matches).
*/
KR_EXPORT
int kr_rule_local_data_ins(const knot_rrset_t *rrs, const knot_rdataset_t *sig_rds,
kr_rule_tags_t tags, kr_rule_opts_t opts);
/** Merge RRs into a local data rule.
*
* - FIXME: with multiple tags variants for the same name-type pair,
* you typically end up with a single RR per RRset
* - RRSIGs get dropped, if any were attached.
* - We assume that this is called with a RW transaction open already,
* which is always true in normal usage (long RW txn covering whole config).
* - TODO: what if opts don't match?
*/
KR_EXPORT
int kr_rule_local_data_merge(const knot_rrset_t *rrs, kr_rule_tags_t tags, kr_rule_opts_t opts);
/** 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, kr_rule_opts_t opts);
/** 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, kr_rule_opts_t opts);
/** Remove a local data rule.
*
* \return the number of deleted rules or error < 0
*
* TODO: some other matching than name+type? Currently `tags` is unused; match all types?
* (would be useful in del_pair)
*/
KR_EXPORT
int kr_rule_local_data_del(const knot_rrset_t *rrs, kr_rule_tags_t tags);
enum kr_rule_sub_t {
/// Empty zone, i.e. with SOA and NS
KR_RULE_SUB_EMPTY = 1,
/// NXDOMAIN for everything; TODO: SOA owner is hard.
KR_RULE_SUB_NXDOMAIN,
/// NODATA answers but not on exact name (e.g. it's similar to DNAME)
KR_RULE_SUB_NODATA,
/// Redirect: anything beneath has the same data as apex (except NS+SOA).
KR_RULE_SUB_REDIRECT,
/// Act similar to DNAME: rebase everything underneath by generated CNAMEs.
KR_RULE_SUB_DNAME,
};
/** Insert a simple sub-tree rule.
*
* - into the default rule-set
* - SOA and NS for generated answers aren't overridable.
* - type: you can't use _DNAME via this function; insert it by kr_rule_local_data_ins()
*/
KR_EXPORT
int kr_rule_local_subtree(const knot_dname_t *apex, enum kr_rule_sub_t type,
uint32_t ttl, kr_rule_tags_t tags, kr_rule_opts_t opts);
/** Insert a view action into the default ruleset.
*
* \param subnet String specifying a subnet, e.g. "192.168.0.0/16".
* \param dst_subnet String specifying a subnet to be matched by the destination address. (or empty/NULL)
* \param protos Set of transport protocols. (or 0 to always match)
* \param action Currently a string to execute, like in old policies, e.g. `policy.REFUSE`
*
* TODO: improve? (return code, warning, ...) Internal queries never get matched.
*
* The concept of chain actions isn't respected; at most one action is chosen.
* The winner needs to fulfill all conditions. Closer subnet match is preferred,
* but otherwise the priority is unspecified (it is deterministic, though).
*
* There's no detection of action rules that clash in this way,
* even if all conditions match exactly.
* TODO we might consider some overwriting semantics,
* but the additional conditions make that harder.
*/
KR_EXPORT
int kr_view_insert_action(const char *subnet, const char *dst_subnet,
kr_proto_set protos, const char *action);
/** Add a tag by name into a tag-set variable.
*
* It also ensures allocation of tag names in the DB, etc.
*/
KR_EXPORT
int kr_rule_tag_add(const char *tag, kr_rule_tags_t *tagset);
struct kr_rule_zonefile_config {
const char *filename; /// NULL if specifying input_str instead
const char *input_str; /// NULL if specifying filename instead
size_t input_len; /// 0 for strlen(input_str)
bool is_rpz; /// interpret either as RPZ or as plain RRsets
bool nodata; /// TODO: implement
kr_rule_tags_t tags; /// tag-set for the generated rule
const char *origin; /// NULL or zone origin if known
uint32_t ttl; /// default TTL
kr_rule_opts_t opts; /// options for these rules
};
/** Load rules from some zonefile format, e.g. RPZ. Code in ./zonefile.c */
KR_EXPORT
int kr_rule_zonefile(const struct kr_rule_zonefile_config *c);
/** Flatten an array of pointers to an array of union kr_sockaddr. (Lua helper) */
KR_EXPORT
void kr_rule_coalesce_targets(const struct sockaddr * targets[], void *data);
struct kr_rule_fwd_flags {
/// Beware of ABI: this struct is memcpy'd to/from rule DB.
bool
is_auth : 1,
is_tcp : 1, /// forced TCP; unused, not needed for DoT
is_nods : 1; /// disable local DNSSEC validation
};
typedef struct kr_rule_fwd_flags kr_rule_fwd_flags_t;
/** Insert/overwrite a forwarding rule.
*
* Into the default rule-set ATM.
* \param targets NULL-terminated array.
*
* For is_auth == true we only support address, e.g. not specifying port or %interface.
*/
KR_EXPORT
int kr_rule_forward(const knot_dname_t *apex, kr_rule_fwd_flags_t flags,
const struct sockaddr * targets[]);
|