From 9661b03fc3ec703b1ba5889e5bea6c038a553318 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Tue, 22 Oct 2024 19:44:16 +1100 Subject: [PATCH 01/23] Refactored ipset to support per-rule configuration --- ipset/ipset.c | 136 ++++++++++++----- ipset/ipset.h | 25 ++- util/config_file.c | 161 ++++++++++--------- util/config_file.h | 61 ++++---- util/configlexer.lex | 96 +++++++----- util/configparser.y | 357 ++++++++++++++++++------------------------- 6 files changed, 428 insertions(+), 408 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 1ad2c09f4..3dfe66227 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -16,6 +16,9 @@ #include "sldns/sbuffer.h" #include "sldns/wire2str.h" #include "sldns/parseutil.h" +#include +#include +#include #ifdef HAVE_NET_PFVAR_H #include @@ -139,10 +142,15 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, return 0; } #else -static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, + int af, time_t ttl) { + int result; + int seq; + unsigned int port_id; struct nlmsghdr *nlh; struct nfgenmsg *nfg; - struct nlattr *nested[2]; + struct nlattr *nested[3]; + char* recv_buffer; static char buffer[BUFF_LEN]; if (strlen(setname) >= IPSET_MAXNAMELEN) { @@ -157,6 +165,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, nlh = mnl_nlmsg_put_header(buffer); nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8); nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL; + nlh->nlmsg_seq = seq = time(NULL); nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); nfg->nfgen_family = af; @@ -170,11 +179,40 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, mnl_attr_put(nlh, (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr); mnl_attr_nest_end(nlh, nested[1]); + if (ttl >= 0) { + nested[2] = mnl_attr_nest_start(nlh, IPSET_ATTR_TIMEOUT); + mnl_attr_put(nlh, NLA_F_NET_BYTEORDER, sizeof(time_t), &ttl); + mnl_attr_nest_end(nlh, nested[2]); + } mnl_attr_nest_end(nlh, nested[0]); - if (mnl_socket_sendto(dev, nlh, nlh->nlmsg_len) < 0) { + if ((result = mnl_socket_sendto(dev, nlh, nlh->nlmsg_len)) < 0) { + log_err("ipset: failed to send netlink packet: %s", strerror(errno)); return -1; } + port_id = mnl_socket_get_portid(dev); + recv_buffer = (char*) calloc(MNL_SOCKET_BUFFER_SIZE, sizeof(char)); + if (!recv_buffer) { + log_err("ipset: failed to allocate receive buffer"); + return -1; + } + do { + result = mnl_socket_recvfrom(dev, recv_buffer, MNL_SOCKET_BUFFER_SIZE); + if (result < 0) { + log_err("ipset: failed to ACK netlink request: %s", strerror(errno)); + free(recv_buffer); + return -1; + } + result = mnl_cb_run(recv_buffer, result, seq, port_id, NULL, NULL); + if (result < 0) { + log_err("ipset: netlink response had error: %s", strerror(errno)); + free(recv_buffer); + return -1; + } else if (result == 0) { + break; + } + } while (result > 0); + free(recv_buffer); return 0; } #endif @@ -182,30 +220,48 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, static void ipset_add_rrset_data(struct ipset_env *ie, struct packed_rrset_data *d, const char* setname, int af, - const char* dname) + const char* dname, bool set_ttl) { int ret; size_t j, rr_len, rd_len; + time_t rr_ttl; uint8_t *rr_data; /* to d->count, not d->rrsig_count, because we do not want to add the RRSIGs, only the addresses */ for (j = 0; j < d->count; j++) { rr_len = d->rr_len[j]; rr_data = d->rr_data[j]; + rr_ttl = d->rr_ttl[j]; rd_len = sldns_read_uint16(rr_data); if(af == AF_INET && rd_len != INET_SIZE) continue; if(af == AF_INET6 && rd_len != INET6_SIZE) continue; + if (!set_ttl) { + rr_ttl = -1; + } if (rr_len - 2 >= rd_len) { if(verbosity >= VERB_QUERY) { char ip[128]; if(inet_ntop(af, rr_data+2, ip, (socklen_t)sizeof(ip)) == 0) snprintf(ip, sizeof(ip), "(inet_ntop_error)"); - verbose(VERB_QUERY, "ipset: add %s to %s for %s", ip, setname, dname); + // TODO: Remove the table argument from the config, it's not needed + if (set_ttl) { + verbose( + VERB_QUERY, + "ipset: add %s to %s for %s with ttl %lds", + ip, setname, dname, rr_ttl + ); + } else { + verbose( + VERB_QUERY, + "ipset: add %s to %s for %s", + ip, setname, dname + ); + } } - ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af); + ret = add_to_ipset((filter_dev)ie->dev, setname, rr_data + 2, af, rr_ttl); if (ret < 0) { log_err("ipset: could not add %s into %s", dname, setname); @@ -224,13 +280,13 @@ ipset_add_rrset_data(struct ipset_env *ie, static int ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, struct ub_packed_rrset_key *rrset, const char *qname, int qlen, - const char *setname, int af) + int af) { static char dname[BUFF_LEN]; const char *ds, *qs; int dlen, plen; - struct config_strlist *p; + struct config_str4list *p; struct packed_rrset_data *d; dlen = sldns_wire2str_dname_buf(rrset->rk.dname, rrset->rk.dname_len, dname, BUFF_LEN); @@ -252,19 +308,43 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, if (p->str[plen - 1] == '.') { plen--; } - + int set_af; + if (strncasecmp(p->str2, "ipv4", 4) == 0) { + set_af = AF_INET; + } else if (strncasecmp(p->str2, "ipv6", 4) == 0) { + set_af = AF_INET6; + } else { + continue; + } if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) { ds = dname + (dlen - plen); } if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) { qs = qname + (qlen - plen); } - if ((ds && strncasecmp(p->str, ds, plen) == 0) - || (qs && strncasecmp(p->str, qs, plen) == 0)) { + if (((ds && strncasecmp(p->str, ds, plen) == 0) + || (qs && strncasecmp(p->str, qs, plen) == 0)) + && set_af == af) { + verbose( + VERB_QUERY, + "ipset: match ((%s && %s == %s) || (%s && %s == %s)) && %d == %d", + ds ? "true" : "false", p->str, ds, + qs ? "true" : "false", p->str, qs, + set_af, af + ); d = (struct packed_rrset_data*)rrset->entry.data; - ipset_add_rrset_data(ie, d, setname, af, dname); + bool set_ttl = strncasecmp(p->str4, "ttl", 3) == 0; + ipset_add_rrset_data(ie, d, p->str3, af, dname, set_ttl); break; - } + } else { + verbose( + VERB_QUERY, + "ipset: no match ((%s && %s == %s) || (%s && %s == %s)) && %d == %d", + ds ? "true" : "false", p->str, ds, + qs ? "true" : "false", p->str, qs, + set_af, af + ); + } } return 0; } @@ -299,23 +379,16 @@ static int ipset_update(struct module_env *env, struct dns_msg *return_msg, } for(i = 0; i < return_msg->rep->rrset_count; i++) { - setname = NULL; rrset = return_msg->rep->rrsets[i]; - if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A && - ie->v4_enabled == 1) { + if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_A) { af = AF_INET; - setname = ie->name_v4; - } else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA && - ie->v6_enabled == 1) { + } else if(ntohs(rrset->rk.type) == LDNS_RR_TYPE_AAAA) { af = AF_INET6; - setname = ie->name_v6; } - if (setname) { - if(ipset_check_zones_for_rrset(env, ie, rrset, qname, - qlen, setname, af) == -1) - return -1; - } + if(ipset_check_zones_for_rrset(env, ie, rrset, qname, + qlen, af) == -1) + return -1; } return 0; @@ -368,19 +441,6 @@ void ipset_destartup(struct module_env* env, int id) { } int ipset_init(struct module_env* env, int id) { - struct ipset_env *ipset_env = env->modinfo[id]; - - ipset_env->name_v4 = env->cfg->ipset_name_v4; - ipset_env->name_v6 = env->cfg->ipset_name_v6; - - ipset_env->v4_enabled = !ipset_env->name_v4 || (strlen(ipset_env->name_v4) == 0) ? 0 : 1; - ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1; - - if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) { - log_err("ipset: set name no configuration?"); - return 0; - } - return 1; } diff --git a/ipset/ipset.h b/ipset/ipset.h index 195c7db93..a073e357a 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -3,6 +3,9 @@ * * Author: Kevin Chou * Email: k9982874@gmail.com + * + * Updated with per-zone support and TTLs. + * Author: Jack Kilrain (EngineersBox) */ #ifndef IPSET_H #define IPSET_H @@ -17,17 +20,13 @@ * and configure with --enable-ipset. And compile. Then enable the ipset * module in unbound.conf with module-config: "ipset validator iterator" * then create it with ipset -N blacklist iphash and then add - * local-zone: "example.com." ipset + * local-zone: "example.com." ipset * statements for the zones where you want the addresses of the names - * looked up added to the set. - * - * Set the name of the set with - * ipset: - * name-v4: "blacklist" - * name-v6: "blacklist6" - * in unbound.conf. The set can be used in this way: - * iptables -A INPUT -m set --set blacklist src -j DROP - * ip6tables -A INPUT -m set --set blacklist6 src -j DROP + * looked up added to specified table + set. Declaring the protocol as either + * "ipv4" or "ipv6" determines which address family to use from the RRSet + * when populating the ipset entry. Specifying ttl at the end will mark the + * ipset entry with a timeout (aka expiry) matching the RRSet TTL, specifying + * no-ttl will prevent setting the TTL on the set entry. */ #include "util/module.h" @@ -38,12 +37,6 @@ extern "C" { struct ipset_env { void* dev; - - int v4_enabled; - int v6_enabled; - - const char *name_v4; - const char *name_v6; }; struct ipset_qstate { diff --git a/util/config_file.c b/util/config_file.c index aca0039d4..d5c1b2c7e 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -135,12 +135,9 @@ config_create(void) cfg->http_query_buffer_size = 4*1024*1024; cfg->http_response_buffer_size = 4*1024*1024; cfg->http_nodelay = 1; - cfg->quic_port = UNBOUND_DNS_OVER_QUIC_PORT; - cfg->quic_size = 8*1024*1024; cfg->use_syslog = 1; cfg->log_identity = NULL; /* changed later with argv[0] */ cfg->log_time_ascii = 0; - cfg->log_time_iso = 0; cfg->log_queries = 0; cfg->log_replies = 0; cfg->log_tag_queryreply = 0; @@ -240,7 +237,6 @@ config_create(void) cfg->harden_short_bufsize = 1; cfg->harden_large_queries = 0; cfg->harden_glue = 1; - cfg->harden_unverified_glue = 0; cfg->harden_dnssec_stripped = 1; cfg->harden_below_nxdomain = 1; cfg->harden_referral_path = 0; @@ -402,21 +398,12 @@ config_create(void) cfg->redis_server_path = NULL; cfg->redis_server_password = NULL; cfg->redis_timeout = 100; - cfg->redis_command_timeout = 0; - cfg->redis_connect_timeout = 0; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; cfg->redis_logical_db = 0; #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ -#ifdef USE_IPSET - cfg->ipset_name_v4 = NULL; - cfg->ipset_name_v6 = NULL; -#endif cfg->ede = 0; - cfg->iter_scrub_ns = 20; - cfg->iter_scrub_cname = 11; - cfg->max_global_quota = 128; return cfg; error_exit: config_delete(cfg); @@ -550,9 +537,6 @@ int config_set_option(struct config_file* cfg, const char* opt, else if(strcmp(opt, "log-time-ascii:") == 0) { IS_YES_OR_NO; cfg->log_time_ascii = (strcmp(val, "yes") == 0); log_set_time_asc(cfg->log_time_ascii); } - else if(strcmp(opt, "log-time-iso:") == 0) - { IS_YES_OR_NO; cfg->log_time_iso = (strcmp(val, "yes") == 0); - log_set_time_iso(cfg->log_time_iso); } else S_SIZET_NONZERO("max-udp-size:", max_udp_size) else S_YNO("use-syslog:", use_syslog) else S_STR("log-identity:", log_identity) @@ -606,8 +590,6 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_MEMSIZE("http-response-buffer-size:", http_response_buffer_size) else S_YNO("http-nodelay:", http_nodelay) else S_YNO("http-notls-downstream:", http_notls_downstream) - else S_NUMBER_NONZERO("quic-port:", quic_port) - else S_MEMSIZE("quic-size:", quic_size) else S_YNO("interface-automatic:", if_automatic) else S_STR("interface-automatic-ports:", if_automatic_ports) else S_YNO("use-systemd:", use_systemd) @@ -686,7 +668,6 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_STRLIST("root-hints:", root_hints) else S_STR("target-fetch-policy:", target_fetch_policy) else S_YNO("harden-glue:", harden_glue) - else S_YNO("harden-unverified-glue:", harden_unverified_glue) else S_YNO("harden-short-bufsize:", harden_short_bufsize) else S_YNO("harden-large-queries:", harden_large_queries) else S_YNO("harden-dnssec-stripped:", harden_dnssec_stripped) @@ -727,17 +708,12 @@ int config_set_option(struct config_file* cfg, const char* opt, SERVE_EXPIRED = cfg->serve_expired; } else if(strcmp(opt, "serve-expired-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_ttl = atoi(val); SERVE_EXPIRED_TTL=(time_t)cfg->serve_expired_ttl;} - else if(strcmp(opt, "serve-expired-ttl-reset:") == 0) - { IS_YES_OR_NO; cfg->serve_expired_ttl_reset = (strcmp(val, "yes") == 0); - SERVE_EXPIRED_TTL_RESET = cfg->serve_expired_ttl_reset; } + else S_YNO("serve-expired-ttl-reset:", serve_expired_ttl_reset) else if(strcmp(opt, "serve-expired-reply-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;} else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout) else S_YNO("ede:", ede) else S_YNO("ede-serve-expired:", ede_serve_expired) - else S_NUMBER_OR_ZERO("iter-scrub-ns:", iter_scrub_ns) - else S_NUMBER_OR_ZERO("iter-scrub-cname:", iter_scrub_cname) - else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota) else S_YNO("serve-original-ttl:", serve_original_ttl) else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations) else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode) @@ -1074,7 +1050,6 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "use-syslog", use_syslog) else O_STR(opt, "log-identity", log_identity) else O_YNO(opt, "log-time-ascii", log_time_ascii) - else O_YNO(opt, "log-time-iso", log_time_iso) else O_DEC(opt, "num-threads", num_threads) else O_IFC(opt, "interface", num_ifs, ifs) else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs) @@ -1158,8 +1133,6 @@ config_get_option(struct config_file* cfg, const char* opt, else O_MEM(opt, "http-response-buffer-size", http_response_buffer_size) else O_YNO(opt, "http-nodelay", http_nodelay) else O_YNO(opt, "http-notls-downstream", http_notls_downstream) - else O_DEC(opt, "quic-port", quic_port) - else O_MEM(opt, "quic-size", quic_size) else O_YNO(opt, "use-systemd", use_systemd) else O_YNO(opt, "do-daemonize", do_daemonize) else O_STR(opt, "chroot", chrootdir) @@ -1185,7 +1158,6 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "harden-short-bufsize", harden_short_bufsize) else O_YNO(opt, "harden-large-queries", harden_large_queries) else O_YNO(opt, "harden-glue", harden_glue) - else O_YNO(opt, "harden-unverified-glue", harden_unverified_glue) else O_YNO(opt, "harden-dnssec-stripped", harden_dnssec_stripped) else O_YNO(opt, "harden-below-nxdomain", harden_below_nxdomain) else O_YNO(opt, "harden-referral-path", harden_referral_path) @@ -1210,9 +1182,6 @@ config_get_option(struct config_file* cfg, const char* opt, else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout) else O_YNO(opt, "ede", ede) else O_YNO(opt, "ede-serve-expired", ede_serve_expired) - else O_DEC(opt, "iter-scrub-ns", iter_scrub_ns) - else O_DEC(opt, "iter-scrub-cname", iter_scrub_cname) - else O_DEC(opt, "max-global-quota", max_global_quota) else O_YNO(opt, "serve-original-ttl", serve_original_ttl) else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations) else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode) @@ -1379,16 +1348,10 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "redis-server-path", redis_server_path) else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_timeout) - else O_DEC(opt, "redis-command-timeout", redis_command_timeout) - else O_DEC(opt, "redis-connect-timeout", redis_connect_timeout) else O_YNO(opt, "redis-expire-records", redis_expire_records) else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ -#ifdef USE_IPSET - else O_STR(opt, "name-v4", ipset_name_v4) - else O_STR(opt, "name-v6", ipset_name_v6) -#endif /* not here: * outgoing-permit, outgoing-avoid - have list of ports * local-zone - zones and nodefault variables @@ -1557,6 +1520,21 @@ config_deltrplstrlist(struct config_str3list* p) } } +void +config_delstr4list(struct config_str4list* p) +{ + struct config_str4list *np; + while(p) { + np = p->next; + free(p->str); + free(p->str2); + free(p->str3); + free(p->str4); + free(p); + p = np; + } +} + void config_delauth(struct config_auth* p) { @@ -1613,7 +1591,7 @@ config_delview(struct config_view* p) config_deldblstrlist(p->local_zones); config_delstrlist(p->local_zones_nodefault); #ifdef USE_IPSET - config_delstrlist(p->local_zones_ipset); + config_delstr4list(p->local_zones_ipset); #endif config_delstrlist(p->local_data); free(p); @@ -1711,7 +1689,7 @@ config_delete(struct config_file* cfg) config_deldblstrlist(cfg->local_zones); config_delstrlist(cfg->local_zones_nodefault); #ifdef USE_IPSET - config_delstrlist(cfg->local_zones_ipset); + config_delstr4list(cfg->local_zones_ipset); #endif config_delstrlist(cfg->local_data); config_deltrplstrlist(cfg->local_zone_overrides); @@ -1765,10 +1743,6 @@ config_delete(struct config_file* cfg) free(cfg->redis_server_password); #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ -#ifdef USE_IPSET - free(cfg->ipset_name_v4); - free(cfg->ipset_name_v6); -#endif free(cfg); } @@ -2130,6 +2104,25 @@ cfg_str3list_insert(struct config_str3list** head, char* item, char* i2, return 1; } +int +cfg_str4list_insert(struct config_str4list** head, char* item, char* i2, + char* i3, char* i4) +{ + struct config_str4list *s; + if(!item || !i2 || !i3 || !i4 || !head) + return 0; + s = (struct config_str4list*)calloc(1, sizeof(struct config_str4list)); + if(!s) + return 0; + s->str = item; + s->str2 = i2; + s->str3 = i3; + s->str4 = i4; + s->next = *head; + *head = s; + return 1; +} + int cfg_strbytelist_insert(struct config_strbytelist** head, char* item, uint8_t* i2, size_t i2len) @@ -2352,7 +2345,7 @@ uint8_t* cfg_parse_nsid(const char* str, uint16_t* nsid_len) uint8_t *dp; for ( ch = str, dp = nsid - ; isxdigit((unsigned char)ch[0]) && isxdigit((unsigned char)ch[1]) + ; isxdigit(ch[0]) && isxdigit(ch[1]) ; ch += 2, dp++) { *dp = (uint8_t)sldns_hexdigit_to_int(ch[0]) * 16; *dp += (uint8_t)sldns_hexdigit_to_int(ch[1]); @@ -2408,7 +2401,6 @@ config_apply(struct config_file* config) MIN_TTL = (time_t)config->min_ttl; SERVE_EXPIRED = config->serve_expired; SERVE_EXPIRED_TTL = (time_t)config->serve_expired_ttl; - SERVE_EXPIRED_TTL_RESET = config->serve_expired_ttl_reset; SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl; SERVE_ORIGINAL_TTL = config->serve_original_ttl; MAX_NEG_TTL = (time_t)config->max_negative_ttl; @@ -2419,12 +2411,10 @@ config_apply(struct config_file* config) MINIMAL_RESPONSES = config->minimal_responses; RRSET_ROUNDROBIN = config->rrset_roundrobin; LOG_TAG_QUERYREPLY = config->log_tag_queryreply; - MAX_GLOBAL_QUOTA = config->max_global_quota; UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit; USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT; BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4; log_set_time_asc(config->log_time_ascii); - log_set_time_iso(config->log_time_iso); autr_permit_small_holddown = config->permit_small_holddown; stream_wait_max = config->stream_wait_size; http2_query_buffer_max = config->http_query_buffer_size; @@ -2574,14 +2564,27 @@ static char* last_space_pos(const char* str) return (sp>tab)?sp:tab; } +static int get_next_token(const char* str, char** start, char** end) { + while(*start && **start && isspace((unsigned char)*str)) + start++; + if(!*start || !**start) { + return 1; + } + *end = next_space_pos(*start); + if (!*end || !**end) { + return 2; + } + return 0; +} + int cfg_parse_local_zone(struct config_file* cfg, const char* val) { - const char *type, *name_end, *name; + char *type, *type_end, *name_end, *name; char buf[256]; /* parse it as: [zone_name] [between stuff] [zone_type] */ - name = val; + name = (char*) val; while(*name && isspace((unsigned char)*name)) name++; if(!*name) { @@ -2599,7 +2602,39 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) } (void)strlcpy(buf, name, sizeof(buf)); buf[name_end-name] = '\0'; - +#ifdef USE_IPSET + type = name_end; + int result = get_next_token(name_end, &type, &type_end); + if (result == 1) { + log_err("syntax error: expected zone type: %s", val); + } else if (result == 0 && strcmp(type, "ipset") == 0) { + char *protocol, *protocol_end, *ip_table, *ip_table_end, + *ipset_name, *ipset_name_end, *ttl; + protocol = type_end; + if (get_next_token(type_end, &protocol, &protocol_end)) { + log_err("syntax error: expected ipset zone protocol: %s", val); + return 0; + } + ipset_name = protocol_end; + if (get_next_token(ip_table_end, &ipset_name, &ipset_name_end)) { + log_err("syntax error: expected ipset zone set name: %s", val); + return 0; + } + ttl = last_space_pos(ipset_name_end); + while(ttl && *ttl && isspace((unsigned char)*ttl)) + ttl++; + if(!ttl || !*ttl) { + log_err("syntax error: expected ipset zone ttl: %s", val); + return 0; + } + return cfg_str4list_insert(&cfg->local_zones_ipset, + strdup(name), strdup(protocol), + strdup(ipset_name), strdup(ttl)); + } + /* There was no next-space after this token, so it must be final + * and as such we don't have enough tokens*/ +#endif + type = last_space_pos(name_end); while(type && *type && isspace((unsigned char)*type)) type++; @@ -2611,11 +2646,6 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) if(strcmp(type, "nodefault")==0) { return cfg_strlist_insert(&cfg->local_zones_nodefault, strdup(name)); -#ifdef USE_IPSET - } else if(strcmp(type, "ipset")==0) { - return cfg_strlist_insert(&cfg->local_zones_ipset, - strdup(name)); -#endif } else { return cfg_str2list_insert(&cfg->local_zones, strdup(buf), strdup(type)); @@ -2827,22 +2857,3 @@ if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port) return 0; #endif } - -/** see if interface is quic, its port number == the quic port number */ -int -if_is_quic(const char* ifname, const char* port, int quic_port) -{ -#ifndef HAVE_NGTCP2 - (void)ifname; - (void)port; - (void)quic_port; - return 0; -#else - char* p = strchr(ifname, '@'); - if(!p && atoi(port) == quic_port) - return 1; - if(p && atoi(p+1) == quic_port) - return 1; - return 0; -#endif -} diff --git a/util/config_file.h b/util/config_file.h index 2969f8433..5f577d256 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -48,6 +48,7 @@ struct config_view; struct config_strlist; struct config_str2list; struct config_str3list; +struct config_str4list; struct config_strbytelist; struct module_qstate; struct sock_list; @@ -161,11 +162,6 @@ struct config_file { /** Disable TLS for http sockets downstream */ int http_notls_downstream; - /** port on which to provide DNS over QUIC service */ - int quic_port; - /** size of the quic data, max bytes */ - size_t quic_size; - /** outgoing port range number of ports (per thread) */ int outgoing_num_ports; /** number of outgoing tcp buffers per (per thread) */ @@ -293,8 +289,6 @@ struct config_file { int harden_large_queries; /** harden against spoofed glue (out of zone data) */ int harden_glue; - /** harden against unverified glue */ - int harden_unverified_glue; /** harden against receiving no DNSSEC data for trust anchor */ int harden_dnssec_stripped; /** harden against queries that fall under known nxdomain names */ @@ -346,8 +340,6 @@ struct config_file { int use_syslog; /** log timestamp in ascii UTC */ int log_time_ascii; - /** log timestamp in ISO8601 format */ - int log_time_iso; /** log queries with one line per query */ int log_queries; /** log replies with one line per reply */ @@ -465,7 +457,7 @@ struct config_file { struct config_strlist* local_zones_nodefault; #ifdef USE_IPSET /** local zones ipset list */ - struct config_strlist* local_zones_ipset; + struct config_str4list* local_zones_ipset; #endif /** do not add any default local zone */ int local_zones_disable_default; @@ -746,10 +738,6 @@ struct config_file { char* redis_server_password; /** timeout (in ms) for communication with the redis server */ int redis_timeout; - /** timeout (in ms) for redis commands */ - int redis_command_timeout; - /** timeout (in ms) for redis connection set up */ - int redis_connect_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; /** set the redis logical database upon connection */ @@ -765,20 +753,8 @@ struct config_file { size_t cookie_secret_len; /** path to cookie secret store */ char* cookie_secret_file; - - /* ipset module */ -#ifdef USE_IPSET - char* ipset_name_v4; - char* ipset_name_v6; -#endif /** respond with Extended DNS Errors (RFC8914) */ int ede; - /** limit on NS RRs in RRset for the iterator scrubber. */ - size_t iter_scrub_ns; - /** limit on CNAME, DNAME RRs in answer for the iterator scrubber. */ - int iter_scrub_cname; - /** limit on upstream queries for an incoming query and subqueries. */ - int max_global_quota; }; /** from cfg username, after daemonize setup performed */ @@ -880,7 +856,7 @@ struct config_view { struct config_strlist* local_zones_nodefault; #ifdef USE_IPSET /** local zones ipset list */ - struct config_strlist* local_zones_ipset; + struct config_str4list* local_zones_ipset; #endif /** Fallback to global local_zones when there is no match in the view * view specific tree. 1 for yes, 0 for no */ @@ -927,6 +903,21 @@ struct config_str3list { char* str3; }; +struct config_str4list { + /** next item in list */ + struct config_str4list* next; + /** first string */ + char* str; + /** second string */ + char* str2; + /** third string */ + char* str3; + /** fourth string */ + char* str4; + /** fifth string */ + char* str5; +}; + /** * List of string, bytestring for config options @@ -1106,6 +1097,18 @@ int cfg_str2list_insert(struct config_str2list** head, char* item, char* i2); int cfg_str3list_insert(struct config_str3list** head, char* item, char* i2, char* i3); +/** + * Insert string into str4list. + * @param head: pointer to str4list head variable. + * @param item: new item. malloced by caller. If NULL the insertion fails. + * @param i2: 2nd string, malloced by caller. If NULL the insertion fails. + * @param i3: 3rd string, malloced by caller. If NULL the insertion fails. + * @param i4: 4th string, malloced by caller. If NULL the insertion fails. + * @return: true on success. + */ +int cfg_str4list_insert(struct config_str4list** head, char* item, char* i2, + char* i3, char* i4); + /** * Insert string into strbytelist. * @param head: pointer to strbytelist head variable. @@ -1411,10 +1414,6 @@ int if_is_pp2(const char* ifname, const char* port, /** see if interface is DNSCRYPT, its port number == the dnscrypt port number */ int if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port); - -/** see if interface is quic, its port number == the quic port number */ -int if_is_quic(const char* ifname, const char* port, int quic_port); - #ifdef USE_LINUX_IP_LOCAL_PORT_RANGE #define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range" #endif diff --git a/util/configlexer.lex b/util/configlexer.lex index 4c0416f73..ed65913c7 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -32,14 +32,19 @@ void ub_c_error(const char *message); /** avoid warning in about fwrite return value */ #define ECHO ub_c_error_msg("syntax error at text: %s", yytext) -/** A parser variable, this is a statement in the config file which is - * of the form variable: value1 value2 ... nargs is the number of values. */ -#define YDVAR(nargs, var) \ - num_args=(nargs); \ - LEXOUT(("v(%s%d) ", yytext, num_args)); \ +/* A parser variable of variable argument count in the range [min, max] in + * the config of the form: value1 value 2 ... */ +#define YDVARMM(nargs_min, nargs_max, var) \ + num_args=(nargs_min); \ + num_args_max=(nargs_max); \ + LEXOUT(("v(%s%d-%d) ", yytext, num_args, num_args_max)); \ if(num_args > 0) { BEGIN(val); } \ return (var); +/** A parser variable, this is a statement in the config file which is + * of the form variable: value1 value2 ... nargs is the number of values. */ +#define YDVAR(nargs, var) YDVARMM(nargs, nargs, var) + struct inc_state { char* filename; int line; @@ -51,6 +56,7 @@ static struct inc_state* config_include_stack = NULL; static int inc_depth = 0; static int inc_prev = 0; static int num_args = 0; +static int num_args_max = 0; static int inc_toplevel = 0; void init_cfg_parse(void) @@ -184,6 +190,22 @@ static void config_end_include(void) } #endif +#define ENSURE_VARARG_CONSISTENCY \ + if (num_args == 0 && num_args_max > 0) { \ + num_args = num_args_max; \ + } \ + num_args_max--; \ + if(--num_args == 0) { \ + if (num_args_max > 0) { \ + LEXOUT(("ARGC(0,%d) ",num_args_max)); \ + BEGIN(val); \ + } else { \ + BEGIN(INITIAL); \ + } \ + } else { \ + BEGIN(val); \ + } + %} %option noinput %option nounput @@ -269,8 +291,6 @@ http-query-buffer-size{COLON} { YDVAR(1, VAR_HTTP_QUERY_BUFFER_SIZE) } http-response-buffer-size{COLON} { YDVAR(1, VAR_HTTP_RESPONSE_BUFFER_SIZE) } http-nodelay{COLON} { YDVAR(1, VAR_HTTP_NODELAY) } http-notls-downstream{COLON} { YDVAR(1, VAR_HTTP_NOTLS_DOWNSTREAM) } -quic-port{COLON} { YDVAR(1, VAR_QUIC_PORT) } -quic-size{COLON} { YDVAR(1, VAR_QUIC_SIZE) } use-systemd{COLON} { YDVAR(1, VAR_USE_SYSTEMD) } do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } interface{COLON} { YDVAR(1, VAR_INTERFACE) } @@ -317,7 +337,6 @@ target-fetch-policy{COLON} { YDVAR(1, VAR_TARGET_FETCH_POLICY) } harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) } harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) } harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) } -harden-unverified-glue{COLON} { YDVAR(1, VAR_HARDEN_UNVERIFIED_GLUE) } harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) } harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) } harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) } @@ -433,14 +452,13 @@ permit-small-holddown{COLON} { YDVAR(1, VAR_PERMIT_SMALL_HOLDDOWN) } use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) } log-identity{COLON} { YDVAR(1, VAR_LOG_IDENTITY) } log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) } -log-time-iso{COLON} { YDVAR(1, VAR_LOG_TIME_ISO) } log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) } log-replies{COLON} { YDVAR(1, VAR_LOG_REPLIES) } log-tag-queryreply{COLON} { YDVAR(1, VAR_LOG_TAG_QUERYREPLY) } log-local-actions{COLON} { YDVAR(1, VAR_LOG_LOCAL_ACTIONS) } log-servfail{COLON} { YDVAR(1, VAR_LOG_SERVFAIL) } log-destaddr{COLON} { YDVAR(1, VAR_LOG_DESTADDR) } -local-zone{COLON} { YDVAR(2, VAR_LOCAL_ZONE) } +local-zone{COLON} { YDVARMM(2, 5, VAR_LOCAL_ZONE) } local-data{COLON} { YDVAR(1, VAR_LOCAL_DATA) } local-data-ptr{COLON} { YDVAR(1, VAR_LOCAL_DATA_PTR) } unblock-lan-zones{COLON} { YDVAR(1, VAR_UNBLOCK_LAN_ZONES) } @@ -517,7 +535,7 @@ dnstap-log-forwarder-query-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) } dnstap-log-forwarder-response-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) } -dnstap-sample-rate{COLON} { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } +dnstap-sample-rate { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) } ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) } ip-ratelimit-cookie{COLON} { YDVAR(1, VAR_IP_RATELIMIT_COOKIE) } @@ -577,13 +595,8 @@ redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } -redis-command-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) } -redis-connect-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } -ipset{COLON} { YDVAR(0, VAR_IPSET) } -name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) } -name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) } udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) } tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) } answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) } @@ -594,26 +607,34 @@ edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) } nsid{COLON} { YDVAR(1, VAR_NSID ) } ede{COLON} { YDVAR(1, VAR_EDE ) } proxy-protocol-port{COLON} { YDVAR(1, VAR_PROXY_PROTOCOL_PORT) } -iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) } -iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) } -max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } -{NEWLINE} { LEXOUT(("NL\n")); cfg_parser->line++; } +{NEWLINE} { + LEXOUT(("NL(%d,%d)\n", num_args, num_args_max)); + if (num_args == 0 && num_args_max > 0) { + /* Early match a set of tokens between the min and max */ + num_args = 0; + num_args_max = 0; + /* Return newline characters to stream for matching */ + /* yyless(0);*/ + LEXOUT(("TOKENS: %s\n", yytext)); + BEGIN(INITIAL); + } else { + cfg_parser->line++; + } +} /* Quoted strings. Strip leading and ending quotes */ \" { BEGIN(quotedstring); LEXOUT(("QS ")); } <> { - yyerror("EOF inside quoted string"); - if(--num_args == 0) { BEGIN(INITIAL); } - else { BEGIN(val); } + yyerror("EOF inside quoted string"); + ENSURE_VARARG_CONSISTENCY } {DQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } {NEWLINE} { yyerror("newline inside quoted string, no end \""); cfg_parser->line++; BEGIN(INITIAL); } \" { - LEXOUT(("QE ")); - if(--num_args == 0) { BEGIN(INITIAL); } - else { BEGIN(val); } - yytext[yyleng - 1] = '\0'; + LEXOUT(("QE ")); + ENSURE_VARARG_CONSISTENCY + yytext[yyleng - 1] = '\0'; yylval.str = strdup(yytext); if(!yylval.str) yyerror("out of memory"); @@ -623,18 +644,16 @@ max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } /* Single Quoted strings. Strip leading and ending quotes */ \' { BEGIN(singlequotedstr); LEXOUT(("SQS ")); } <> { - yyerror("EOF inside quoted string"); - if(--num_args == 0) { BEGIN(INITIAL); } - else { BEGIN(val); } + yyerror("EOF inside quoted string"); + ENSURE_VARARG_CONSISTENCY } {SQANY}* { LEXOUT(("STR(%s) ", yytext)); yymore(); } {NEWLINE} { yyerror("newline inside quoted string, no end '"); cfg_parser->line++; BEGIN(INITIAL); } \' { - LEXOUT(("SQE ")); - if(--num_args == 0) { BEGIN(INITIAL); } - else { BEGIN(val); } - yytext[yyleng - 1] = '\0'; + LEXOUT(("SQE ")); + ENSURE_VARARG_CONSISTENCY + yytext[yyleng - 1] = '\0'; yylval.str = strdup(yytext); if(!yylval.str) yyerror("out of memory"); @@ -716,9 +735,12 @@ max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } return (VAR_FORCE_TOPLEVEL); } -{UNQUOTEDLETTER}* { LEXOUT(("unquotedstr(%s) ", yytext)); - if(--num_args == 0) { BEGIN(INITIAL); } - yylval.str = strdup(yytext); return STRING_ARG; } +{UNQUOTEDLETTER}* { + LEXOUT(("unquotedstr(%s) ", yytext)); + ENSURE_VARARG_CONSISTENCY + yylval.str = strdup(yytext); + return STRING_ARG; +} {UNQUOTEDLETTER_NOCOLON}* { ub_c_error_msg("unknown keyword '%s'", yytext); diff --git a/util/configparser.y b/util/configparser.y index c10a5f475..e017b81d9 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -182,7 +182,6 @@ extern struct config_parser_state* cfg_parser; %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD %token VAR_CACHEDB_REDISLOGICALDB -%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISCONNECTTIMEOUT %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL @@ -194,7 +193,6 @@ extern struct config_parser_state* cfg_parser; %token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE %token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI -%token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE %token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG VAR_RPZ_LOG_NAME %token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_STRING @@ -203,22 +201,19 @@ extern struct config_parser_state* cfg_parser; %token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA -%token VAR_QUIC_PORT VAR_QUIC_SIZE %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO %token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE %token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED -%token VAR_COOKIE_SECRET_FILE VAR_ITER_SCRUB_NS VAR_ITER_SCRUB_CNAME -%token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO +%token VAR_COOKIE_SECRET_FILE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; -toplevelvar: serverstart contents_server | stub_clause | - forward_clause | pythonstart contents_py | - rcstart contents_rc | dtstart contents_dt | view_clause | +toplevelvar: serverstart contents_server | stubstart contents_stub | + forwardstart contents_forward | pythonstart contents_py | + rcstart contents_rc | dtstart contents_dt | viewstart contents_view | dnscstart contents_dnsc | cachedbstart contents_cachedb | - ipsetstart contents_ipset | authstart contents_auth | - rpzstart contents_rpz | dynlibstart contents_dl | - force_toplevel + authstart contents_auth | rpzstart contents_rpz | + dynlibstart contents_dl | force_toplevel ; force_toplevel: VAR_FORCE_TOPLEVEL { @@ -343,21 +338,10 @@ content_server: server_num_threads | server_verbosity | server_port | server_edns_client_string_opcode | server_nsid | server_zonemd_permissive_mode | server_max_reuse_tcp_queries | server_tcp_reuse_timeout | server_tcp_auth_query_timeout | - server_quic_port | server_quic_size | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | server_harden_unknown_additional | server_disable_edns_do | - server_log_destaddr | server_cookie_secret_file | - server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota | - server_harden_unverified_glue | server_log_time_iso - ; -stub_clause: stubstart contents_stub - { - /* stub end */ - if(cfg_parser->cfg->stubs && - !cfg_parser->cfg->stubs->name) - yyerror("stub-zone without name"); - } + server_log_destaddr | server_cookie_secret_file ; stubstart: VAR_STUB_ZONE { @@ -373,19 +357,17 @@ stubstart: VAR_STUB_ZONE } } ; -contents_stub: contents_stub content_stub - | ; +contents_stub: content_stub contents_stub + | + { + /* stub end */ + if(cfg_parser->cfg->stubs && + !cfg_parser->cfg->stubs->name) + yyerror("stub-zone without name"); + }; content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first | stub_no_cache | stub_ssl_upstream | stub_tcp_upstream ; -forward_clause: forwardstart contents_forward - { - /* forward end */ - if(cfg_parser->cfg->forwards && - !cfg_parser->cfg->forwards->name) - yyerror("forward-zone without name"); - } - ; forwardstart: VAR_FORWARD_ZONE { struct config_stub* s; @@ -400,19 +382,17 @@ forwardstart: VAR_FORWARD_ZONE } } ; -contents_forward: contents_forward content_forward - | ; +contents_forward: content_forward contents_forward + | + { + /* forward end */ + if(cfg_parser->cfg->forwards && + !cfg_parser->cfg->forwards->name) + yyerror("forward-zone without name"); + }; content_forward: forward_name | forward_host | forward_addr | forward_first | forward_no_cache | forward_ssl_upstream | forward_tcp_upstream ; -view_clause: viewstart contents_view - { - /* view end */ - if(cfg_parser->cfg->views && - !cfg_parser->cfg->views->name) - yyerror("view without name"); - } - ; viewstart: VAR_VIEW { struct config_view* s; @@ -427,8 +407,14 @@ viewstart: VAR_VIEW } } ; -contents_view: contents_view content_view - | ; +contents_view: content_view contents_view + | + { + /* view end */ + if(cfg_parser->cfg->views && + !cfg_parser->cfg->views->name) + yyerror("view without name"); + }; content_view: view_name | view_local_zone | view_local_data | view_first | view_response_ip | view_response_ip_data | view_local_data_ptr ; @@ -1211,26 +1197,6 @@ server_http_notls_downstream: VAR_HTTP_NOTLS_DOWNSTREAM STRING_ARG else cfg_parser->cfg->http_notls_downstream = (strcmp($2, "yes")==0); free($2); }; -server_quic_port: VAR_QUIC_PORT STRING_ARG - { - OUTYY(("P(server_quic_port:%s)\n", $2)); -#ifndef HAVE_NGTCP2 - log_warn("%s:%d: Unbound is not compiled with " - "ngtcp2. This is required to use DNS " - "over QUIC.", cfg_parser->filename, cfg_parser->line); -#endif - if(atoi($2) == 0) - yyerror("port number expected"); - else cfg_parser->cfg->quic_port = atoi($2); - free($2); - }; -server_quic_size: VAR_QUIC_SIZE STRING_ARG - { - OUTYY(("P(server_quic_size:%s)\n", $2)); - if(!cfg_parse_memsize($2, &cfg_parser->cfg->quic_size)) - yyerror("memory size expected"); - free($2); - }; server_use_systemd: VAR_USE_SYSTEMD STRING_ARG { OUTYY(("P(server_use_systemd:%s)\n", $2)); @@ -1272,15 +1238,6 @@ server_log_time_ascii: VAR_LOG_TIME_ASCII STRING_ARG free($2); } ; -server_log_time_iso: VAR_LOG_TIME_ISO STRING_ARG - { - OUTYY(("P(server_log_time_iso:%s)\n", $2)); - if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) - yyerror("expected yes or no."); - else cfg_parser->cfg->log_time_iso = (strcmp($2, "yes")==0); - free($2); - } - ; server_log_queries: VAR_LOG_QUERIES STRING_ARG { OUTYY(("P(server_log_queries:%s)\n", $2)); @@ -1846,16 +1803,6 @@ server_harden_glue: VAR_HARDEN_GLUE STRING_ARG free($2); } ; -server_harden_unverified_glue: VAR_HARDEN_UNVERIFIED_GLUE STRING_ARG - { - OUTYY(("P(server_harden_unverified_glue:%s)\n", $2)); - if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) - yyerror("expected yes or no."); - else cfg_parser->cfg->harden_unverified_glue = - (strcmp($2, "yes")==0); - free($2); - } - ; server_harden_dnssec_stripped: VAR_HARDEN_DNSSEC_STRIPPED STRING_ARG { OUTYY(("P(server_harden_dnssec_stripped:%s)\n", $2)); @@ -2377,22 +2324,11 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG local_zones_nodefault, $2)) fatal_exit("out of memory adding local-zone"); free($3); -#ifdef USE_IPSET - } else if(strcmp($3, "ipset")==0) { - size_t len = strlen($2); - /* Make sure to add the trailing dot. - * These are str compared to domain names. */ - if($2[len-1] != '.') { - if(!($2 = realloc($2, len+2))) { - fatal_exit("out of memory adding local-zone"); - } - $2[len] = '.'; - $2[len+1] = 0; - } - if(!cfg_strlist_insert(&cfg_parser->cfg-> - local_zones_ipset, $2)) - fatal_exit("out of memory adding local-zone"); - free($3); +#ifdef USE_IPSET + } else if (strcmp($3, "ipset") == 0) { + /* Transaprent pass-through to 5-param variant */ + free($2); + free($3); #endif } else { if(!cfg_str2list_insert(&cfg_parser->cfg->local_zones, @@ -2400,6 +2336,59 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG fatal_exit("out of memory adding local-zone"); } } + | VAR_LOCAL_ZONE STRING_ARG STRING_ARG STRING_ARG STRING_ARG STRING_ARG + { + OUTYY(("P(server_local_zone: %s %s %s %s %s)\n", $2, $3, $4, $5, $6)); + if (strcmp($3, "ipset") != 0) { + yyerror("local-zone type: expected static, deny, " + "refuse, redirect, transparent, " + "typetransparent, inform, inform_deny, " + "inform_redirect, always_transparent, block_a," + "always_refuse, always_nxdomain, " + "always_nodata, always_deny, always_null, " + "noview, nodefault or ipset"); + free($2); + free($3); + free($4); + free($5); + free($6); +#ifdef USE_IPSET + } else if (strcmp($3, "ipset") == 0) { + /* Format: ipset */ + if (strncmp($6, "ttl", 3) != 0 + && strncmp($6, "no-ttl", 6) != 0) { + yyerror("local-zone with ipset expected ttl/no-ttl"); + free($2); + free($3); + free($4); + free($5); + free($6); + } else { + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if ($2[len-1] != '.') { + if (!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } + if(!cfg_str4list_insert(&cfg_parser->cfg-> + local_zones_ipset, $2, $4, $5, $6)) + fatal_exit("out of memory adding local-zone"); + free($3); + } +#endif + } else { + yyerror("local-zone: too many parameters"); + free($2); + free($3); + free($4); + free($5); + free($6); + } + } ; server_local_data: VAR_LOCAL_DATA STRING_ARG { @@ -3339,22 +3328,9 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG local_zones_nodefault, $2)) fatal_exit("out of memory adding local-zone"); free($3); -#ifdef USE_IPSET - } else if(strcmp($3, "ipset")==0) { - size_t len = strlen($2); - /* Make sure to add the trailing dot. - * These are str compared to domain names. */ - if($2[len-1] != '.') { - if(!($2 = realloc($2, len+2))) { - fatal_exit("out of memory adding local-zone"); - } - $2[len] = '.'; - $2[len+1] = 0; - } - if(!cfg_strlist_insert(&cfg_parser->cfg->views-> - local_zones_ipset, $2)) - fatal_exit("out of memory adding local-zone"); - free($3); +#ifdef USE_IPSET + } else if (strcmp($3, "ipset") == 0) { + /* Transaprent pass-through to 5-param variant */ #endif } else { if(!cfg_str2list_insert( @@ -3363,6 +3339,59 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG fatal_exit("out of memory adding local-zone"); } } + | VAR_LOCAL_ZONE STRING_ARG STRING_ARG STRING_ARG STRING_ARG STRING_ARG + { + OUTYY(("P(server_local_zone: %s %s %s %s %s)\n", $2, $3, $4, $5, $6)); + if (strcmp($3, "ipset") != 0) { + yyerror("local-zone type: expected static, deny, " + "refuse, redirect, transparent, " + "typetransparent, inform, inform_deny, " + "inform_redirect, always_transparent, " + "always_refuse, always_nxdomain, " + "always_nodata, always_deny, always_null, " + "noview, nodefault or ipset"); + free($2); + free($3); + free($4); + free($5); + free($6); +#ifdef USE_IPSET + } else if (strcmp($3, "ipset") == 0) { + /* Format: ipset
*/ + if (strncmp($6, "ttl", 3) != 0 + || strncmp($6, "no-ttl", 6) != 0) { + yyerror("local-zone with ipset expected ttl/no-ttl"); + free($2); + free($3); + free($4); + free($5); + free($6); + } else { + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if ($2[len-1] != '.') { + if (!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } + if(!cfg_str4list_insert(&cfg_parser->cfg->views-> + local_zones_ipset, $2, $4, $5, $6)) + fatal_exit("out of memory adding local-zone"); + free($3); + } +#endif + } else { + yyerror("local-zone: too many parameters"); + free($2); + free($3); + free($4); + free($5); + free($6); + } + } ; view_response_ip: VAR_RESPONSE_IP STRING_ARG STRING_ARG { @@ -3870,8 +3899,7 @@ contents_cachedb: contents_cachedb content_cachedb content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | redis_expire_records | redis_server_path | redis_server_password | - cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired | - redis_command_timeout | redis_connect_timeout + cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3987,32 +4015,6 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG free($2); } ; -redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG - { - #if defined(USE_CACHEDB) && defined(USE_REDIS) - OUTYY(("P(redis_command_timeout:%s)\n", $2)); - if(atoi($2) == 0 && strcmp($2, "0") != 0) - yyerror("redis command timeout value expected"); - else cfg_parser->cfg->redis_command_timeout = atoi($2); - #else - OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); - #endif - free($2); - } - ; -redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG - { - #if defined(USE_CACHEDB) && defined(USE_REDIS) - OUTYY(("P(redis_connect_timeout:%s)\n", $2)); - if(atoi($2) == 0 && strcmp($2, "0") != 0) - yyerror("redis connect timeout value expected"); - else cfg_parser->cfg->redis_connect_timeout = atoi($2); - #else - OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); - #endif - free($2); - } - ; redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS) @@ -4084,73 +4086,6 @@ server_cookie_secret_file: VAR_COOKIE_SECRET_FILE STRING_ARG cfg_parser->cfg->cookie_secret_file = $2; } ; -server_iter_scrub_ns: VAR_ITER_SCRUB_NS STRING_ARG - { - OUTYY(("P(server_iter_scrub_ns:%s)\n", $2)); - if(atoi($2) == 0 && strcmp($2, "0") != 0) - yyerror("number expected"); - else cfg_parser->cfg->iter_scrub_ns = atoi($2); - free($2); - } - ; -server_iter_scrub_cname: VAR_ITER_SCRUB_CNAME STRING_ARG - { - OUTYY(("P(server_iter_scrub_cname:%s)\n", $2)); - if(atoi($2) == 0 && strcmp($2, "0") != 0) - yyerror("number expected"); - else cfg_parser->cfg->iter_scrub_cname = atoi($2); - free($2); - } - ; -server_max_global_quota: VAR_MAX_GLOBAL_QUOTA STRING_ARG - { - OUTYY(("P(server_max_global_quota:%s)\n", $2)); - if(atoi($2) == 0 && strcmp($2, "0") != 0) - yyerror("number expected"); - else cfg_parser->cfg->max_global_quota = atoi($2); - free($2); - } - ; -ipsetstart: VAR_IPSET - { - OUTYY(("\nP(ipset:)\n")); - cfg_parser->started_toplevel = 1; - } - ; -contents_ipset: contents_ipset content_ipset - | ; -content_ipset: ipset_name_v4 | ipset_name_v6 - ; -ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG - { - #ifdef USE_IPSET - OUTYY(("P(name-v4:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v4) - yyerror("ipset name v4 override, there must be one " - "name for ip v4"); - free(cfg_parser->cfg->ipset_name_v4); - cfg_parser->cfg->ipset_name_v4 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } - ; -ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG - { - #ifdef USE_IPSET - OUTYY(("P(name-v6:%s)\n", $2)); - if(cfg_parser->cfg->ipset_name_v6) - yyerror("ipset name v6 override, there must be one " - "name for ip v6"); - free(cfg_parser->cfg->ipset_name_v6); - cfg_parser->cfg->ipset_name_v6 = $2; - #else - OUTYY(("P(Compiled without ipset, ignoring)\n")); - free($2); - #endif - } - ; %% /* parse helper routines could be here */ From eb349ad097f3dd2b88864fb65a3ee4b4d43c399d Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Tue, 22 Oct 2024 19:49:23 +1100 Subject: [PATCH 02/23] Refactored ipset to support per-rule configuration --- ipset/ipset.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ipset/ipset.h b/ipset/ipset.h index a073e357a..995ec0064 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -19,14 +19,14 @@ * To use the IPset module, install the libmnl-dev (or libmnl-devel) package * and configure with --enable-ipset. And compile. Then enable the ipset * module in unbound.conf with module-config: "ipset validator iterator" - * then create it with ipset -N blacklist iphash and then add - * local-zone: "example.com." ipset
+ * then create it with "ipset create hash:ip" and then add + * local-zone: "example.com." ipset * statements for the zones where you want the addresses of the names - * looked up added to specified table + set. Declaring the protocol as either + * looked up added to specified set. Declaring the protocol as either * "ipv4" or "ipv6" determines which address family to use from the RRSet - * when populating the ipset entry. Specifying ttl at the end will mark the + * when populating the ipset entry. Specifying "ttl" at the end will mark the * ipset entry with a timeout (aka expiry) matching the RRSet TTL, specifying - * no-ttl will prevent setting the TTL on the set entry. + * "no-ttl" will prevent setting the TTL on the set entry. */ #include "util/module.h" From 45f9bbe726babcceb8444e4ff2551207c8e664b3 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Tue, 22 Oct 2024 20:11:28 +1100 Subject: [PATCH 03/23] Merged in upstream changes --- util/config_file.c | 55 ++++++++++++++++- util/config_file.h | 23 ++++++++ util/configlexer.lex | 11 +++- util/configparser.y | 138 +++++++++++++++++++++++++++++++++---------- 4 files changed, 194 insertions(+), 33 deletions(-) diff --git a/util/config_file.c b/util/config_file.c index d5c1b2c7e..122b27d71 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -135,9 +135,12 @@ config_create(void) cfg->http_query_buffer_size = 4*1024*1024; cfg->http_response_buffer_size = 4*1024*1024; cfg->http_nodelay = 1; + cfg->quic_port = UNBOUND_DNS_OVER_QUIC_PORT; + cfg->quic_size = 8*1024*1024; cfg->use_syslog = 1; cfg->log_identity = NULL; /* changed later with argv[0] */ cfg->log_time_ascii = 0; + cfg->log_time_iso = 0; cfg->log_queries = 0; cfg->log_replies = 0; cfg->log_tag_queryreply = 0; @@ -237,6 +240,7 @@ config_create(void) cfg->harden_short_bufsize = 1; cfg->harden_large_queries = 0; cfg->harden_glue = 1; + cfg->harden_unverified_glue = 0; cfg->harden_dnssec_stripped = 1; cfg->harden_below_nxdomain = 1; cfg->harden_referral_path = 0; @@ -398,12 +402,17 @@ config_create(void) cfg->redis_server_path = NULL; cfg->redis_server_password = NULL; cfg->redis_timeout = 100; + cfg->redis_command_timeout = 0; + cfg->redis_connect_timeout = 0; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; cfg->redis_logical_db = 0; #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ cfg->ede = 0; + cfg->iter_scrub_ns = 20; + cfg->iter_scrub_cname = 11; + cfg->max_global_quota = 128; return cfg; error_exit: config_delete(cfg); @@ -537,6 +546,9 @@ int config_set_option(struct config_file* cfg, const char* opt, else if(strcmp(opt, "log-time-ascii:") == 0) { IS_YES_OR_NO; cfg->log_time_ascii = (strcmp(val, "yes") == 0); log_set_time_asc(cfg->log_time_ascii); } + else if(strcmp(opt, "log-time-iso:") == 0) + { IS_YES_OR_NO; cfg->log_time_iso = (strcmp(val, "yes") == 0); + log_set_time_iso(cfg->log_time_iso); } else S_SIZET_NONZERO("max-udp-size:", max_udp_size) else S_YNO("use-syslog:", use_syslog) else S_STR("log-identity:", log_identity) @@ -590,6 +602,8 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_MEMSIZE("http-response-buffer-size:", http_response_buffer_size) else S_YNO("http-nodelay:", http_nodelay) else S_YNO("http-notls-downstream:", http_notls_downstream) + else S_NUMBER_NONZERO("quic-port:", quic_port) + else S_MEMSIZE("quic-size:", quic_size) else S_YNO("interface-automatic:", if_automatic) else S_STR("interface-automatic-ports:", if_automatic_ports) else S_YNO("use-systemd:", use_systemd) @@ -668,6 +682,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_STRLIST("root-hints:", root_hints) else S_STR("target-fetch-policy:", target_fetch_policy) else S_YNO("harden-glue:", harden_glue) + else S_YNO("harden-unverified-glue:", harden_unverified_glue) else S_YNO("harden-short-bufsize:", harden_short_bufsize) else S_YNO("harden-large-queries:", harden_large_queries) else S_YNO("harden-dnssec-stripped:", harden_dnssec_stripped) @@ -708,12 +723,17 @@ int config_set_option(struct config_file* cfg, const char* opt, SERVE_EXPIRED = cfg->serve_expired; } else if(strcmp(opt, "serve-expired-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_ttl = atoi(val); SERVE_EXPIRED_TTL=(time_t)cfg->serve_expired_ttl;} - else S_YNO("serve-expired-ttl-reset:", serve_expired_ttl_reset) + else if(strcmp(opt, "serve-expired-ttl-reset:") == 0) + { IS_YES_OR_NO; cfg->serve_expired_ttl_reset = (strcmp(val, "yes") == 0); + SERVE_EXPIRED_TTL_RESET = cfg->serve_expired_ttl_reset; } else if(strcmp(opt, "serve-expired-reply-ttl:") == 0) { IS_NUMBER_OR_ZERO; cfg->serve_expired_reply_ttl = atoi(val); SERVE_EXPIRED_REPLY_TTL=(time_t)cfg->serve_expired_reply_ttl;} else S_NUMBER_OR_ZERO("serve-expired-client-timeout:", serve_expired_client_timeout) else S_YNO("ede:", ede) else S_YNO("ede-serve-expired:", ede_serve_expired) + else S_NUMBER_OR_ZERO("iter-scrub-ns:", iter_scrub_ns) + else S_NUMBER_OR_ZERO("iter-scrub-cname:", iter_scrub_cname) + else S_NUMBER_OR_ZERO("max-global-quota:", max_global_quota) else S_YNO("serve-original-ttl:", serve_original_ttl) else S_STR("val-nsec3-keysize-iterations:", val_nsec3_key_iterations) else S_YNO("zonemd-permissive-mode:", zonemd_permissive_mode) @@ -1050,6 +1070,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "use-syslog", use_syslog) else O_STR(opt, "log-identity", log_identity) else O_YNO(opt, "log-time-ascii", log_time_ascii) + else O_YNO(opt, "log-time-iso", log_time_iso) else O_DEC(opt, "num-threads", num_threads) else O_IFC(opt, "interface", num_ifs, ifs) else O_IFC(opt, "outgoing-interface", num_out_ifs, out_ifs) @@ -1133,6 +1154,8 @@ config_get_option(struct config_file* cfg, const char* opt, else O_MEM(opt, "http-response-buffer-size", http_response_buffer_size) else O_YNO(opt, "http-nodelay", http_nodelay) else O_YNO(opt, "http-notls-downstream", http_notls_downstream) + else O_DEC(opt, "quic-port", quic_port) + else O_MEM(opt, "quic-size", quic_size) else O_YNO(opt, "use-systemd", use_systemd) else O_YNO(opt, "do-daemonize", do_daemonize) else O_STR(opt, "chroot", chrootdir) @@ -1158,6 +1181,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "harden-short-bufsize", harden_short_bufsize) else O_YNO(opt, "harden-large-queries", harden_large_queries) else O_YNO(opt, "harden-glue", harden_glue) + else O_YNO(opt, "harden-unverified-glue", harden_unverified_glue) else O_YNO(opt, "harden-dnssec-stripped", harden_dnssec_stripped) else O_YNO(opt, "harden-below-nxdomain", harden_below_nxdomain) else O_YNO(opt, "harden-referral-path", harden_referral_path) @@ -1182,6 +1206,9 @@ config_get_option(struct config_file* cfg, const char* opt, else O_DEC(opt, "serve-expired-client-timeout", serve_expired_client_timeout) else O_YNO(opt, "ede", ede) else O_YNO(opt, "ede-serve-expired", ede_serve_expired) + else O_DEC(opt, "iter-scrub-ns", iter_scrub_ns) + else O_DEC(opt, "iter-scrub-cname", iter_scrub_cname) + else O_DEC(opt, "max-global-quota", max_global_quota) else O_YNO(opt, "serve-original-ttl", serve_original_ttl) else O_STR(opt, "val-nsec3-keysize-iterations",val_nsec3_key_iterations) else O_YNO(opt, "zonemd-permissive-mode", zonemd_permissive_mode) @@ -1348,6 +1375,8 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "redis-server-path", redis_server_path) else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_timeout) + else O_DEC(opt, "redis-command-timeout", redis_command_timeout) + else O_DEC(opt, "redis-connect-timeout", redis_connect_timeout) else O_YNO(opt, "redis-expire-records", redis_expire_records) else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ @@ -2345,7 +2374,7 @@ uint8_t* cfg_parse_nsid(const char* str, uint16_t* nsid_len) uint8_t *dp; for ( ch = str, dp = nsid - ; isxdigit(ch[0]) && isxdigit(ch[1]) + ; isxdigit((unsigned char)ch[0]) && isxdigit((unsigned char)ch[1]) ; ch += 2, dp++) { *dp = (uint8_t)sldns_hexdigit_to_int(ch[0]) * 16; *dp += (uint8_t)sldns_hexdigit_to_int(ch[1]); @@ -2401,6 +2430,7 @@ config_apply(struct config_file* config) MIN_TTL = (time_t)config->min_ttl; SERVE_EXPIRED = config->serve_expired; SERVE_EXPIRED_TTL = (time_t)config->serve_expired_ttl; + SERVE_EXPIRED_TTL_RESET = config->serve_expired_ttl_reset; SERVE_EXPIRED_REPLY_TTL = (time_t)config->serve_expired_reply_ttl; SERVE_ORIGINAL_TTL = config->serve_original_ttl; MAX_NEG_TTL = (time_t)config->max_negative_ttl; @@ -2411,10 +2441,12 @@ config_apply(struct config_file* config) MINIMAL_RESPONSES = config->minimal_responses; RRSET_ROUNDROBIN = config->rrset_roundrobin; LOG_TAG_QUERYREPLY = config->log_tag_queryreply; + MAX_GLOBAL_QUOTA = config->max_global_quota; UNKNOWN_SERVER_NICENESS = config->unknown_server_time_limit; USEFUL_SERVER_TOP_TIMEOUT = RTT_MAX_TIMEOUT; BLACKLIST_PENALTY = USEFUL_SERVER_TOP_TIMEOUT*4; log_set_time_asc(config->log_time_ascii); + log_set_time_iso(config->log_time_iso); autr_permit_small_holddown = config->permit_small_holddown; stream_wait_max = config->stream_wait_size; http2_query_buffer_max = config->http_query_buffer_size; @@ -2857,3 +2889,22 @@ if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port) return 0; #endif } + +/** see if interface is quic, its port number == the quic port number */ +int +if_is_quic(const char* ifname, const char* port, int quic_port) +{ +#ifndef HAVE_NGTCP2 + (void)ifname; + (void)port; + (void)quic_port; + return 0; +#else + char* p = strchr(ifname, '@'); + if(!p && atoi(port) == quic_port) + return 1; + if(p && atoi(p+1) == quic_port) + return 1; + return 0; +#endif +} diff --git a/util/config_file.h b/util/config_file.h index 5f577d256..65368d6ba 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -162,6 +162,11 @@ struct config_file { /** Disable TLS for http sockets downstream */ int http_notls_downstream; + /** port on which to provide DNS over QUIC service */ + int quic_port; + /** size of the quic data, max bytes */ + size_t quic_size; + /** outgoing port range number of ports (per thread) */ int outgoing_num_ports; /** number of outgoing tcp buffers per (per thread) */ @@ -289,6 +294,8 @@ struct config_file { int harden_large_queries; /** harden against spoofed glue (out of zone data) */ int harden_glue; + /** harden against unverified glue */ + int harden_unverified_glue; /** harden against receiving no DNSSEC data for trust anchor */ int harden_dnssec_stripped; /** harden against queries that fall under known nxdomain names */ @@ -340,6 +347,8 @@ struct config_file { int use_syslog; /** log timestamp in ascii UTC */ int log_time_ascii; + /** log timestamp in ISO8601 format */ + int log_time_iso; /** log queries with one line per query */ int log_queries; /** log replies with one line per reply */ @@ -738,6 +747,10 @@ struct config_file { char* redis_server_password; /** timeout (in ms) for communication with the redis server */ int redis_timeout; + /** timeout (in ms) for redis commands */ + int redis_command_timeout; + /** timeout (in ms) for redis connection set up */ + int redis_connect_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; /** set the redis logical database upon connection */ @@ -755,6 +768,12 @@ struct config_file { char* cookie_secret_file; /** respond with Extended DNS Errors (RFC8914) */ int ede; + /** limit on NS RRs in RRset for the iterator scrubber. */ + size_t iter_scrub_ns; + /** limit on CNAME, DNAME RRs in answer for the iterator scrubber. */ + int iter_scrub_cname; + /** limit on upstream queries for an incoming query and subqueries. */ + int max_global_quota; }; /** from cfg username, after daemonize setup performed */ @@ -1414,6 +1433,10 @@ int if_is_pp2(const char* ifname, const char* port, /** see if interface is DNSCRYPT, its port number == the dnscrypt port number */ int if_is_dnscrypt(const char* ifname, const char* port, int dnscrypt_port); + +/** see if interface is quic, its port number == the quic port number */ +int if_is_quic(const char* ifname, const char* port, int quic_port); + #ifdef USE_LINUX_IP_LOCAL_PORT_RANGE #define LINUX_IP_LOCAL_PORT_RANGE_PATH "/proc/sys/net/ipv4/ip_local_port_range" #endif diff --git a/util/configlexer.lex b/util/configlexer.lex index ed65913c7..2d264b4bc 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -291,6 +291,8 @@ http-query-buffer-size{COLON} { YDVAR(1, VAR_HTTP_QUERY_BUFFER_SIZE) } http-response-buffer-size{COLON} { YDVAR(1, VAR_HTTP_RESPONSE_BUFFER_SIZE) } http-nodelay{COLON} { YDVAR(1, VAR_HTTP_NODELAY) } http-notls-downstream{COLON} { YDVAR(1, VAR_HTTP_NOTLS_DOWNSTREAM) } +quic-port{COLON} { YDVAR(1, VAR_QUIC_PORT) } +quic-size{COLON} { YDVAR(1, VAR_QUIC_SIZE) } use-systemd{COLON} { YDVAR(1, VAR_USE_SYSTEMD) } do-daemonize{COLON} { YDVAR(1, VAR_DO_DAEMONIZE) } interface{COLON} { YDVAR(1, VAR_INTERFACE) } @@ -337,6 +339,7 @@ target-fetch-policy{COLON} { YDVAR(1, VAR_TARGET_FETCH_POLICY) } harden-short-bufsize{COLON} { YDVAR(1, VAR_HARDEN_SHORT_BUFSIZE) } harden-large-queries{COLON} { YDVAR(1, VAR_HARDEN_LARGE_QUERIES) } harden-glue{COLON} { YDVAR(1, VAR_HARDEN_GLUE) } +harden-unverified-glue{COLON} { YDVAR(1, VAR_HARDEN_UNVERIFIED_GLUE) } harden-dnssec-stripped{COLON} { YDVAR(1, VAR_HARDEN_DNSSEC_STRIPPED) } harden-below-nxdomain{COLON} { YDVAR(1, VAR_HARDEN_BELOW_NXDOMAIN) } harden-referral-path{COLON} { YDVAR(1, VAR_HARDEN_REFERRAL_PATH) } @@ -452,6 +455,7 @@ permit-small-holddown{COLON} { YDVAR(1, VAR_PERMIT_SMALL_HOLDDOWN) } use-syslog{COLON} { YDVAR(1, VAR_USE_SYSLOG) } log-identity{COLON} { YDVAR(1, VAR_LOG_IDENTITY) } log-time-ascii{COLON} { YDVAR(1, VAR_LOG_TIME_ASCII) } +log-time-iso{COLON} { YDVAR(1, VAR_LOG_TIME_ISO) } log-queries{COLON} { YDVAR(1, VAR_LOG_QUERIES) } log-replies{COLON} { YDVAR(1, VAR_LOG_REPLIES) } log-tag-queryreply{COLON} { YDVAR(1, VAR_LOG_TAG_QUERYREPLY) } @@ -535,7 +539,7 @@ dnstap-log-forwarder-query-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_QUERY_MESSAGES) } dnstap-log-forwarder-response-messages{COLON} { YDVAR(1, VAR_DNSTAP_LOG_FORWARDER_RESPONSE_MESSAGES) } -dnstap-sample-rate { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } +dnstap-sample-rate{COLON} { YDVAR(1, VAR_DNSTAP_SAMPLE_RATE) } disable-dnssec-lame-check{COLON} { YDVAR(1, VAR_DISABLE_DNSSEC_LAME_CHECK) } ip-ratelimit{COLON} { YDVAR(1, VAR_IP_RATELIMIT) } ip-ratelimit-cookie{COLON} { YDVAR(1, VAR_IP_RATELIMIT_COOKIE) } @@ -595,6 +599,8 @@ redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } redis-server-password{COLON} { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) } redis-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) } +redis-command-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) } +redis-connect-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) } @@ -607,6 +613,9 @@ edns-client-string-opcode{COLON} { YDVAR(1, VAR_EDNS_CLIENT_STRING_OPCODE) } nsid{COLON} { YDVAR(1, VAR_NSID ) } ede{COLON} { YDVAR(1, VAR_EDE ) } proxy-protocol-port{COLON} { YDVAR(1, VAR_PROXY_PROTOCOL_PORT) } +iter-scrub-ns{COLON} { YDVAR(1, VAR_ITER_SCRUB_NS) } +iter-scrub-cname{COLON} { YDVAR(1, VAR_ITER_SCRUB_CNAME) } +max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } {NEWLINE} { LEXOUT(("NL(%d,%d)\n", num_args, num_args_max)); if (num_args == 0 && num_args_max > 0) { diff --git a/util/configparser.y b/util/configparser.y index e017b81d9..47ecc88bd 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -182,6 +182,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT %token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD %token VAR_CACHEDB_REDISLOGICALDB +%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISCONNECTTIMEOUT %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL @@ -201,16 +202,18 @@ extern struct config_parser_state* cfg_parser; %token VAR_RPZ_SIGNAL_NXDOMAIN_RA VAR_INTERFACE_AUTOMATIC_PORTS VAR_EDE %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA +%token VAR_QUIC_PORT VAR_QUIC_SIZE %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO %token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO VAR_CACHEDB_NO_STORE %token VAR_LOG_DESTADDR VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED -%token VAR_COOKIE_SECRET_FILE +%token VAR_COOKIE_SECRET_FILE VAR_ITER_SCRUB_NS VAR_ITER_SCRUB_CNAME +%token VAR_MAX_GLOBAL_QUOTA VAR_HARDEN_UNVERIFIED_GLUE VAR_LOG_TIME_ISO %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; -toplevelvar: serverstart contents_server | stubstart contents_stub | - forwardstart contents_forward | pythonstart contents_py | - rcstart contents_rc | dtstart contents_dt | viewstart contents_view | +toplevelvar: serverstart contents_server | stub_clause | + forward_clause | pythonstart contents_py | + rcstart contents_rc | dtstart contents_dt | view_clause | dnscstart contents_dnsc | cachedbstart contents_cachedb | authstart contents_auth | rpzstart contents_rpz | dynlibstart contents_dl | force_toplevel @@ -338,10 +341,21 @@ content_server: server_num_threads | server_verbosity | server_port | server_edns_client_string_opcode | server_nsid | server_zonemd_permissive_mode | server_max_reuse_tcp_queries | server_tcp_reuse_timeout | server_tcp_auth_query_timeout | + server_quic_port | server_quic_size | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | server_harden_unknown_additional | server_disable_edns_do | - server_log_destaddr | server_cookie_secret_file + server_log_destaddr | server_cookie_secret_file | + server_iter_scrub_ns | server_iter_scrub_cname | server_max_global_quota | + server_harden_unverified_glue | server_log_time_iso + ; +stub_clause: stubstart contents_stub + { + /* stub end */ + if(cfg_parser->cfg->stubs && + !cfg_parser->cfg->stubs->name) + yyerror("stub-zone without name"); + } ; stubstart: VAR_STUB_ZONE { @@ -357,17 +371,19 @@ stubstart: VAR_STUB_ZONE } } ; -contents_stub: content_stub contents_stub - | - { - /* stub end */ - if(cfg_parser->cfg->stubs && - !cfg_parser->cfg->stubs->name) - yyerror("stub-zone without name"); - }; +contents_stub: contents_stub content_stub + | ; content_stub: stub_name | stub_host | stub_addr | stub_prime | stub_first | stub_no_cache | stub_ssl_upstream | stub_tcp_upstream ; +forward_clause: forwardstart contents_forward + { + /* forward end */ + if(cfg_parser->cfg->forwards && + !cfg_parser->cfg->forwards->name) + yyerror("forward-zone without name"); + } + ; forwardstart: VAR_FORWARD_ZONE { struct config_stub* s; @@ -382,17 +398,19 @@ forwardstart: VAR_FORWARD_ZONE } } ; -contents_forward: content_forward contents_forward - | - { - /* forward end */ - if(cfg_parser->cfg->forwards && - !cfg_parser->cfg->forwards->name) - yyerror("forward-zone without name"); - }; +contents_forward: contents_forward content_forward + | ; content_forward: forward_name | forward_host | forward_addr | forward_first | forward_no_cache | forward_ssl_upstream | forward_tcp_upstream ; +view_clause: viewstart contents_view + { + /* view end */ + if(cfg_parser->cfg->views && + !cfg_parser->cfg->views->name) + yyerror("view without name"); + } + ; viewstart: VAR_VIEW { struct config_view* s; @@ -407,14 +425,8 @@ viewstart: VAR_VIEW } } ; -contents_view: content_view contents_view - | - { - /* view end */ - if(cfg_parser->cfg->views && - !cfg_parser->cfg->views->name) - yyerror("view without name"); - }; +contents_view: contents_view content_view + | ; content_view: view_name | view_local_zone | view_local_data | view_first | view_response_ip | view_response_ip_data | view_local_data_ptr ; @@ -1197,6 +1209,26 @@ server_http_notls_downstream: VAR_HTTP_NOTLS_DOWNSTREAM STRING_ARG else cfg_parser->cfg->http_notls_downstream = (strcmp($2, "yes")==0); free($2); }; +server_quic_port: VAR_QUIC_PORT STRING_ARG + { + OUTYY(("P(server_quic_port:%s)\n", $2)); +#ifndef HAVE_NGTCP2 + log_warn("%s:%d: Unbound is not compiled with " + "ngtcp2. This is required to use DNS " + "over QUIC.", cfg_parser->filename, cfg_parser->line); +#endif + if(atoi($2) == 0) + yyerror("port number expected"); + else cfg_parser->cfg->quic_port = atoi($2); + free($2); + }; +server_quic_size: VAR_QUIC_SIZE STRING_ARG + { + OUTYY(("P(server_quic_size:%s)\n", $2)); + if(!cfg_parse_memsize($2, &cfg_parser->cfg->quic_size)) + yyerror("memory size expected"); + free($2); + }; server_use_systemd: VAR_USE_SYSTEMD STRING_ARG { OUTYY(("P(server_use_systemd:%s)\n", $2)); @@ -1238,6 +1270,15 @@ server_log_time_ascii: VAR_LOG_TIME_ASCII STRING_ARG free($2); } ; +server_log_time_iso: VAR_LOG_TIME_ISO STRING_ARG + { + OUTYY(("P(server_log_time_iso:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->log_time_iso = (strcmp($2, "yes")==0); + free($2); + } + ; server_log_queries: VAR_LOG_QUERIES STRING_ARG { OUTYY(("P(server_log_queries:%s)\n", $2)); @@ -1803,6 +1844,16 @@ server_harden_glue: VAR_HARDEN_GLUE STRING_ARG free($2); } ; +server_harden_unverified_glue: VAR_HARDEN_UNVERIFIED_GLUE STRING_ARG + { + OUTYY(("P(server_harden_unverified_glue:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->harden_unverified_glue = + (strcmp($2, "yes")==0); + free($2); + } + ; server_harden_dnssec_stripped: VAR_HARDEN_DNSSEC_STRIPPED STRING_ARG { OUTYY(("P(server_harden_dnssec_stripped:%s)\n", $2)); @@ -3899,7 +3950,8 @@ contents_cachedb: contents_cachedb content_cachedb content_cachedb: cachedb_backend_name | cachedb_secret_seed | redis_server_host | redis_server_port | redis_timeout | redis_expire_records | redis_server_path | redis_server_password | - cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired + cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired | + redis_command_timeout | redis_connect_timeout ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -4015,6 +4067,32 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG free($2); } ; +redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_command_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis command timeout value expected"); + else cfg_parser->cfg->redis_command_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; +redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + OUTYY(("P(redis_connect_timeout:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("redis connect timeout value expected"); + else cfg_parser->cfg->redis_connect_timeout = atoi($2); + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS) From 500882004097f830b4e4ced2f3c746223a1bf4ea Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Tue, 22 Oct 2024 20:19:14 +1100 Subject: [PATCH 04/23] Merged in upstream changes --- util/configparser.y | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/util/configparser.y b/util/configparser.y index 47ecc88bd..844e131ab 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -4164,6 +4164,33 @@ server_cookie_secret_file: VAR_COOKIE_SECRET_FILE STRING_ARG cfg_parser->cfg->cookie_secret_file = $2; } ; +server_iter_scrub_ns: VAR_ITER_SCRUB_NS STRING_ARG + { + OUTYY(("P(server_iter_scrub_ns:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->cfg->iter_scrub_ns = atoi($2); + free($2); + } + ; +server_iter_scrub_cname: VAR_ITER_SCRUB_CNAME STRING_ARG + { + OUTYY(("P(server_iter_scrub_cname:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->cfg->iter_scrub_cname = atoi($2); + free($2); + } + ; +server_max_global_quota: VAR_MAX_GLOBAL_QUOTA STRING_ARG + { + OUTYY(("P(server_max_global_quota:%s)\n", $2)); + if(atoi($2) == 0 && strcmp($2, "0") != 0) + yyerror("number expected"); + else cfg_parser->cfg->max_global_quota = atoi($2); + free($2); + } + ; %% /* parse helper routines could be here */ From ba17288949c89823ce52dc850ffeb1095670bbe5 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Tue, 22 Oct 2024 23:35:57 +1100 Subject: [PATCH 05/23] Added checkconf warning for ipset with TTL when compiled with BSD pf --- ipset/ipset.c | 24 +++++------------------- util/configparser.y | 30 ++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 3dfe66227..0a247cbcc 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -86,7 +86,8 @@ static void * open_filter() { #endif #ifdef HAVE_NET_PFVAR_H -static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, int af) { +static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, + int af, time_t _ttl) { struct pfioc_table io; struct pfr_addr addr; const char *p; @@ -325,27 +326,12 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, if (((ds && strncasecmp(p->str, ds, plen) == 0) || (qs && strncasecmp(p->str, qs, plen) == 0)) && set_af == af) { - verbose( - VERB_QUERY, - "ipset: match ((%s && %s == %s) || (%s && %s == %s)) && %d == %d", - ds ? "true" : "false", p->str, ds, - qs ? "true" : "false", p->str, qs, - set_af, af - ); d = (struct packed_rrset_data*)rrset->entry.data; bool set_ttl = strncasecmp(p->str4, "ttl", 3) == 0; ipset_add_rrset_data(ie, d, p->str3, af, dname, set_ttl); break; - } else { - verbose( - VERB_QUERY, - "ipset: no match ((%s && %s == %s) || (%s && %s == %s)) && %d == %d", - ds ? "true" : "false", p->str, ds, - qs ? "true" : "false", p->str, qs, - set_af, af - ); - } - } + } + } return 0; } @@ -440,7 +426,7 @@ void ipset_destartup(struct module_env* env, int id) { env->modinfo[id] = NULL; } -int ipset_init(struct module_env* env, int id) { +int ipset_init(struct module_env *ATTR_UNUSED(env), int ATTR_UNUSED(id)) { return 1; } diff --git a/util/configparser.y b/util/configparser.y index 844e131ab..38403f24c 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -43,6 +43,7 @@ #include #include #include +#include #include "util/configyyrename.h" #include "util/config_file.h" @@ -52,12 +53,15 @@ int ub_c_lex(void); void ub_c_error(const char *message); +static void yywarn(const char *str); static void validate_respip_action(const char* action); static void validate_acl_action(const char* action); /* these need to be global, otherwise they cannot be used inside yacc */ extern struct config_parser_state* cfg_parser; +static bool ttl_pf_have_warned = false; + #if 0 #define OUTYY(s) printf s /* used ONLY when debugging */ #else @@ -2404,7 +2408,7 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($5); free($6); #ifdef USE_IPSET - } else if (strcmp($3, "ipset") == 0) { + } else if (strncmp($3, "ipset", 5) == 0) { /* Format: ipset */ if (strncmp($6, "ttl", 3) != 0 && strncmp($6, "no-ttl", 6) != 0) { @@ -2415,6 +2419,15 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($5); free($6); } else { +#ifdef HAVE_NET_PFVAR_H + if (!ttl_pf_have_warned && strncmp($6, "ttl", 3) == 0) { + yywarn( + "local-zone ipset: per-address TTL not supported in" + "BSD packet filter tables, ignoring" + ); + ttl_pf_have_warned = false; + } +#endif size_t len = strlen($2); /* Make sure to add the trailing dot. * These are str compared to domain names. */ @@ -3408,7 +3421,7 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($6); #ifdef USE_IPSET } else if (strcmp($3, "ipset") == 0) { - /* Format: ipset
*/ + /* Format: ipset */ if (strncmp($6, "ttl", 3) != 0 || strncmp($6, "no-ttl", 6) != 0) { yyerror("local-zone with ipset expected ttl/no-ttl"); @@ -3418,6 +3431,15 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($5); free($6); } else { +#ifdef HAVE_NET_PFVAR_H + if (!ttl_pf_have_warned && strncmp($6, "ttl", 3) == 0) { + yywarn( + "local-zone ipset: per-address TTL not supported in" + "BSD packet filter tables, ignoring" + ); + ttl_pf_have_warned = false; + } +#endif size_t len = strlen($2); /* Make sure to add the trailing dot. * These are str compared to domain names. */ @@ -4228,3 +4250,7 @@ validate_acl_action(const char* action) "allow_snoop or allow_cookie as access control action"); } } + +static void yywarn(const char *str) { + fprintf(stderr, "warning: %s\n", str); +} From c12ee39c3165ee3dd19fbb2f1e1079521d71e70f Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:04:29 +1100 Subject: [PATCH 06/23] Supported backwards compat with global ipsets --- ipset/ipset.c | 46 +++++++++++++++++++++++++- ipset/ipset.h | 6 ++++ util/config_file.c | 16 ++++++++++ util/config_file.h | 4 +++ util/configparser.y | 78 ++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 144 insertions(+), 6 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 0a247cbcc..952f99079 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -426,7 +426,51 @@ void ipset_destartup(struct module_env* env, int id) { env->modinfo[id] = NULL; } -int ipset_init(struct module_env *ATTR_UNUSED(env), int ATTR_UNUSED(id)) { +int convert_global_ipset(struct module_env* env, struct ipset_env* ipset_env) { + struct config_str4list *p; + for (p = env->cfg->local_zones_ipset; p; p = p->next) { + if (strncmp(p->str3, "@global@", 8) != 0) { + continue; + } + if (ipset_env->v4_enabled) { + p->str2 = "ipv4"; + p->str3 = strdup(ipset_env->name_v4); + } else if (ipset_env->v6_enabled) { + p->str2 = "ipv6"; + p->str3 = strdup(ipset_env->name_v6); + continue; + } + if (ipset_env->v4_enabled && ipset->v6_enabled) { + if (!cfg_str4list_insert( + &env->cfg->local_zones_ipset, + strdup(p->str), + "ipv6", + strdup(ipset_env->name_v6), + "no-ttl" + )) { + log_err("ipset: out of memory adding rule mapping for global declaration"); + return 0; + } + } + } +} + +int ipset_init(struct module_env* env, int id) { + struct ipset_env *ipset_env = env->modinfo[id]; + + ipset_env->name_v4 = env->cfg->ipset_name_v4; + ipset_env->name_v6 = env->cfg->ipset_name_v6; + + ipset_env->v4_enabled = !ipset_env->name_v4 || (strlen(ipset_env->name_v4) == 0) ? 0 : 1; + ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1; + + if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) { + log_err("ipset: set name no configuration?"); + return 0; + } + + convert_global_ipset(env, ipset_env); + return 1; } diff --git a/ipset/ipset.h b/ipset/ipset.h index 995ec0064..2118961dd 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -37,6 +37,12 @@ extern "C" { struct ipset_env { void* dev; + + int v4_enabled; + int v6_enabled; + + const char *name_v4; + const char *name_v6; }; struct ipset_qstate { diff --git a/util/config_file.c b/util/config_file.c index 122b27d71..935e2af3e 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -409,6 +409,10 @@ config_create(void) cfg->redis_logical_db = 0; #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ +#ifdef USE_IPSET + cfg->ipset_name_v4 = NULL; + cfg->ipset_name_v6 = NULL; +#endif cfg->ede = 0; cfg->iter_scrub_ns = 20; cfg->iter_scrub_cname = 11; @@ -1381,6 +1385,10 @@ config_get_option(struct config_file* cfg, const char* opt, else O_DEC(opt, "redis-logical-db", redis_logical_db) #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ +#ifdef USE_IPSET + else O_STR(opt, "name-v4", ipset_name_v4) + else O_STR(opt, "name-v6", ipset_name_v6) +#endif /* not here: * outgoing-permit, outgoing-avoid - have list of ports * local-zone - zones and nodefault variables @@ -1772,6 +1780,14 @@ config_delete(struct config_file* cfg) free(cfg->redis_server_password); #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ +#ifdef USE_IPSET + if (cfg->ipset_name_v4 != NULL) { + free(cfg->ipset_name_v4); + } + if (cfg->ipset_name_v6 != NULL) { + free(cfg->ipset_name_v6); + } +#endif free(cfg); } diff --git a/util/config_file.h b/util/config_file.h index 65368d6ba..ecae593c2 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -766,6 +766,10 @@ struct config_file { size_t cookie_secret_len; /** path to cookie secret store */ char* cookie_secret_file; +#ifdef USE_IPSET + char* ipset_name_v4; + char* ipset_name_v6; +#endif /** respond with Extended DNS Errors (RFC8914) */ int ede; /** limit on NS RRs in RRset for the iterator scrubber. */ diff --git a/util/configparser.y b/util/configparser.y index 38403f24c..3e8b88b82 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -198,6 +198,7 @@ static bool ttl_pf_have_warned = false; %token VAR_DISCARD_TIMEOUT VAR_WAIT_LIMIT VAR_WAIT_LIMIT_COOKIE %token VAR_WAIT_LIMIT_NETBLOCK VAR_WAIT_LIMIT_COOKIE_NETBLOCK %token VAR_STREAM_WAIT_SIZE VAR_TLS_CIPHERS VAR_TLS_CIPHERSUITES VAR_TLS_USE_SNI +%token VAR_IPSET VAR_IPSET_NAME_V4 VAR_IPSET_NAME_V6 %token VAR_TLS_SESSION_TICKET_KEYS VAR_RPZ VAR_TAGS VAR_RPZ_ACTION_OVERRIDE %token VAR_RPZ_CNAME_OVERRIDE VAR_RPZ_LOG VAR_RPZ_LOG_NAME %token VAR_DYNLIB VAR_DYNLIB_FILE VAR_EDNS_CLIENT_STRING @@ -219,8 +220,9 @@ toplevelvar: serverstart contents_server | stub_clause | forward_clause | pythonstart contents_py | rcstart contents_rc | dtstart contents_dt | view_clause | dnscstart contents_dnsc | cachedbstart contents_cachedb | - authstart contents_auth | rpzstart contents_rpz | - dynlibstart contents_dl | force_toplevel + ipsetstart contents_ipset |authstart contents_auth | + rpzstart contents_rpz | dynlibstart contents_dl | + force_toplevel ; force_toplevel: VAR_FORCE_TOPLEVEL { @@ -2381,8 +2383,20 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($3); #ifdef USE_IPSET } else if (strcmp($3, "ipset") == 0) { - /* Transaprent pass-through to 5-param variant */ - free($2); + /* Transform existing 2 param variant into 5 param with global lookup */ + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if ($2[len-1] != '.') { + if (!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } + if(!cfg_str4list_insert(&cfg_parser->cfg-> + local_zones_ipset, $2, "@global@", "@global@", "no-ttl")) + fatal_exit("out of memory adding local-zone"); free($3); #endif } else { @@ -3394,7 +3408,21 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($3); #ifdef USE_IPSET } else if (strcmp($3, "ipset") == 0) { - /* Transaprent pass-through to 5-param variant */ + /* Transform existing 2 param variant into 5 param with global lookup */ + size_t len = strlen($2); + /* Make sure to add the trailing dot. + * These are str compared to domain names. */ + if ($2[len-1] != '.') { + if (!($2 = realloc($2, len+2))) { + fatal_exit("out of memory adding local-zone"); + } + $2[len] = '.'; + $2[len+1] = 0; + } + if(!cfg_str4list_insert(&cfg_parser->cfg->views-> + local_zones_ipset, $2, "@global@", "@global@", "no-ttl")) + fatal_exit("out of memory adding local-zone"); + free($3); #endif } else { if(!cfg_str2list_insert( @@ -4212,6 +4240,46 @@ server_max_global_quota: VAR_MAX_GLOBAL_QUOTA STRING_ARG else cfg_parser->cfg->max_global_quota = atoi($2); free($2); } + ; +ipsetstart: VAR_IPSET + { + OUTYY(("\nP(ipset:)\n")); + cfg_parser->started_toplevel = 1; + } + ; +contents_ipset: contents_ipset content_ipset + | ; +content_ipset: ipset_name_v4 | ipset_name_v6 + ; +ipset_name_v4: VAR_IPSET_NAME_V4 STRING_ARG + { + #ifdef USE_IPSET + OUTYY(("P(name-v4:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v4) + yyerror("ipset name v4 override, there must be one " + "name for ip v4"); + free(cfg_parser->cfg->ipset_name_v4); + cfg_parser->cfg->ipset_name_v4 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } + ; +ipset_name_v6: VAR_IPSET_NAME_V6 STRING_ARG + { + #ifdef USE_IPSET + OUTYY(("P(name-v6:%s)\n", $2)); + if(cfg_parser->cfg->ipset_name_v6) + yyerror("ipset name v6 override, there must be one " + "name for ip v6"); + free(cfg_parser->cfg->ipset_name_v6); + cfg_parser->cfg->ipset_name_v6 = $2; + #else + OUTYY(("P(Compiled without ipset, ignoring)\n")); + free($2); + #endif + } ; %% From 93ff2a5eb5227128c7a7107d029f9a805b73c9ee Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:08:54 +1100 Subject: [PATCH 07/23] Supported backwards compat with global ipsets --- ipset/ipset.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ipset/ipset.h b/ipset/ipset.h index 2118961dd..c690d0576 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -38,11 +38,11 @@ extern "C" { struct ipset_env { void* dev; - int v4_enabled; - int v6_enabled; + int v4_enabled; + int v6_enabled; const char *name_v4; - const char *name_v6; + const char *nmame_v6; }; struct ipset_qstate { From a1e3eb79524da50fc8dec6da2de9443e9c7d011f Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:09:30 +1100 Subject: [PATCH 08/23] Supported backwards compat with global ipsets --- ipset/ipset.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipset/ipset.h b/ipset/ipset.h index c690d0576..77daccceb 100644 --- a/ipset/ipset.h +++ b/ipset/ipset.h @@ -41,8 +41,8 @@ struct ipset_env { int v4_enabled; int v6_enabled; - const char *name_v4; - const char *nmame_v6; + const char *name_v4; + const char *name_v6; }; struct ipset_qstate { From 6209e2d58a679517ef484be8c9e071056b8c8dea Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:16:10 +1100 Subject: [PATCH 09/23] Supported backwards compat with global ipsets --- util/config_file.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/util/config_file.c b/util/config_file.c index 935e2af3e..ed5bce896 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -410,8 +410,8 @@ config_create(void) #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ #ifdef USE_IPSET - cfg->ipset_name_v4 = NULL; - cfg->ipset_name_v6 = NULL; + cfg->ipset_name_v4 = NULL; + cfg->ipset_name_v6 = NULL; #endif cfg->ede = 0; cfg->iter_scrub_ns = 20; @@ -1386,8 +1386,8 @@ config_get_option(struct config_file* cfg, const char* opt, #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ #ifdef USE_IPSET - else O_STR(opt, "name-v4", ipset_name_v4) - else O_STR(opt, "name-v6", ipset_name_v6) + else O_STR(opt, "name-v4", ipset_name_v4) + else O_STR(opt, "name-v6", ipset_name_v6) #endif /* not here: * outgoing-permit, outgoing-avoid - have list of ports @@ -2650,6 +2650,7 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) } (void)strlcpy(buf, name, sizeof(buf)); buf[name_end-name] = '\0'; +#define USE_IPSET #ifdef USE_IPSET type = name_end; int result = get_next_token(name_end, &type, &type_end); @@ -2660,8 +2661,8 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) *ipset_name, *ipset_name_end, *ttl; protocol = type_end; if (get_next_token(type_end, &protocol, &protocol_end)) { - log_err("syntax error: expected ipset zone protocol: %s", val); - return 0; + /* We don't have the 5 argument variant, so defer to 2 arg variant */ + goto parse_global_ipset; } ipset_name = protocol_end; if (get_next_token(ip_table_end, &ipset_name, &ipset_name_end)) { @@ -2679,6 +2680,7 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) strdup(name), strdup(protocol), strdup(ipset_name), strdup(ttl)); } +parse_global_ipset:; /* There was no next-space after this token, so it must be final * and as such we don't have enough tokens*/ #endif @@ -2694,6 +2696,11 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) if(strcmp(type, "nodefault")==0) { return cfg_strlist_insert(&cfg->local_zones_nodefault, strdup(name)); +#ifdef USE_IPSET + } else if(strcmp(type, "ipset")==0) { + return cfg_strlist_insert(&cfg->local_zones_ipset, + strdup(name)); +#endif } else { return cfg_str2list_insert(&cfg->local_zones, strdup(buf), strdup(type)); From fd6740b07daf88746a58eee22f20409fb359c608 Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:17:34 +1100 Subject: [PATCH 10/23] Removed unused lexer token return --- util/configlexer.lex | 3 --- 1 file changed, 3 deletions(-) diff --git a/util/configlexer.lex b/util/configlexer.lex index 2d264b4bc..d66e52d90 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -622,9 +622,6 @@ max-global-quota{COLON} { YDVAR(1, VAR_MAX_GLOBAL_QUOTA) } /* Early match a set of tokens between the min and max */ num_args = 0; num_args_max = 0; - /* Return newline characters to stream for matching */ - /* yyless(0);*/ - LEXOUT(("TOKENS: %s\n", yytext)); BEGIN(INITIAL); } else { cfg_parser->line++; From 124c6129180671437b544da2a0e051cb979352ed Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Fri, 25 Oct 2024 12:18:19 +1100 Subject: [PATCH 11/23] Removed TODO comment --- ipset/ipset.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 952f99079..9dddd731c 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -247,7 +247,6 @@ ipset_add_rrset_data(struct ipset_env *ie, char ip[128]; if(inet_ntop(af, rr_data+2, ip, (socklen_t)sizeof(ip)) == 0) snprintf(ip, sizeof(ip), "(inet_ntop_error)"); - // TODO: Remove the table argument from the config, it's not needed if (set_ttl) { verbose( VERB_QUERY, From 70c252e9b7ecd61df9c519e0dd24ec8bd87f5162 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Fri, 25 Oct 2024 21:29:49 +1100 Subject: [PATCH 12/23] Fixed memory leak and missing lexer tokens --- ipset/ipset.c | 13 ++++++------- util/configlexer.lex | 3 +++ util/configparser.y | 6 ++++-- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 9dddd731c..51c6b5ae6 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -432,20 +432,20 @@ int convert_global_ipset(struct module_env* env, struct ipset_env* ipset_env) { continue; } if (ipset_env->v4_enabled) { - p->str2 = "ipv4"; + p->str2 = strdup("ipv4"); p->str3 = strdup(ipset_env->name_v4); } else if (ipset_env->v6_enabled) { - p->str2 = "ipv6"; + p->str2 = strdup("ipv6"); p->str3 = strdup(ipset_env->name_v6); continue; } - if (ipset_env->v4_enabled && ipset->v6_enabled) { + if (ipset_env->v4_enabled && ipset_env->v6_enabled) { if (!cfg_str4list_insert( &env->cfg->local_zones_ipset, strdup(p->str), - "ipv6", + strdup("ipv6"), strdup(ipset_env->name_v6), - "no-ttl" + strdup("no-ttl") )) { log_err("ipset: out of memory adding rule mapping for global declaration"); return 0; @@ -464,8 +464,7 @@ int ipset_init(struct module_env* env, int id) { ipset_env->v6_enabled = !ipset_env->name_v6 || (strlen(ipset_env->name_v6) == 0) ? 0 : 1; if ((ipset_env->v4_enabled < 1) && (ipset_env->v6_enabled < 1)) { - log_err("ipset: set name no configuration?"); - return 0; + return 1; } convert_global_ipset(env, ipset_env); diff --git a/util/configlexer.lex b/util/configlexer.lex index d66e52d90..ecc501875 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -603,6 +603,9 @@ redis-command-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) } redis-connect-timeout{COLON} { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) } redis-expire-records{COLON} { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) } redis-logical-db{COLON} { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) } +ipset{COLON} { YDVAR(0, VAR_IPSET) } +name-v4{COLON} { YDVAR(1, VAR_IPSET_NAME_V4) } +name-v6{COLON} { YDVAR(1, VAR_IPSET_NAME_V6) } udp-upstream-without-downstream{COLON} { YDVAR(1, VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM) } tcp-connection-limit{COLON} { YDVAR(2, VAR_TCP_CONNECTION_LIMIT) } answer-cookie{COLON} { YDVAR(1, VAR_ANSWER_COOKIE ) } diff --git a/util/configparser.y b/util/configparser.y index 3e8b88b82..ec2f487c5 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -2395,8 +2395,9 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG $2[len+1] = 0; } if(!cfg_str4list_insert(&cfg_parser->cfg-> - local_zones_ipset, $2, "@global@", "@global@", "no-ttl")) + local_zones_ipset, $2, strdup("@global@"), strdup("@global@"), strdup("no-ttl"))) { fatal_exit("out of memory adding local-zone"); + } free($3); #endif } else { @@ -3420,8 +3421,9 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG $2[len+1] = 0; } if(!cfg_str4list_insert(&cfg_parser->cfg->views-> - local_zones_ipset, $2, "@global@", "@global@", "no-ttl")) + local_zones_ipset, $2, strdup("@global@"), strdup("@global@"), strdup("no-ttl"))) { fatal_exit("out of memory adding local-zone"); + } free($3); #endif } else { From d35a4d72d67210041606355fc4296515ed0761bd Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Fri, 25 Oct 2024 22:00:11 +1100 Subject: [PATCH 13/23] Fixed memory leak and missing lexer tokens --- util/config_file.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/util/config_file.c b/util/config_file.c index ed5bce896..b5cc4fa27 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -2650,7 +2650,6 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) } (void)strlcpy(buf, name, sizeof(buf)); buf[name_end-name] = '\0'; -#define USE_IPSET #ifdef USE_IPSET type = name_end; int result = get_next_token(name_end, &type, &type_end); @@ -2665,7 +2664,7 @@ cfg_parse_local_zone(struct config_file* cfg, const char* val) goto parse_global_ipset; } ipset_name = protocol_end; - if (get_next_token(ip_table_end, &ipset_name, &ipset_name_end)) { + if (get_next_token(protocol_end, &ipset_name, &ipset_name_end)) { log_err("syntax error: expected ipset zone set name: %s", val); return 0; } @@ -2698,8 +2697,8 @@ parse_global_ipset:; strdup(name)); #ifdef USE_IPSET } else if(strcmp(type, "ipset")==0) { - return cfg_strlist_insert(&cfg->local_zones_ipset, - strdup(name)); + return cfg_str4list_insert(&cfg->local_zones_ipset, + strdup(name), "@global@", "@global@", "no-ttl"); #endif } else { return cfg_str2list_insert(&cfg->local_zones, strdup(buf), From c1879aeaa76155fb38164fafba4c64d9f547b392 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Fri, 25 Oct 2024 22:09:09 +1100 Subject: [PATCH 14/23] Fixed BSD warning flag not updated to true --- util/configparser.y | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/configparser.y b/util/configparser.y index ec2f487c5..efcd86e68 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -60,7 +60,7 @@ static void validate_acl_action(const char* action); /* these need to be global, otherwise they cannot be used inside yacc */ extern struct config_parser_state* cfg_parser; -static bool ttl_pf_have_warned = false; +static bool ttl_pf_has_warned = false; #if 0 #define OUTYY(s) printf s /* used ONLY when debugging */ @@ -2435,12 +2435,12 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($6); } else { #ifdef HAVE_NET_PFVAR_H - if (!ttl_pf_have_warned && strncmp($6, "ttl", 3) == 0) { + if (!ttl_pf_has_warned && strncmp($6, "ttl", 3) == 0) { yywarn( "local-zone ipset: per-address TTL not supported in" "BSD packet filter tables, ignoring" ); - ttl_pf_have_warned = false; + ttl_pf_has_warned = true; } #endif size_t len = strlen($2); @@ -3462,12 +3462,12 @@ view_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG free($6); } else { #ifdef HAVE_NET_PFVAR_H - if (!ttl_pf_have_warned && strncmp($6, "ttl", 3) == 0) { + if (!ttl_pf_has_warned && strncmp($6, "ttl", 3) == 0) { yywarn( "local-zone ipset: per-address TTL not supported in" "BSD packet filter tables, ignoring" ); - ttl_pf_have_warned = false; + ttl_pf_has_warned = true; } #endif size_t len = strlen($2); From 89dedeeb1e9177f382161d453d697664583b5321 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Mon, 28 Oct 2024 15:21:23 +1100 Subject: [PATCH 15/23] Added tests --- testdata/ipset_inline.tdir/ipset_inline.conf | 18 +++++ testdata/ipset_inline.tdir/ipset_inline.dsc | 16 +++++ testdata/ipset_inline.tdir/ipset_inline.post | 13 ++++ testdata/ipset_inline.tdir/ipset_inline.pre | 38 +++++++++++ testdata/ipset_inline.tdir/ipset_inline.test | 66 +++++++++++++++++++ .../ipset_inline.tdir/ipset_inline.testns | 43 ++++++++++++ 6 files changed, 194 insertions(+) create mode 100644 testdata/ipset_inline.tdir/ipset_inline.conf create mode 100644 testdata/ipset_inline.tdir/ipset_inline.dsc create mode 100644 testdata/ipset_inline.tdir/ipset_inline.post create mode 100644 testdata/ipset_inline.tdir/ipset_inline.pre create mode 100644 testdata/ipset_inline.tdir/ipset_inline.test create mode 100644 testdata/ipset_inline.tdir/ipset_inline.testns diff --git a/testdata/ipset_inline.tdir/ipset_inline.conf b/testdata/ipset_inline.tdir/ipset_inline.conf new file mode 100644 index 000000000..4e893e626 --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.conf @@ -0,0 +1,18 @@ +server: + verbosity: 3 + num-threads: 1 + module-config: "ipset iterator" + outgoing-range: 16 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + local-zone: "example.net." ipset ipv4 anothermadeupnamefor4 ttl + local-zone: "example.net." ipset ipv6 anothermadeupnamefor6 no-ttl +stub-zone: + name: "example.net." + stub-addr: "127.0.0.1@@TOPORT@" diff --git a/testdata/ipset_inline.tdir/ipset_inline.dsc b/testdata/ipset_inline.tdir/ipset_inline.dsc new file mode 100644 index 000000000..0872b2c0d --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.dsc @@ -0,0 +1,16 @@ +BaseName: ipset_inline +Version: 1.0 +Description: mock test ipset module with inline declarations +CreationDate: Wed Mar 2 13:00:38 CET 2022 +Maintainer: George Thessalonikefs +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: ipset_inline.pre +Post: ipset_inline.post +Test: ipset_inline.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/ipset_inline.tdir/ipset_inline.post b/testdata/ipset_inline.tdir/ipset_inline.post new file mode 100644 index 000000000..0b0f914ee --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.post @@ -0,0 +1,13 @@ +# #-- ipset_inline.post --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# source the test var file when it's there +[ -f .tpkg.var.test ] && source .tpkg.var.test +# +# do your teardown here +. ../common.sh +PRE="../.." +kill_pid $FWD_PID +kill_pid $UNBOUND_PID +cat unbound.log +exit 0 diff --git a/testdata/ipset_inline.tdir/ipset_inline.pre b/testdata/ipset_inline.tdir/ipset_inline.pre new file mode 100644 index 000000000..e62039ed5 --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.pre @@ -0,0 +1,38 @@ +# #-- ipset_inline.pre--# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ../common.sh + +PRE="../.." +if grep "define USE_IPSET 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi +if grep "define HAVE_NET_PFVAR_H 1" $PRE/config.h; then + if test ! -f /dev/pf; then + skip_test "no /dev/pf" + fi +fi + +get_random_port 2 +UNBOUND_PORT=$RND_PORT +FWD_PORT=$(($RND_PORT + 1)) +echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test +echo "FWD_PORT=$FWD_PORT" >> .tpkg.var.test + +# start forwarder +get_ldns_testns +$LDNS_TESTNS -p $FWD_PORT ipset_inline.testns >fwd.log 2>&1 & +FWD_PID=$!1 +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' < ipset_inline.conf > ub.conf +# start unbound in the background +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log diff --git a/testdata/ipset_inline.tdir/ipset_inline.test b/testdata/ipset_inline.tdir/ipset_inline.test new file mode 100644 index 000000000..6b9d9ab3c --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.test @@ -0,0 +1,66 @@ +# #-- ipset_inline.test --# +# source the master var file when it's there +[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master +# use .tpkg.var.test for in test variable passing +[ -f .tpkg.var.test ] && source .tpkg.var.test + +. ...netmon.sh +PRE="../.." + +# Global ipset declaration + +# Make all the queries. They need to succeed by the way. +echo "> dig cname.example.net. A" +dig @127.0.0.1 -p $UNBOUND_PORT cname.example.net. A | tee outfile +echo "> check answer" +if grep "1.1.1.1" outfile; then + echo "OK" +else + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +fi +echo "> check ipset" +if grep "ipset: add 1.1.1.1 to anothermadeupnamefor4 for target.example.net. with ttl 3600" unbound.log; then + echo "ipset OK" +else + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +fi + +echo "> dig cname.example.net. AAAA" +dig @127.0.0.1 -p $UNBOUND_PORT cname.example.net. AAAA | tee outfile +echo "> check answer" +if grep "::1" outfile; then + echo "OK" +else + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +fi +echo "> check ipset" +if grep "ipset: add ::1 to anothermadeupnamefor6 for target.example.net." unbound.log; then + echo "ipset OK" +else + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +fi + +# Finalisation + +echo "> cat logfiles" +cat tap.log +cat tap.errlog +cat fwd.log +echo "> OK" +exit 0 diff --git a/testdata/ipset_inline.tdir/ipset_inline.testns b/testdata/ipset_inline.tdir/ipset_inline.testns new file mode 100644 index 000000000..683317ddf --- /dev/null +++ b/testdata/ipset_inline.tdir/ipset_inline.testns @@ -0,0 +1,43 @@ +; nameserver test file +$ORIGIN example.net. +$TTL 3600 + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +cname IN A +SECTION ANSWER +cname IN CNAME target.example.net. +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +cname IN AAAA +SECTION ANSWER +cname IN CNAME target.example.net. +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +target IN A +SECTION ANSWER +target IN A 1.1.1.1 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +target IN AAAA +SECTION ANSWER +target IN AAAA ::1 +ENTRY_END From 11c7cab970e5ec0c815168855bd3b83f42be5af7 Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Mon, 28 Oct 2024 15:24:26 +1100 Subject: [PATCH 16/23] Added tests --- testdata/ipset_inline.tdir/ipset_inline.dsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/ipset_inline.tdir/ipset_inline.dsc b/testdata/ipset_inline.tdir/ipset_inline.dsc index 0872b2c0d..85ed78e9e 100644 --- a/testdata/ipset_inline.tdir/ipset_inline.dsc +++ b/testdata/ipset_inline.tdir/ipset_inline.dsc @@ -1,8 +1,8 @@ BaseName: ipset_inline Version: 1.0 Description: mock test ipset module with inline declarations -CreationDate: Wed Mar 2 13:00:38 CET 2022 -Maintainer: George Thessalonikefs +CreationDate: Mon Oct 28 15:22:32 AEST 2024 +Maintainer: Jack Kilrain Category: Component: CmdDepends: From 3e037da59309b8b20da104280ab5f8eaeed9bb2b Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Mon, 28 Oct 2024 16:32:16 +1100 Subject: [PATCH 17/23] Updated docs --- doc/unbound.conf.5.in | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index da494087c..ab8c88bb0 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "@date@" "NLnet Labs" "unbound @version@" +.TH "unbound.conf" "5" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" .\" .\" unbound.conf.5 -- unbound.conf manual .\" @@ -1258,6 +1258,11 @@ The python module can be listed in different places, it then processes the output of the module it is just before. The dynlib module can be listed pretty much anywhere, it is only a very thin wrapper that allows dynamic libraries to run in its place. +.IP +When the server is built with ipset support and has is run with the \fICAP_NET_ADMIN\fR +capability, the ipset module can be utilised. Example configuration for this is +"\fIipset iterator\fR". I can easily be combined with any other module without +any issues. .TP .B trust\-anchor\-file: \fI File with trusted keys for validation. Both DS and DNSKEY entries can appear @@ -1507,7 +1512,7 @@ Configure a local zone. The type determines the answer to give if there is no match from local\-data. The types are deny, refuse, static, transparent, redirect, nodefault, typetransparent, inform, inform_deny, inform_redirect, always_transparent, block_a, always_refuse, always_nxdomain, -always_null, noview, and are explained below. After that the default settings +always_null, noview, ipset, and are explained below. After that the default settings are listed. Use local\-data: to enter data into the local zone. Answers for local zones are authoritative DNS answers. By default the zones are class IN. .IP @@ -1620,6 +1625,30 @@ also turn off default contents for the zone. The 'nodefault' option has no other effect than turning off default contents for the given zone. Use \fInodefault\fR if you use exactly that zone, if you want to use a subzone, use \fItransparent\fR. +.TP 10 +\h'5'\fInodefault\fR +Used to specify an ipset to insert resolved addresses into. If the deprecated +global \fIipset\fR block is used, then it can be referenced using the form +.nf +server: + lcoal\-zone: "example.com." ipset +ipset: + name\-v4: +.fi +However, per-rule declarations are also supported where delineation of +addresses is required. This is done via the form: +.nf +local\-zone: "example.net." ipset +.fi +Here you can specify the \fIprotocol\fR as \fIipv4\fR or \fIipv6\fR, the +name of the ipset and finally whether to use the DNS record TTL as an +auto-expiry on the inserted ipset entry. Note that on a BSD distribution, +where the server is compiled with the packet filter framework, there is +no support for TTLs to be set on individual table entries. The only support +that pf provides is invoking manual expiry of table entries past a delta of +\fIn\fR seconds via the \fIpfctl -t
-T expire \fR flag. +See \fIpftcl\fR(8) Thus no support is added there for TTLs and a suitable warning +is raised from the parser when the config is checked. .P The default zones are localhost, reverse 127.0.0.1 and ::1, the home.arpa, the onion, test, invalid and the AS112 zones. The AS112 zones are reverse From d415337f23e7fd72ab0bab5bbdda1ef300f9b66b Mon Sep 17 00:00:00 2001 From: EngineersBox Date: Mon, 28 Oct 2024 16:33:31 +1100 Subject: [PATCH 18/23] Updated docs --- doc/unbound.conf.5.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index ab8c88bb0..2f9f9caa4 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1,4 +1,4 @@ -.TH "unbound.conf" "5" "Oct 17, 2024" "NLnet Labs" "unbound 1.22.0" +.TH "unbound.conf" "5" "@date@" "NLnet Labs" "unbound @version@" .\" .\" unbound.conf.5 -- unbound.conf manual .\" From 1b39c41be37099aae71050cab78a17cde51aa251 Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Mon, 4 Nov 2024 22:42:09 +1100 Subject: [PATCH 19/23] Fixed timeouts --- ipset/ipset.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 9dddd731c..4b408e801 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -150,7 +150,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, unsigned int port_id; struct nlmsghdr *nlh; struct nfgenmsg *nfg; - struct nlattr *nested[3]; + struct nlattr *nested[2]; char* recv_buffer; static char buffer[BUFF_LEN]; @@ -165,7 +165,14 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, nlh = mnl_nlmsg_put_header(buffer); nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8); - nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + if (ttl >= 0) { + // Replace if we a TTL to extend the entry time + nlh->nlmsg_flags |= NLM_F_REPLACE; + } else { + // Don't replace if we have no TTL since entry doesn't expire + nlh->nlmsg_flags |= NLM_F_EXCL; + } nlh->nlmsg_seq = seq = time(NULL); nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg)); @@ -181,9 +188,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, | NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr); mnl_attr_nest_end(nlh, nested[1]); if (ttl >= 0) { - nested[2] = mnl_attr_nest_start(nlh, IPSET_ATTR_TIMEOUT); - mnl_attr_put(nlh, NLA_F_NET_BYTEORDER, sizeof(time_t), &ttl); - mnl_attr_nest_end(nlh, nested[2]); + mnl_attr_put_u32(nlh, IPSET_ATTR_TIMEOUT | NLA_F_NET_BYTEORDER, ttl); } mnl_attr_nest_end(nlh, nested[0]); From 839b7ab8c320d1faee725a48a7f3c689d7d779b8 Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Mon, 4 Nov 2024 22:52:30 +1100 Subject: [PATCH 20/23] Ensured creation with no TTL and no replacement enacted --- ipset/ipset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 97bd2c2d0..7a0129ee3 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -168,7 +168,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; if (ttl >= 0) { // Replace if we a TTL to extend the entry time - nlh->nlmsg_flags |= NLM_F_REPLACE; + nlh->nlmsg_flags |= NLM_F_REPLACE | NLM_F_CREATE; } else { // Don't replace if we have no TTL since entry doesn't expire nlh->nlmsg_flags |= NLM_F_EXCL; From 1a948577cd555cd024562f6c888dcbc5af73ed47 Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Mon, 4 Nov 2024 23:09:41 +1100 Subject: [PATCH 21/23] Ensured creation with no TTL and no replacement enacted --- ipset/ipset.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 7a0129ee3..7edab899e 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -163,10 +163,11 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, return -1; } + const bool set_ttl = ttl >= 0; nlh = mnl_nlmsg_put_header(buffer); nlh->nlmsg_type = IPSET_CMD_ADD | (NFNL_SUBSYS_IPSET << 8); nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; - if (ttl >= 0) { + if (set_ttl) { // Replace if we a TTL to extend the entry time nlh->nlmsg_flags |= NLM_F_REPLACE | NLM_F_CREATE; } else { @@ -187,7 +188,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, mnl_attr_put(nlh, (af == AF_INET ? IPSET_ATTR_IPADDR_IPV4 : IPSET_ATTR_IPADDR_IPV6) | NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr); mnl_attr_nest_end(nlh, nested[1]); - if (ttl >= 0) { + if (set_ttl) { mnl_attr_put_u32(nlh, IPSET_ATTR_TIMEOUT | NLA_F_NET_BYTEORDER, ttl); } mnl_attr_nest_end(nlh, nested[0]); @@ -210,6 +211,12 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, return -1; } result = mnl_cb_run(recv_buffer, result, seq, port_id, NULL, NULL); + if (set_ttl && errno == IPSET_ERR_EXIST) { + // If we have no TTL, then we don't replace entries. + // This error indicates we already have an entry, so we + // can ignore it and move on. + break; + } if (result < 0) { log_err("ipset: netlink response had error: %s", strerror(errno)); free(recv_buffer); From 1c6c67cb7277936dc92c9c328a1be996daf03995 Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Mon, 4 Nov 2024 23:31:47 +1100 Subject: [PATCH 22/23] Ensured creation with no TTL and no replacement enacted --- ipset/ipset.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index 7edab899e..5d2bec818 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -211,7 +211,7 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, return -1; } result = mnl_cb_run(recv_buffer, result, seq, port_id, NULL, NULL); - if (set_ttl && errno == IPSET_ERR_EXIST) { + if (!set_ttl && errno == IPSET_ERR_EXIST) { // If we have no TTL, then we don't replace entries. // This error indicates we already have an entry, so we // can ignore it and move on. From deb6ec8a29abcd47ffdfb95766612539e64568df Mon Sep 17 00:00:00 2001 From: Jack Kilrain Date: Tue, 5 Nov 2024 00:49:58 +1100 Subject: [PATCH 23/23] Added conversion from host byte order to net byte order --- doc/unbound.conf.5.in | 16 +++++++++------- ipset/ipset.c | 14 +++++++++++++- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 2f9f9caa4..70af9dadc 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1642,13 +1642,15 @@ local\-zone: "example.net." ipset .fi Here you can specify the \fIprotocol\fR as \fIipv4\fR or \fIipv6\fR, the name of the ipset and finally whether to use the DNS record TTL as an -auto-expiry on the inserted ipset entry. Note that on a BSD distribution, -where the server is compiled with the packet filter framework, there is -no support for TTLs to be set on individual table entries. The only support -that pf provides is invoking manual expiry of table entries past a delta of -\fIn\fR seconds via the \fIpfctl -t
-T expire \fR flag. -See \fIpftcl\fR(8) Thus no support is added there for TTLs and a suitable warning -is raised from the parser when the config is checked. +auto-expiry on the inserted ipset entry. When declaring a local-zone with +TTL support, the associated ipset must be created with \fIIPSET_EXT_TIMEOUT\fR, +or with the \fIipset\fR CLI, using the \fItimeout \fR option. +Note that on a BSD distribution, where the server is compiled with the packet +filter framework, there is no support for TTLs to be set on individual table +entries. The only support that pf provides is invoking manual expiry of table +entries past a delta of \fIn\fR seconds via the \fIpfctl -t
-T expire \fR +flag. See \fIpftcl\fR(8) Thus no support is added there for TTLs and a suitable +warning is raised from the parser when the config is checked. .P The default zones are localhost, reverse 127.0.0.1 and ::1, the home.arpa, the onion, test, invalid and the AS112 zones. The AS112 zones are reverse diff --git a/ipset/ipset.c b/ipset/ipset.c index 5d2bec818..ac741602d 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -189,7 +189,19 @@ static int add_to_ipset(filter_dev dev, const char *setname, const void *ipaddr, | NLA_F_NET_BYTEORDER, (af == AF_INET ? sizeof(struct in_addr) : sizeof(struct in6_addr)), ipaddr); mnl_attr_nest_end(nlh, nested[1]); if (set_ttl) { - mnl_attr_put_u32(nlh, IPSET_ATTR_TIMEOUT | NLA_F_NET_BYTEORDER, ttl); + // Netlink packets are packed based on a pointer and data size + // to memcpy into an appropriately sized buffer within the packet + // data section. Thus we need to ensure that the TTL is in a u32 + // sized variable, otherwise we would end up copying the upper + // 32 bits of a 64 bit integer. + const uint32_t entry_ttl = (uint32_t) ttl > UINT32_MAX ? UINT32_MAX : ttl; + mnl_attr_put_u32( + nlh, + IPSET_ATTR_TIMEOUT | NLA_F_NET_BYTEORDER, + // Expecting net byte order, we should convert from host order + // into net byte order + htonl(entry_ttl) + ); } mnl_attr_nest_end(nlh, nested[0]);