From b5cc8b6c59c0a43c837234a904fb41326cdd2e04 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 24 Apr 2023 16:15:56 +0200 Subject: [PATCH 01/64] - Generalise the proxy protocol code --- daemon/worker.c | 2 + libunbound/libworker.c | 2 + testcode/streamtcp.c | 7 ++- util/netevent.c | 33 +++++++--- util/proxy_protocol.c | 136 +++++++++++++++++++++++++++-------------- util/proxy_protocol.h | 42 ++++++++++--- 6 files changed, 161 insertions(+), 61 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 99dcf9940..46331376d 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -66,6 +66,7 @@ #include "util/data/msgencode.h" #include "util/data/dname.h" #include "util/fptr_wlist.h" +#include "util/proxy_protocol.h" #include "util/tube.h" #include "util/edns.h" #include "iterator/iter_fwd.h" @@ -2168,6 +2169,7 @@ worker_init(struct worker* worker, struct config_file *cfg, worker->env.cfg->stat_interval); worker_restart_timer(worker); } + pp_init(&sldns_write_uint16, &sldns_write_uint32); return 1; } diff --git a/libunbound/libworker.c b/libunbound/libworker.c index ebc1df2e5..7d08b488a 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -62,6 +62,7 @@ #include "util/random.h" #include "util/config_file.h" #include "util/netevent.h" +#include "util/proxy_protocol.h" #include "util/storage/lookup3.h" #include "util/storage/slabhash.h" #include "util/net_help.h" @@ -263,6 +264,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) w->env->kill_sub = &mesh_state_delete; w->env->detect_cycle = &mesh_detect_cycle; comm_base_timept(w->base, &w->env->now, &w->env->now_tv); + pp_init(&sldns_write_uint16, &sldns_write_uint32); return w; } diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index b2c0d5328..53b29852f 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -353,6 +353,7 @@ static int parse_pp2_client(const char* pp2_client, int udp, sldns_buffer* proxy_buf) { struct sockaddr_storage pp2_addr; + size_t bytes_written; socklen_t pp2_addrlen = 0; memset(&pp2_addr, 0, sizeof(pp2_addr)); if(*pp2_client == 0) return 0; @@ -361,7 +362,9 @@ static int parse_pp2_client(const char* pp2_client, int udp, exit(1); } sldns_buffer_clear(proxy_buf); - pp2_write_to_buf(proxy_buf, &pp2_addr, !udp); + bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf), + sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp); + sldns_buffer_set_position(proxy_buf, bytes_written); sldns_buffer_flip(proxy_buf); return 1; } @@ -529,6 +532,8 @@ int main(int argc, char** argv) break; case 'p': pp2_client = optarg; + pp_init(&sldns_write_uint16, + &sldns_write_uint32); break; case 'a': onarrival = 1; diff --git a/util/netevent.c b/util/netevent.c index fe3d51164..c0fe1e6a6 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -761,8 +761,11 @@ static int udp_recv_needs_log(int err) static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, int stream) { size_t size; - struct pp2_header *header = pp2_read_header(buf); - if(header == NULL) return 0; + struct pp2_header *header; + int err = pp2_read_header(sldns_buffer_begin(buf), + sldns_buffer_remaining(buf)); + if(err) return 0; + header = (struct pp2_header*)sldns_buffer_begin(buf); size = PP2_HEADER_SIZE + ntohs(header->len); if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) { /* A connection from the proxy itself. @@ -803,6 +806,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Ignore the destination address; it should be us. */ break; + default: + log_err("proxy_protocol: unsupported family and " + "protocol"); + return 0; } rep->is_proxied = 1; done: @@ -1675,12 +1682,17 @@ ssl_handle_read(struct comm_point* c) } } if(c->pp2_header_state == pp2_header_init) { - header = pp2_read_header(c->buffer); - if(!header) { + int err; + err = pp2_read_header( + sldns_buffer_begin(c->buffer), + sldns_buffer_remaining(c->buffer)); + if(err) { log_err("proxy_protocol: could not parse " - "PROXYv2 header"); + "PROXYv2 header (%s)", + pp_lookup_error(err)); return 0; } + header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); if(sldns_buffer_remaining(c->buffer) < PP2_HEADER_SIZE + want_read_size) { @@ -2067,12 +2079,17 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) } } if(c->pp2_header_state == pp2_header_init) { - header = pp2_read_header(c->buffer); - if(!header) { + int err; + err = pp2_read_header( + sldns_buffer_begin(c->buffer), + sldns_buffer_remaining(c->buffer)); + if(err) { log_err("proxy_protocol: could not parse " - "PROXYv2 header"); + "PROXYv2 header (%s)", + pp_lookup_error(err)); return 0; } + header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); if(sldns_buffer_remaining(c->buffer) < PP2_HEADER_SIZE + want_read_size) { diff --git a/util/proxy_protocol.c b/util/proxy_protocol.c index 757c5141d..fd253372f 100644 --- a/util/proxy_protocol.c +++ b/util/proxy_protocol.c @@ -38,102 +38,148 @@ * * This file contains PROXY protocol functions. */ -#include "config.h" -#include "util/log.h" #include "util/proxy_protocol.h" -int -pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src, +/** + * Internal struct initialized with function pointers for writing uint16 and + * uint32. + */ +struct proxy_protocol_data { + void (*write_uint16)(void* buf, uint16_t data); + void (*write_uint32)(void* buf, uint32_t data); +}; +struct proxy_protocol_data pp_data; + +/** + * Internal lookup table; could be further generic like sldns_lookup_table + * for all the future generic stuff. + */ +struct proxy_protocol_lookup_table { + int id; + const char *text; +}; + +/** + * Internal parsing error text; could be exposed with pp_lookup_error. + */ +static struct proxy_protocol_lookup_table pp_parse_errors_data[] = { + { PP_PARSE_NOERROR, "no parse error" }, + { PP_PARSE_SIZE, "not enough space for header" }, + { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" }, + { PP_PARSE_UNKNOWN_CMD, "unknown command" }, + { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" }, +}; + +void +pp_init(void (*write_uint16)(void* buf, uint16_t data), + void (*write_uint32)(void* buf, uint32_t data)) { + pp_data.write_uint16 = write_uint16; + pp_data.write_uint32 = write_uint32; +} + +const char* +pp_lookup_error(enum pp_parse_errors error) { + return pp_parse_errors_data[error].text; +} + +size_t +pp2_write_to_buf(uint8_t* buf, size_t buflen, struct sockaddr_storage* src, int stream) { int af; + size_t expected_size; if(!src) return 0; af = (int)((struct sockaddr_in*)src)->sin_family; - if(sldns_buffer_remaining(buf) < - PP2_HEADER_SIZE + (af==AF_INET?12:36)) { + expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36); + if(buflen < expected_size) { return 0; } /* sig */ - sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN); + memcpy(buf, PP2_SIG, PP2_SIG_LEN); + buf += PP2_SIG_LEN; /* version and command */ - sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY); + *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; + buf++; if(af==AF_INET) { /* family and protocol */ - sldns_buffer_write_u8(buf, - (PP2_AF_INET<<4) | - (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + *buf = (PP2_AF_INET<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); + buf++; /* length */ - sldns_buffer_write_u16(buf, 12); + (*pp_data.write_uint16)(buf, 12); + buf += 2; /* src addr */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); + buf += 4; /* dst addr */ - sldns_buffer_write_u32(buf, 0); + (*pp_data.write_uint32)(buf, 0); + buf += 4; /* src port */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in*)src)->sin_port, 2); + buf += 2; + /* dst addr */ /* dst port */ - sldns_buffer_write_u16(buf, 0); + (*pp_data.write_uint16)(buf, 12); } else { /* family and protocol */ - sldns_buffer_write_u8(buf, - (PP2_AF_INET6<<4) | - (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + *buf = (PP2_AF_INET6<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); + buf++; /* length */ - sldns_buffer_write_u16(buf, 36); + (*pp_data.write_uint16)(buf, 36); + buf += 2; /* src addr */ - sldns_buffer_write(buf, + memcpy(buf, &((struct sockaddr_in6*)src)->sin6_addr, 16); + buf += 16; /* dst addr */ - sldns_buffer_set_at(buf, - sldns_buffer_position(buf), 0, 16); - sldns_buffer_skip(buf, 16); + memset(buf, 0, 16); + buf += 16; /* src port */ - sldns_buffer_write(buf, - &((struct sockaddr_in6*)src)->sin6_port, 2); + memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2); + buf += 2; /* dst port */ - sldns_buffer_write_u16(buf, 0); + (*pp_data.write_uint16)(buf, 0); } - return 1; + return expected_size; } -struct pp2_header* -pp2_read_header(struct sldns_buffer* buf) +int +pp2_read_header(uint8_t* buf, size_t buflen) { size_t size; - struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf); + struct pp2_header* header = (struct pp2_header*)buf; /* Try to fail all the unsupported cases first. */ - if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) { - log_err("proxy_protocol: not enough space for header"); - return NULL; + if(buflen < PP2_HEADER_SIZE) { + return PP_PARSE_SIZE; } /* Check for PROXYv2 header */ if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { - log_err("proxy_protocol: could not match PROXYv2 header"); - return NULL; + return PP_PARSE_WRONG_HEADERv2; } /* Check the length */ size = PP2_HEADER_SIZE + ntohs(header->len); - if(sldns_buffer_remaining(buf) < size) { - log_err("proxy_protocol: not enough space for header"); - return NULL; + if(buflen < size) { + return PP_PARSE_SIZE; } /* Check for supported commands */ if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && (header->ver_cmd & 0xF) != PP2_CMD_PROXY) { - log_err("proxy_protocol: unsupported command"); - return NULL; + return PP_PARSE_UNKNOWN_CMD; } /* Check for supported family and protocol */ if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ && header->fam_prot != 0x11 /* AF_INET|STREAM */ && header->fam_prot != 0x12 /* AF_INET|DGRAM */ && header->fam_prot != 0x21 /* AF_INET6|STREAM */ && - header->fam_prot != 0x22 /* AF_INET6|DGRAM */) { - log_err("proxy_protocol: unsupported family and protocol"); - return NULL; + header->fam_prot != 0x22 /* AF_INET6|DGRAM */ && + header->fam_prot != 0x31 /* AF_UNIX|STREAM */ && + header->fam_prot != 0x32 /* AF_UNIX|DGRAM */) { + return PP_PARSE_UNKNOWN_FAM_PROT; } /* We have a correct header */ - return header; + return PP_PARSE_NOERROR; } diff --git a/util/proxy_protocol.h b/util/proxy_protocol.h index 13cab9d74..f4867ad6c 100644 --- a/util/proxy_protocol.h +++ b/util/proxy_protocol.h @@ -42,7 +42,7 @@ #ifndef PROXY_PROTOCOL_H #define PROXY_PROTOCOL_H -#include "sldns/sbuffer.h" +#include "config.h" /** PROXYv2 minimum header size */ #define PP2_HEADER_SIZE 16 @@ -109,23 +109,51 @@ struct pp2_header { } addr; }; +/** + * PROXY parse errors. + */ +enum pp_parse_errors { + PP_PARSE_NOERROR = 0, + PP_PARSE_SIZE, + PP_PARSE_WRONG_HEADERv2, + PP_PARSE_UNKNOWN_CMD, + PP_PARSE_UNKNOWN_FAM_PROT, +}; + +/** + * Initialize the internal proxy structure. + * @param write_uint16: pointer to a function that can write uint16. + * @param write_uint32: pointer to a function that can write uint32. + */ +void pp_init(void (*write_uint16)(void* buf, uint16_t data), + void (*write_uint32)(void* buf, uint32_t data)); + +/** + * Lookup the parsing error description. + * @param error: parsing error from pp2_read_header. + * @return the description. + */ +const char* pp_lookup_error(enum pp_parse_errors error); + /** * Write a PROXYv2 header at the current position of the buffer. - * @param buf: the buffer to write to. + * @param buf: pointer to the buffer to write data to. + * @param buflen: available size on the buffer. * @param src: the source address. * @param stream: if the protocol is stream or datagram. * @return 1 on success, 0 on failure. */ -int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src, - int stream); +size_t pp2_write_to_buf(uint8_t* buf, size_t buflen, + struct sockaddr_storage* src, int stream); /** * Read a PROXYv2 header from the current position of the buffer. * It does initial validation and returns a pointer to the buffer position on * success. - * @param buf: the buffer to read from. - * @return the pointer to the buffer position on success, NULL on error. + * @param buf: pointer to the buffer data to read from. + * @param buflen: available size on the buffer. + * @return parsing error, 0 on success. */ -struct pp2_header* pp2_read_header(struct sldns_buffer* buf); +int pp2_read_header(uint8_t* buf, size_t buflen); #endif /* PROXY_PROTOCOL_H */ From 542f717bf9a01c5a84e357f23a17fd403be4b237 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Apr 2023 08:16:19 +0200 Subject: [PATCH 02/64] - adjust generic proxy-protocol header for IPv6 support with ifdef. --- util/proxy_protocol.c | 11 ++++++++++- util/proxy_protocol.h | 7 ++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/util/proxy_protocol.c b/util/proxy_protocol.c index fd253372f..03db06037 100644 --- a/util/proxy_protocol.c +++ b/util/proxy_protocol.c @@ -83,7 +83,12 @@ pp_lookup_error(enum pp_parse_errors error) { } size_t -pp2_write_to_buf(uint8_t* buf, size_t buflen, struct sockaddr_storage* src, +pp2_write_to_buf(uint8_t* buf, size_t buflen, +#ifdef INET6 + struct sockaddr_storage* src, +#else + struct sockaddr_in* src, +#endif int stream) { int af; @@ -123,6 +128,7 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, struct sockaddr_storage* src, /* dst port */ (*pp_data.write_uint16)(buf, 12); } else { +#ifdef INET6 /* family and protocol */ *buf = (PP2_AF_INET6<<4) | (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); @@ -142,6 +148,9 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, struct sockaddr_storage* src, buf += 2; /* dst port */ (*pp_data.write_uint16)(buf, 0); +#else + return 0; +#endif /* INET6 */ } return expected_size; } diff --git a/util/proxy_protocol.h b/util/proxy_protocol.h index f4867ad6c..58d3f8d57 100644 --- a/util/proxy_protocol.h +++ b/util/proxy_protocol.h @@ -144,7 +144,12 @@ const char* pp_lookup_error(enum pp_parse_errors error); * @return 1 on success, 0 on failure. */ size_t pp2_write_to_buf(uint8_t* buf, size_t buflen, - struct sockaddr_storage* src, int stream); +#ifdef INET6 + struct sockaddr_storage* src, +#else + struct sockaddr_in* src, +#endif + int stream); /** * Read a PROXYv2 header from the current position of the buffer. From 4bcc0a0a7aadec12e51cedb2ffbd617e4122f37c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 25 Apr 2023 16:44:58 +0200 Subject: [PATCH 03/64] streamtcp, implement IXFR=N queries, add documentation for proxy option. --- testcode/streamtcp.1 | 7 +++++++ testcode/streamtcp.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/testcode/streamtcp.1 b/testcode/streamtcp.1 index f02b168d2..629e69570 100644 --- a/testcode/streamtcp.1 +++ b/testcode/streamtcp.1 @@ -61,6 +61,13 @@ Specify the server to send the queries to. If not specified localhost (127.0.0.1 .B \-d \fIsecs Delay after the connection before sending query. This tests the timeout on the other side, eg. if shorter the connection is closed. +.TP +.B \-p \fIclient +Use proxy protocol to send the query. Specify the ipaddr@portnr of the client +to include in PROXYv2. +.TP +.B IXFR=serial +Pass the type of the query as IXFR=N to send an IXFR query with serial N. .SH "EXAMPLES" .LP Some examples of use. diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index 53b29852f..ea9b3cf49 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -79,6 +79,7 @@ static void usage(char* argv[]) printf("-d secs delay after connection before sending query\n"); printf("-s use ssl\n"); printf("-h this help text\n"); + printf("IXFR=N for the type, sends ixfr query with serial N.\n"); exit(1); } @@ -123,6 +124,8 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, { struct query_info qinfo; size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf); + int have_serial = 0; + uint32_t serial = 0; /* qname */ qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); if(!qinfo.qname) { @@ -131,7 +134,13 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, } /* qtype and qclass */ - qinfo.qtype = sldns_get_rr_type_by_name(strtype); + if(strncasecmp(strtype, "IXFR=", 5) == 0) { + serial = (uint32_t)atoi(strtype+5); + have_serial = 1; + qinfo.qtype = LDNS_RR_TYPE_IXFR; + } else { + qinfo.qtype = sldns_get_rr_type_by_name(strtype); + } qinfo.qclass = sldns_get_rr_class_by_name(strclass); /* clear local alias */ @@ -142,6 +151,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, sldns_buffer_write_u16_at(buf, 0, id); sldns_buffer_write_u16_at(buf, 2, BIT_RD); + if(have_serial && qinfo.qtype == LDNS_RR_TYPE_IXFR) { + /* Attach serial to SOA record in the authority section. */ + sldns_buffer_set_position(buf, sldns_buffer_limit(buf)); + sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf)); + /* Write compressed reference to the query */ + sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE)); + sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA); + sldns_buffer_write_u16(buf, qinfo.qclass); + sldns_buffer_write_u32(buf, 3600); /* TTL */ + sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */ + sldns_buffer_write_u8(buf, 0); /* primary "." */ + sldns_buffer_write_u8(buf, 0); /* email "." */ + sldns_buffer_write_u32(buf, serial); /* serial */ + sldns_buffer_write_u32(buf, 0); /* refresh */ + sldns_buffer_write_u32(buf, 0); /* retry */ + sldns_buffer_write_u32(buf, 0); /* expire */ + sldns_buffer_write_u32(buf, 0); /* minimum */ + LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1); + sldns_buffer_flip(buf); + } + if(1) { /* add EDNS DO */ struct edns_data edns; From 15a2add0f85f6807ed8db67982e1689cb11c4a27 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 26 Apr 2023 11:57:10 +0200 Subject: [PATCH 04/64] streamtcp, implement NOTIFY[=N] that sends a notify packet. --- testcode/streamtcp.1 | 4 +++ testcode/streamtcp.c | 60 +++++++++++++++++++++++++++++++------------- 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/testcode/streamtcp.1 b/testcode/streamtcp.1 index 629e69570..55ed4a279 100644 --- a/testcode/streamtcp.1 +++ b/testcode/streamtcp.1 @@ -68,6 +68,10 @@ to include in PROXYv2. .TP .B IXFR=serial Pass the type of the query as IXFR=N to send an IXFR query with serial N. +.TP +.B NOTIFY[=serial] +Pass the type of the query as NOTIFY[=N] to send a notify packet. The serial N +of the new zone can be included. .SH "EXAMPLES" .LP Some examples of use. diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index ea9b3cf49..0cd9f185f 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -80,6 +80,7 @@ static void usage(char* argv[]) printf("-s use ssl\n"); printf("-h this help text\n"); printf("IXFR=N for the type, sends ixfr query with serial N.\n"); + printf("NOTIFY[=N] for the type, sends notify. Can set new zone serial N.\n"); exit(1); } @@ -116,6 +117,29 @@ open_svr(const char* svr, int udp, struct sockaddr_storage* addr, return fd; } +/** Append a SOA record with serial number */ +static void +write_soa_serial_to_buf(sldns_buffer* buf, struct query_info* qinfo, + uint32_t serial) +{ + sldns_buffer_set_position(buf, sldns_buffer_limit(buf)); + sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf)); + /* Write compressed reference to the query */ + sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE)); + sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA); + sldns_buffer_write_u16(buf, qinfo->qclass); + sldns_buffer_write_u32(buf, 3600); /* TTL */ + sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */ + sldns_buffer_write_u8(buf, 0); /* primary "." */ + sldns_buffer_write_u8(buf, 0); /* email "." */ + sldns_buffer_write_u32(buf, serial); /* serial */ + sldns_buffer_write_u32(buf, 0); /* refresh */ + sldns_buffer_write_u32(buf, 0); /* retry */ + sldns_buffer_write_u32(buf, 0); /* expire */ + sldns_buffer_write_u32(buf, 0); /* minimum */ + sldns_buffer_flip(buf); +} + /** write a query over the TCP fd */ static void write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, @@ -124,7 +148,7 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, { struct query_info qinfo; size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf); - int have_serial = 0; + int have_serial = 0, is_notify = 0; uint32_t serial = 0; /* qname */ qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); @@ -138,6 +162,14 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, serial = (uint32_t)atoi(strtype+5); have_serial = 1; qinfo.qtype = LDNS_RR_TYPE_IXFR; + } else if(strcasecmp(strtype, "NOTIFY") == 0) { + is_notify = 1; + qinfo.qtype = LDNS_RR_TYPE_SOA; + } else if(strncasecmp(strtype, "NOTIFY=", 7) == 0) { + serial = (uint32_t)atoi(strtype+7); + have_serial = 1; + is_notify = 1; + qinfo.qtype = LDNS_RR_TYPE_SOA; } else { qinfo.qtype = sldns_get_rr_type_by_name(strtype); } @@ -153,23 +185,17 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, if(have_serial && qinfo.qtype == LDNS_RR_TYPE_IXFR) { /* Attach serial to SOA record in the authority section. */ - sldns_buffer_set_position(buf, sldns_buffer_limit(buf)); - sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf)); - /* Write compressed reference to the query */ - sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE)); - sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA); - sldns_buffer_write_u16(buf, qinfo.qclass); - sldns_buffer_write_u32(buf, 3600); /* TTL */ - sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */ - sldns_buffer_write_u8(buf, 0); /* primary "." */ - sldns_buffer_write_u8(buf, 0); /* email "." */ - sldns_buffer_write_u32(buf, serial); /* serial */ - sldns_buffer_write_u32(buf, 0); /* refresh */ - sldns_buffer_write_u32(buf, 0); /* retry */ - sldns_buffer_write_u32(buf, 0); /* expire */ - sldns_buffer_write_u32(buf, 0); /* minimum */ + write_soa_serial_to_buf(buf, &qinfo, serial); LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1); - sldns_buffer_flip(buf); + } + if(is_notify) { + LDNS_OPCODE_SET(sldns_buffer_begin(buf), LDNS_PACKET_NOTIFY); + LDNS_RD_CLR(sldns_buffer_begin(buf)); + LDNS_AA_SET(sldns_buffer_begin(buf)); + if(have_serial) { + write_soa_serial_to_buf(buf, &qinfo, serial); + LDNS_ANCOUNT_SET(sldns_buffer_begin(buf), 1); + } } if(1) { From 80153decd1a2dd66e97f81f305711094629303c0 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 May 2023 14:36:29 +0200 Subject: [PATCH 05/64] - Fix proxy-protocol buffer checks when writing and read from buffer. --- util/netevent.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/util/netevent.c b/util/netevent.c index c0fe1e6a6..4bbed6364 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -1685,7 +1685,7 @@ ssl_handle_read(struct comm_point* c) int err; err = pp2_read_header( sldns_buffer_begin(c->buffer), - sldns_buffer_remaining(c->buffer)); + sldns_buffer_position(c->buffer)); if(err) { log_err("proxy_protocol: could not parse " "PROXYv2 header (%s)", @@ -1694,7 +1694,7 @@ ssl_handle_read(struct comm_point* c) } header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); - if(sldns_buffer_remaining(c->buffer) < + if(sldns_buffer_limit(c->buffer) < PP2_HEADER_SIZE + want_read_size) { log_err_addr("proxy_protocol: not enough " "buffer size to read PROXYv2 header", "", @@ -1753,6 +1753,7 @@ ssl_handle_read(struct comm_point* c) c->repinfo.remote_addrlen); return 0; } + sldns_buffer_flip(c->buffer); if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { log_err_addr("proxy_protocol: could not consume " "PROXYv2 header", "", &c->repinfo.remote_addr, @@ -2082,7 +2083,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) int err; err = pp2_read_header( sldns_buffer_begin(c->buffer), - sldns_buffer_remaining(c->buffer)); + sldns_buffer_position(c->buffer)); if(err) { log_err("proxy_protocol: could not parse " "PROXYv2 header (%s)", @@ -2091,7 +2092,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) } header = (struct pp2_header*)sldns_buffer_begin(c->buffer); want_read_size = ntohs(header->len); - if(sldns_buffer_remaining(c->buffer) < + if(sldns_buffer_limit(c->buffer) < PP2_HEADER_SIZE + want_read_size) { log_err_addr("proxy_protocol: not enough " "buffer size to read PROXYv2 header", "", @@ -2128,6 +2129,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) c->repinfo.remote_addrlen); return 0; } + sldns_buffer_flip(c->buffer); if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { log_err_addr("proxy_protocol: could not consume " "PROXYv2 header", "", &c->repinfo.remote_addr, From cac1d13fda1072521031fa1779a161dc8203a774 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 2 May 2023 14:54:51 +0200 Subject: [PATCH 06/64] - Fix proxy-protocol to read header in multiple reads and check buffer size. --- util/netevent.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/netevent.c b/util/netevent.c index 4bbed6364..edb9da8b5 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -1677,6 +1677,7 @@ ssl_handle_read(struct comm_point* c) return 0; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_init; } @@ -1685,7 +1686,7 @@ ssl_handle_read(struct comm_point* c) int err; err = pp2_read_header( sldns_buffer_begin(c->buffer), - sldns_buffer_position(c->buffer)); + sldns_buffer_limit(c->buffer)); if(err) { log_err("proxy_protocol: could not parse " "PROXYv2 header (%s)", @@ -1743,6 +1744,7 @@ ssl_handle_read(struct comm_point* c) return 0; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_done; } @@ -2075,6 +2077,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) goto recv_error_initial; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_init; } @@ -2083,7 +2086,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) int err; err = pp2_read_header( sldns_buffer_begin(c->buffer), - sldns_buffer_position(c->buffer)); + sldns_buffer_limit(c->buffer)); if(err) { log_err("proxy_protocol: could not parse " "PROXYv2 header (%s)", @@ -2119,6 +2122,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok) goto recv_error; } c->tcp_byte_count += r; + sldns_buffer_skip(c->buffer, r); if(c->tcp_byte_count != current_read_size) return 1; c->pp2_header_state = pp2_header_done; } From 2cd7c719ef629e6548081bd381ba5dc12e133568 Mon Sep 17 00:00:00 2001 From: Florian Obser Date: Tue, 5 Sep 2023 17:35:30 +0200 Subject: [PATCH 07/64] Prevent warnings from -Wmissing-prototypes. --- util/rfc_1982.c | 1 + util/siphash.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/util/rfc_1982.c b/util/rfc_1982.c index c28deded6..cf64e21d0 100644 --- a/util/rfc_1982.c +++ b/util/rfc_1982.c @@ -39,6 +39,7 @@ * This file contains functions for RFC 1982 serial number arithmetic. */ #include "config.h" +#include "util/rfc_1982.h" int compare_1982(uint32_t a, uint32_t b) diff --git a/util/siphash.c b/util/siphash.c index 0e1b597d0..32797dff6 100644 --- a/util/siphash.c +++ b/util/siphash.c @@ -26,6 +26,11 @@ */ #include "config.h" +/** EDIT + * prevent warning from -Wmissing-prototypes + */ +#include "util/siphash.h" + /* default: SipHash-2-4 */ #define cROUNDS 2 #define dROUNDS 4 From 1143050ea63c1f31124d2d01e6c137558ab44f35 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 6 Sep 2023 09:49:35 +0200 Subject: [PATCH 08/64] Changelog note for #931 - Merge #931: Prevent warnings from -Wmissing-prototypes. --- doc/Changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 64845293b..bc6c14321 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +6 September 2023: Wouter + - Merge #931: Prevent warnings from -Wmissing-prototypes. + 31 August 2023: Wouter - Fix autoconf 2.69 warnings in configure. - Fix #927: unbound 1.18.0 make test error. Fix make test without SHA1. From dfc00271d17752481506b9a4b473c4eb177ac980 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 11:08:04 +0200 Subject: [PATCH 09/64] - Fix to scrub resource records of type A and AAAA that have an inappropriate size. They are removed from responses. --- doc/Changelog | 4 + iterator/iter_priv.c | 13 +- iterator/iter_priv.h | 7 + iterator/iter_scrub.c | 46 +++++ testdata/dns64_lookup.rpl | 48 ------ testdata/iter_scrub_rr_length.rpl | 273 ++++++++++++++++++++++++++++++ 6 files changed, 338 insertions(+), 53 deletions(-) create mode 100644 testdata/iter_scrub_rr_length.rpl diff --git a/doc/Changelog b/doc/Changelog index bc6c14321..fe03f4378 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +7 September 2023: Wouter + - Fix to scrub resource records of type A and AAAA that have an + inappropriate size. They are removed from responses. + 6 September 2023: Wouter - Merge #931: Prevent warnings from -Wmissing-prototypes. diff --git a/iterator/iter_priv.c b/iterator/iter_priv.c index 90bea1746..675c93907 100644 --- a/iterator/iter_priv.c +++ b/iterator/iter_priv.c @@ -208,14 +208,17 @@ size_t priv_get_mem(struct iter_priv* priv) } /** remove RR from msgparse RRset, return true if rrset is entirely bad */ -static int -remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, +int +msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen) { if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { uint8_t buf[LDNS_MAX_DOMAINLEN+1]; dname_pkt_copy(pkt, buf, rrset->dname); - log_name_addr(VERB_QUERY, str, buf, addr, addrlen); + if(addr) + log_name_addr(VERB_QUERY, str, buf, addr, addrlen); + else log_nametypeclass(VERB_QUERY, str, buf, + rrset->type, ntohs(rrset->rrset_class)); } if(prev) prev->next = (*rr)->next; @@ -261,7 +264,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) return 1; continue; } @@ -284,7 +287,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET6_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) return 1; continue; } diff --git a/iterator/iter_priv.h b/iterator/iter_priv.h index 0430d57e3..7f58ec2f7 100644 --- a/iterator/iter_priv.h +++ b/iterator/iter_priv.h @@ -48,6 +48,8 @@ struct iter_env; struct config_file; struct regional; struct rrset_parse; +struct rr_parse; +struct rrset_parse; /** * Iterator priv structure @@ -109,4 +111,9 @@ int priv_rrset_bad(struct iter_priv* priv, struct sldns_buffer* pkt, */ size_t priv_get_mem(struct iter_priv* priv); +/** remove RR from msgparse RRset, return true if rrset is entirely bad */ +int msgparse_rrset_remove_rr(const char* str, struct sldns_buffer* pkt, + struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse** rr, + struct sockaddr_storage* addr, socklen_t addrlen); + #endif /* ITERATOR_ITER_PRIV_H */ diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index d1fedcd0f..c34ccfd3c 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -716,6 +716,45 @@ static int sanitize_nsec_is_overreach(sldns_buffer* pkt, return 0; } +/** Remove individual RRs, if the length is wrong. Returns true if the RRset + * has been removed. */ +static int +scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, + struct rrset_parse* prev, struct rrset_parse** rrset) +{ + struct rr_parse* rr, *rr_prev = NULL; + for(rr = (*rrset)->rr_first; rr; rr = rr->next) { + + /* Sanity check for length of records + * An A record should be 6 bytes only + * (2 bytes for length and 4 for IPv4 addr)*/ + if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) { + if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:", + pkt, *rrset, rr_prev, &rr, NULL, 0)) { + remove_rrset("sanitize: removing type A RRset of inappropriate length:", + pkt, msg, prev, rrset); + return 1; + } + continue; + } + + /* Sanity check for length of records + * An AAAA record should be 18 bytes only + * (2 bytes for length and 16 for IPv6 addr)*/ + if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) { + if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:", + pkt, *rrset, rr_prev, &rr, NULL, 0)) { + remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:", + pkt, msg, prev, rrset); + return 1; + } + continue; + } + rr_prev = rr; + } + return 0; +} + /** * Given a response event, remove suspect RRsets from the response. * "Suspect" rrsets are potentially poison. Note that this routine expects @@ -781,6 +820,13 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, rrset = msg->rrset_first; while(rrset) { + /* Sanity check for length of records */ + if(rrset->type == LDNS_RR_TYPE_A || + rrset->type == LDNS_RR_TYPE_AAAA) { + if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset)) + continue; + } + /* remove private addresses */ if( (rrset->type == LDNS_RR_TYPE_A || rrset->type == LDNS_RR_TYPE_AAAA)) { diff --git a/testdata/dns64_lookup.rpl b/testdata/dns64_lookup.rpl index 898d0d01a..7986fc8fc 100644 --- a/testdata/dns64_lookup.rpl +++ b/testdata/dns64_lookup.rpl @@ -140,33 +140,6 @@ SECTION ADDITIONAL ns.example.com. IN A 1.2.3.4 ENTRY_END -ENTRY_BEGIN -MATCH opcode qtype qname -ADJUST copy_id -REPLY QR NOERROR -SECTION QUESTION -broken.example.com. IN AAAA -SECTION ANSWER -; NO AAAA present -SECTION AUTHORITY -example.com. IN SOA a. b. 1 2 3 4 5 -ENTRY_END - -ENTRY_BEGIN -MATCH opcode qtype qname -ADJUST copy_id -REPLY QR NOERROR -SECTION QUESTION -broken.example.com. IN A -SECTION ANSWER -broken.example.com. IN A 5.6.7.8 -broken.example.com. IN A \# 3 030405 -SECTION AUTHORITY -example.com. IN NS ns.example.com. -SECTION ADDITIONAL -ns.example.com. IN A 1.2.3.4 -ENTRY_END - ENTRY_BEGIN MATCH opcode qtype qname ADJUST copy_id @@ -284,25 +257,4 @@ SECTION AUTHORITY 7.6.5.in-addr.arpa. IN NS ns.example.com. ENTRY_END -; synthesize from broken, malformed A records -STEP 80 QUERY -ENTRY_BEGIN -REPLY RD -SECTION QUESTION -broken.example.com. IN AAAA -ENTRY_END - -; recursion happens here. -STEP 90 CHECK_ANSWER -ENTRY_BEGIN -MATCH all -REPLY QR RD RA NOERROR -SECTION QUESTION -broken.example.com. IN AAAA -SECTION ANSWER -SECTION AUTHORITY -example.com. IN SOA a. b. 1 2 3 4 5 -SECTION ADDITIONAL -ENTRY_END - SCENARIO_END diff --git a/testdata/iter_scrub_rr_length.rpl b/testdata/iter_scrub_rr_length.rpl new file mode 100644 index 000000000..ccb791eb2 --- /dev/null +++ b/testdata/iter_scrub_rr_length.rpl @@ -0,0 +1,273 @@ +; config options +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + minimal-responses: no + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test scrub of RRs of inappropriate length + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +www.example.com. IN A \# 3 030405 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN AAAA +SECTION ANSWER +www.example.com. IN AAAA 2001:db8::1234 +www.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken1.example.com. IN A +SECTION ANSWER +broken1.example.com. IN A \# 3 030405 +broken1.example.com. IN A \# 3 030406 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken1.example.com. IN AAAA +SECTION ANSWER +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E30 +broken1.example.com. IN AAAA \# 48 000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E31 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +broken2.example.com. IN A +SECTION ANSWER +broken2.example.com. IN A 1.2.3.4 +broken2.example.com. IN A \# 3 030405 +broken2.example.com. IN A 1.2.3.5 +broken2.example.com. IN A \# 3 030406 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A \# 3 030407 +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A \# 3 030408 +ns.example.com. IN A \# 3 030409 +ns.example.com. IN A 1.2.3.7 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.example.com. IN AAAA +ENTRY_END + +STEP 30 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.example.com. IN AAAA +SECTION ANSWER +www.example.com. IN AAAA 2001:db8::1234 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken1.example.com. IN A +ENTRY_END + +STEP 50 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken1.example.com. IN A +SECTION ANSWER +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 60 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken1.example.com. IN AAAA +ENTRY_END + +STEP 70 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken1.example.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +STEP 80 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +broken2.example.com. IN A +ENTRY_END + +STEP 90 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +broken2.example.com. IN A +SECTION ANSWER +broken2.example.com. IN A 1.2.3.4 +broken2.example.com. IN A 1.2.3.5 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A 1.2.3.7 +ENTRY_END + +SCENARIO_END From 63616a5fce838549b341aad9224c3382c08b82da Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 11:29:53 +0200 Subject: [PATCH 10/64] - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. --- Makefile.in | 2 +- doc/Changelog | 1 + iterator/iter_priv.c | 29 ++--------------------------- iterator/iter_priv.h | 7 ------- iterator/iter_scrub.c | 4 ++-- testdata/iter_scrub_rr_length.rpl | 1 + util/data/msgparse.c | 25 +++++++++++++++++++++++++ util/data/msgparse.h | 18 ++++++++++++++++++ 8 files changed, 50 insertions(+), 37 deletions(-) diff --git a/Makefile.in b/Makefile.in index 0a2e7f9b6..627a650f6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -738,7 +738,7 @@ msgencode.lo msgencode.o: $(srcdir)/util/data/msgencode.c config.h $(srcdir)/uti msgparse.lo msgparse.o: $(srcdir)/util/data/msgparse.c config.h $(srcdir)/util/data/msgparse.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/sldns/pkthdr.h \ $(srcdir)/sldns/rrdef.h $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h \ - $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/sldns/sbuffer.h \ + $(srcdir)/util/data/dname.h $(srcdir)/util/storage/lookup3.h $(srcdir)/util/regional.h $(srcdir)/util/net_help.h $(srcdir)/sldns/sbuffer.h \ $(srcdir)/sldns/parseutil.h $(srcdir)/sldns/wire2str.h msgreply.lo msgreply.o: $(srcdir)/util/data/msgreply.c config.h $(srcdir)/util/data/msgreply.h \ $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/util/data/packed_rrset.h \ diff --git a/doc/Changelog b/doc/Changelog index fe03f4378..a9f74ab75 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,7 @@ 7 September 2023: Wouter - Fix to scrub resource records of type A and AAAA that have an inappropriate size. They are removed from responses. + - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. 6 September 2023: Wouter - Merge #931: Prevent warnings from -Wmissing-prototypes. diff --git a/iterator/iter_priv.c b/iterator/iter_priv.c index 675c93907..be4219216 100644 --- a/iterator/iter_priv.c +++ b/iterator/iter_priv.c @@ -207,31 +207,6 @@ size_t priv_get_mem(struct iter_priv* priv) return sizeof(*priv) + regional_get_mem(priv->region); } -/** remove RR from msgparse RRset, return true if rrset is entirely bad */ -int -msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, - struct rr_parse* prev, struct rr_parse** rr, struct sockaddr_storage* addr, socklen_t addrlen) -{ - if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { - uint8_t buf[LDNS_MAX_DOMAINLEN+1]; - dname_pkt_copy(pkt, buf, rrset->dname); - if(addr) - log_name_addr(VERB_QUERY, str, buf, addr, addrlen); - else log_nametypeclass(VERB_QUERY, str, buf, - rrset->type, ntohs(rrset->rrset_class)); - } - if(prev) - prev->next = (*rr)->next; - else rrset->rr_first = (*rr)->next; - if(rrset->rr_last == *rr) - rrset->rr_last = prev; - rrset->rr_count --; - rrset->size -= (*rr)->size; - /* rr struct still exists, but is unlinked, so that in the for loop - * the rr->next works fine to continue. */ - return rrset->rr_count == 0; -} - int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, struct rrset_parse* rrset) { @@ -264,7 +239,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len)) return 1; continue; } @@ -287,7 +262,7 @@ int priv_rrset_bad(struct iter_priv* priv, sldns_buffer* pkt, INET6_SIZE); memmove(&addr, &sa, len); if(priv_lookup_addr(priv, &addr, len)) { - if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, &rr, &addr, len)) + if(msgparse_rrset_remove_rr("sanitize: removing public name with private address", pkt, rrset, prev, rr, &addr, len)) return 1; continue; } diff --git a/iterator/iter_priv.h b/iterator/iter_priv.h index 7f58ec2f7..0430d57e3 100644 --- a/iterator/iter_priv.h +++ b/iterator/iter_priv.h @@ -48,8 +48,6 @@ struct iter_env; struct config_file; struct regional; struct rrset_parse; -struct rr_parse; -struct rrset_parse; /** * Iterator priv structure @@ -111,9 +109,4 @@ int priv_rrset_bad(struct iter_priv* priv, struct sldns_buffer* pkt, */ size_t priv_get_mem(struct iter_priv* priv); -/** remove RR from msgparse RRset, return true if rrset is entirely bad */ -int msgparse_rrset_remove_rr(const char* str, struct sldns_buffer* pkt, - struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse** rr, - struct sockaddr_storage* addr, socklen_t addrlen); - #endif /* ITERATOR_ITER_PRIV_H */ diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index c34ccfd3c..a7d60e4b2 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -730,7 +730,7 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, * (2 bytes for length and 4 for IPv4 addr)*/ if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) { if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:", - pkt, *rrset, rr_prev, &rr, NULL, 0)) { + pkt, *rrset, rr_prev, rr, NULL, 0)) { remove_rrset("sanitize: removing type A RRset of inappropriate length:", pkt, msg, prev, rrset); return 1; @@ -743,7 +743,7 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, * (2 bytes for length and 16 for IPv6 addr)*/ if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) { if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:", - pkt, *rrset, rr_prev, &rr, NULL, 0)) { + pkt, *rrset, rr_prev, rr, NULL, 0)) { remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:", pkt, msg, prev, rrset); return 1; diff --git a/testdata/iter_scrub_rr_length.rpl b/testdata/iter_scrub_rr_length.rpl index ccb791eb2..14bd55265 100644 --- a/testdata/iter_scrub_rr_length.rpl +++ b/testdata/iter_scrub_rr_length.rpl @@ -3,6 +3,7 @@ server: target-fetch-policy: "0 0 0 0 0" qname-minimisation: "no" minimal-responses: no + rrset-roundrobin: no stub-zone: name: "." diff --git a/util/data/msgparse.c b/util/data/msgparse.c index b5414c6d0..d06b7bb25 100644 --- a/util/data/msgparse.c +++ b/util/data/msgparse.c @@ -47,6 +47,7 @@ #include "util/regional.h" #include "util/rfc_1982.h" #include "util/edns.h" +#include "util/net_help.h" #include "sldns/rrdef.h" #include "sldns/sbuffer.h" #include "sldns/parseutil.h" @@ -1306,3 +1307,27 @@ log_edns_opt_list(enum verbosity_value level, const char* info_str, } } +/** remove RR from msgparse RRset, return true if rrset is entirely bad */ +int +msgparse_rrset_remove_rr(const char* str, sldns_buffer* pkt, struct rrset_parse* rrset, + struct rr_parse* prev, struct rr_parse* rr, struct sockaddr_storage* addr, socklen_t addrlen) +{ + if(verbosity >= VERB_QUERY && rrset->dname_len <= LDNS_MAX_DOMAINLEN && str) { + uint8_t buf[LDNS_MAX_DOMAINLEN+1]; + dname_pkt_copy(pkt, buf, rrset->dname); + if(addr) + log_name_addr(VERB_QUERY, str, buf, addr, addrlen); + else log_nametypeclass(VERB_QUERY, str, buf, + rrset->type, ntohs(rrset->rrset_class)); + } + if(prev) + prev->next = rr->next; + else rrset->rr_first = rr->next; + if(rrset->rr_last == rr) + rrset->rr_last = prev; + rrset->rr_count --; + rrset->size -= rr->size; + /* rr struct still exists, but is unlinked, so that in the for loop + * the rr->next works fine to continue. */ + return rrset->rr_count == 0; +} diff --git a/util/data/msgparse.h b/util/data/msgparse.h index b7dc235d6..8e5c94a28 100644 --- a/util/data/msgparse.h +++ b/util/data/msgparse.h @@ -371,4 +371,22 @@ void msgparse_bucket_remove(struct msg_parse* msg, struct rrset_parse* rrset); void log_edns_opt_list(enum verbosity_value level, const char* info_str, struct edns_option* list); +/** + * Remove RR from msgparse RRset. + * @param str: this string is used for logging if verbose. If NULL, there is + * no logging of the remove. + * @param pkt: packet in buffer that is removed from. Used to log the name + * of the item removed. + * @param rrset: RRset that the RR is removed from. + * @param prev: previous RR in list, or NULL. + * @param rr: RR that is removed. + * @param addr: address used for logging, if verbose, or NULL then it is not + * used. + * @param addrlen: length of addr, if that is not NULL. + * @return true if rrset is entirely bad, it would then need to be removed. + */ +int msgparse_rrset_remove_rr(const char* str, struct sldns_buffer* pkt, + struct rrset_parse* rrset, struct rr_parse* prev, struct rr_parse* rr, + struct sockaddr_storage* addr, socklen_t addrlen); + #endif /* UTIL_DATA_MSGPARSE_H */ From fdd5f8ff835edb7af044bdab298f2f1fcd6431eb Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 14:44:48 +0200 Subject: [PATCH 11/64] - Fix to add EDE text when RRs have been removed due to length. --- doc/Changelog | 1 + iterator/iter_scrub.c | 25 ++++- iterator/iter_scrub.h | 5 +- iterator/iterator.c | 19 +++- testdata/iter_scrub_rr_length.rpl | 30 +++++- testdata/val_scrub_rr_length.rpl | 164 ++++++++++++++++++++++++++++++ util/module.c | 18 ++++ util/module.h | 8 ++ 8 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 testdata/val_scrub_rr_length.rpl diff --git a/doc/Changelog b/doc/Changelog index a9f74ab75..e35f9c389 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,7 @@ - Fix to scrub resource records of type A and AAAA that have an inappropriate size. They are removed from responses. - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. + - Fix to add EDE text when RRs have been removed due to length. 6 September 2023: Wouter - Merge #931: Prevent warnings from -Wmissing-prototypes. diff --git a/iterator/iter_scrub.c b/iterator/iter_scrub.c index a7d60e4b2..5f2e30337 100644 --- a/iterator/iter_scrub.c +++ b/iterator/iter_scrub.c @@ -720,7 +720,8 @@ static int sanitize_nsec_is_overreach(sldns_buffer* pkt, * has been removed. */ static int scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, - struct rrset_parse* prev, struct rrset_parse** rrset) + struct rrset_parse* prev, struct rrset_parse** rrset, int* added_ede, + struct module_qstate* qstate) { struct rr_parse* rr, *rr_prev = NULL; for(rr = (*rrset)->rr_first; rr; rr = rr->next) { @@ -729,6 +730,11 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, * An A record should be 6 bytes only * (2 bytes for length and 4 for IPv4 addr)*/ if((*rrset)->type == LDNS_RR_TYPE_A && rr->size != 6 ) { + if(!*added_ede) { + *added_ede = 1; + errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", + LDNS_EDE_OTHER); + } if(msgparse_rrset_remove_rr("sanitize: removing type A RR of inappropriate length:", pkt, *rrset, rr_prev, rr, NULL, 0)) { remove_rrset("sanitize: removing type A RRset of inappropriate length:", @@ -742,6 +748,11 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, * An AAAA record should be 18 bytes only * (2 bytes for length and 16 for IPv6 addr)*/ if((*rrset)->type == LDNS_RR_TYPE_AAAA && rr->size != 18 ) { + if(!*added_ede) { + *added_ede = 1; + errinf_ede(qstate, "sanitize: records of inappropriate length have been removed.", + LDNS_EDE_OTHER); + } if(msgparse_rrset_remove_rr("sanitize: removing type AAAA RR of inappropriate length:", pkt, *rrset, rr_prev, rr, NULL, 0)) { remove_rrset("sanitize: removing type AAAA RRset of inappropriate length:", @@ -767,15 +778,17 @@ scrub_sanitize_rr_length(sldns_buffer* pkt, struct msg_parse* msg, * @param zonename: name of server zone. * @param env: module environment with config and cache. * @param ie: iterator environment with private address data. + * @param qstate: for setting errinf for EDE error messages. * @return 0 on error. */ static int scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct module_env* env, - struct iter_env* ie) + struct iter_env* ie, struct module_qstate* qstate) { int del_addi = 0; /* if additional-holding rrsets are deleted, we do not trust the normalized additional-A-AAAA any more */ + int added_rrlen_ede = 0; struct rrset_parse* rrset, *prev; prev = NULL; rrset = msg->rrset_first; @@ -823,7 +836,8 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, /* Sanity check for length of records */ if(rrset->type == LDNS_RR_TYPE_A || rrset->type == LDNS_RR_TYPE_AAAA) { - if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset)) + if(scrub_sanitize_rr_length(pkt, msg, prev, &rrset, + &added_rrlen_ede, qstate)) continue; } @@ -900,7 +914,8 @@ scrub_sanitize(sldns_buffer* pkt, struct msg_parse* msg, int scrub_message(sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct regional* region, - struct module_env* env, struct iter_env* ie) + struct module_env* env, struct module_qstate* qstate, + struct iter_env* ie) { /* basic sanity checks */ log_nametypeclass(VERB_ALGO, "scrub for", zonename, LDNS_RR_TYPE_NS, @@ -932,7 +947,7 @@ scrub_message(sldns_buffer* pkt, struct msg_parse* msg, if(!scrub_normalize(pkt, msg, qinfo, region, env)) return 0; /* delete all out-of-zone information */ - if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie)) + if(!scrub_sanitize(pkt, msg, qinfo, zonename, env, ie, qstate)) return 0; return 1; } diff --git a/iterator/iter_scrub.h b/iterator/iter_scrub.h index cbbaf73c9..4d6ce7166 100644 --- a/iterator/iter_scrub.h +++ b/iterator/iter_scrub.h @@ -48,6 +48,7 @@ struct query_info; struct regional; struct module_env; struct iter_env; +struct module_qstate; /** * Cleanup the passed dns message. @@ -59,11 +60,13 @@ struct iter_env; * Used to determine out of bailiwick information. * @param regional: where to allocate (new) parts of the message. * @param env: module environment with config settings and cache. + * @param qstate: for setting errinf for EDE error messages. * @param ie: iterator module environment data. * @return: false if the message is total waste. true if scrubbed with success. */ int scrub_message(struct sldns_buffer* pkt, struct msg_parse* msg, struct query_info* qinfo, uint8_t* zonename, struct regional* regional, - struct module_env* env, struct iter_env* ie); + struct module_env* env, struct module_qstate* qstate, + struct iter_env* ie); #endif /* ITERATOR_ITER_SCRUB_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 1548dfcae..9f78aa17d 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -3874,6 +3874,23 @@ processFinished(struct module_qstate* qstate, struct iter_qstate* iq, /* explicitly set the EDE string to NULL */ iq->response->rep->reason_bogus_str = NULL; + if((qstate->env->cfg->val_log_level >= 2 || + qstate->env->cfg->log_servfail) && qstate->errinf && + !qstate->env->cfg->val_log_squelch) { + char* err_str = errinf_to_str_misc(qstate); + if(err_str) { + size_t err_str_len = strlen(err_str); + verbose(VERB_ALGO, "iterator EDE: %s", err_str); + /* allocate space and store the error + * string */ + iq->response->rep->reason_bogus_str = regional_alloc( + qstate->region, + sizeof(char) * (err_str_len+1)); + memcpy(iq->response->rep->reason_bogus_str, + err_str, err_str_len+1); + } + free(err_str); + } /* we have finished processing this query */ qstate->ext_state[id] = module_finished; @@ -4098,7 +4115,7 @@ process_response(struct module_qstate* qstate, struct iter_qstate* iq, /* normalize and sanitize: easy to delete items from linked lists */ if(!scrub_message(pkt, prs, &iq->qinfo_out, iq->dp->name, - qstate->env->scratch, qstate->env, ie)) { + qstate->env->scratch, qstate->env, qstate, ie)) { /* if 0x20 enabled, start fallback, but we have no message */ if(event == module_event_capsfail && !iq->caps_fallback) { iq->caps_fallback = 1; diff --git a/testdata/iter_scrub_rr_length.rpl b/testdata/iter_scrub_rr_length.rpl index 14bd55265..70c6fc54b 100644 --- a/testdata/iter_scrub_rr_length.rpl +++ b/testdata/iter_scrub_rr_length.rpl @@ -4,6 +4,8 @@ server: qname-minimisation: "no" minimal-responses: no rrset-roundrobin: no + ede: yes + log-servfail: yes stub-zone: name: "." @@ -13,7 +15,7 @@ CONFIG_END SCENARIO_BEGIN Test scrub of RRs of inappropriate length ; K.ROOT-SERVERS.NET. -RANGE_BEGIN 0 100 +RANGE_BEGIN 0 200 ADDRESS 193.0.14.129 ENTRY_BEGIN MATCH opcode qtype qname @@ -41,7 +43,7 @@ ENTRY_END RANGE_END ; a.gtld-servers.net. -RANGE_BEGIN 0 100 +RANGE_BEGIN 0 200 ADDRESS 192.5.6.30 ENTRY_BEGIN MATCH opcode qtype qname @@ -69,7 +71,7 @@ ENTRY_END RANGE_END ; ns.example.com. -RANGE_BEGIN 0 100 +RANGE_BEGIN 0 200 ADDRESS 1.2.3.4 ENTRY_BEGIN MATCH opcode qtype qname @@ -271,4 +273,26 @@ ns.example.com. IN A 1.2.3.6 ns.example.com. IN A 1.2.3.7 ENTRY_END +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD CD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +STEP 110 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=any +REPLY QR RD CD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.6 +ns.example.com. IN A 1.2.3.7 +ENTRY_END + SCENARIO_END diff --git a/testdata/val_scrub_rr_length.rpl b/testdata/val_scrub_rr_length.rpl new file mode 100644 index 000000000..f57cf773a --- /dev/null +++ b/testdata/val_scrub_rr_length.rpl @@ -0,0 +1,164 @@ +; config options +; The island of trust is at example.com +server: + trust-anchor: "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af" + val-override-date: "20070916134226" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + trust-anchor-signaling: no + minimal-responses: no + rrset-roundrobin: no + ede: yes + log-servfail: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test validator with scrub of RR for inappropriate length + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. IN DNSKEY 256 3 8 AwEAAdug/L739i0mgN2nuK/bhxu3wFn5Ud9nK2+XUmZQlPUEZUC5YZvm1rfMmEWTGBn87fFxEu/kjFZHJ55JLzqsbbpVHLbmKCTT2gYR2FV2WDKROGKuYbVkJIXdKAjJ0ONuK507NinYvlWXIoxHn22KAWOd9wKgSTNHBlmGkX+ts3hh ;{id = 55566 (zsk), size = 1024b} +example.com. 3600 IN RRSIG DNSKEY 8 2 3600 20070926134150 20070829134150 55566 example.com. Ni7Q17l2dzKcAnHdU3Mycpdwo0I6qgGxRvBhBNI43xIUFHJpgKpbeMFxKvVTkbwHyMPMIuHmOaC82IBhOpGD10SExVh4erQhWS3Hvl+m4Cwl3WI9N+AW6CTB9yj+d4xzX3bHjjBt6MSk4bU8ABR7qIoAjgjY7zdtUDWQlaM+d18= +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.example.com. IN AAAA +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END + +; response to query of interest +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +www.example.com. IN A \# 5 0102030405 +; RRSIG includes the malformed record. +www.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. W4WFu9B81uRvp3Dj8uLIscypznKWuLuKrZqVg1on5/45/3/xyjHvj3TjTL3gruWFXPiQpldvOstXLZ5eN3OpqILdkVey0eqVATujpHwIruY6GWztVx5WptmFfK6E6zzshZ3RmAARqq/czQ+IZli2A9xixdY2H0o1dSU6gohEjjE= +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 8 2 3600 20070926134150 20070829134150 55566 example.com. cHdLVCzujUQs6b67c1SmCX+/br4tgOg86Gj/R/x+PKUQmWHyeVwBSTlJuLOHbca3CQoyIQc+V2ilK6fjwjbY/dLk4uOlux8L+Zn7HsUXSOwJPIjsM3LuTa8CYDMvYhOP7KGR+vNpJVSsQ25pyDn6Rzsdl3E7DAf7uSkPV8VJwa8= +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 8 3 3600 20070926134150 20070829134150 55566 example.com. PBwNifMNxTXlDorHX1neq1wUhWLmqk+PZ+PBZCI5BJAmakdgOXdLQiVqlKaErJyA/4uN+99fUf6/DqxwgxL8FIPdBkxMOTJaKrCFjEhL6qozTd3+DI6qFJPgTm1lrkpvb9W72MtK2vxAyT5I/bG2SWKdpzOaQXysbDb2hnxq3as= +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ede=any +REPLY QR RD RA DO SERVFAIL +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +ENTRY_END + +SCENARIO_END diff --git a/util/module.c b/util/module.c index 773dab853..62e5de4a0 100644 --- a/util/module.c +++ b/util/module.c @@ -194,6 +194,24 @@ char* errinf_to_str_servfail(struct module_qstate* qstate) return p; } +char* errinf_to_str_misc(struct module_qstate* qstate) +{ + char buf[20480]; + char* p = buf; + size_t left = sizeof(buf); + struct errinf_strlist* s; + if(!qstate->errinf) + snprintf(p, left, "misc failure"); + else for(s=qstate->errinf; s; s=s->next) { + snprintf(p, left, "%s%s", (s==qstate->errinf?"":" "), s->str); + left -= strlen(p); p += strlen(p); + } + p = strdup(buf); + if(!p) + log_err("malloc failure in errinf_to_str"); + return p; +} + void errinf_rrset(struct module_qstate* qstate, struct ub_packed_rrset_key *rr) { char buf[1024]; diff --git a/util/module.h b/util/module.h index 5b6fcc93c..d25bebd15 100644 --- a/util/module.h +++ b/util/module.h @@ -842,6 +842,14 @@ sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate); */ char* errinf_to_str_servfail(struct module_qstate* qstate); +/** + * Create error info in string. For misc failures that are not servfail. + * @param qstate: query state. + * @return string or NULL on malloc failure (already logged). + * This string is malloced and has to be freed by caller. + */ +char* errinf_to_str_misc(struct module_qstate* qstate); + /** * Initialize the edns known options by allocating the required space. * @param env: the module environment. From 33784e612a374f53171f6c6249b4d8f58a3630e7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 14:58:51 +0200 Subject: [PATCH 12/64] - Fix to set ede match in unit test for rr length removal. --- testdata/iter_scrub_rr_length.rpl | 2 +- testdata/val_scrub_rr_length.rpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/iter_scrub_rr_length.rpl b/testdata/iter_scrub_rr_length.rpl index 70c6fc54b..2ef73c2fe 100644 --- a/testdata/iter_scrub_rr_length.rpl +++ b/testdata/iter_scrub_rr_length.rpl @@ -282,7 +282,7 @@ ENTRY_END STEP 110 CHECK_ANSWER ENTRY_BEGIN -MATCH all ede=any +MATCH all ede=0 REPLY QR RD CD RA DO NOERROR SECTION QUESTION www.example.com. IN A diff --git a/testdata/val_scrub_rr_length.rpl b/testdata/val_scrub_rr_length.rpl index f57cf773a..0219b918e 100644 --- a/testdata/val_scrub_rr_length.rpl +++ b/testdata/val_scrub_rr_length.rpl @@ -154,7 +154,7 @@ ENTRY_END ; recursion happens here. STEP 10 CHECK_ANSWER ENTRY_BEGIN -MATCH all ede=any +MATCH all ede=0 REPLY QR RD RA DO SERVFAIL SECTION QUESTION www.example.com. IN A From 5b8a7340bb7996b3c15287bad7febd065bede969 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 14:59:13 +0200 Subject: [PATCH 13/64] - Fix to set ede match in unit test for rr length removal. Changelog note. --- doc/Changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Changelog b/doc/Changelog index e35f9c389..ef289dca4 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,7 @@ inappropriate size. They are removed from responses. - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. - Fix to add EDE text when RRs have been removed due to length. + - Fix to set ede match in unit test for rr length removal. 6 September 2023: Wouter - Merge #931: Prevent warnings from -Wmissing-prototypes. From 8c751d48a24b74e4ac0bbfd948d2b7e87dd87f6a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 7 Sep 2023 15:28:01 +0200 Subject: [PATCH 14/64] - Fix to print EDE text in readable form in output logs. --- doc/Changelog | 1 + sldns/wire2str.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ sldns/wire2str.h | 13 ++++++++ 3 files changed, 95 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index ef289dca4..f45221936 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,7 @@ - Fix to move msgparse_rrset_remove_rr code to util/msgparse.c. - Fix to add EDE text when RRs have been removed due to length. - Fix to set ede match in unit test for rr length removal. + - Fix to print EDE text in readable form in output logs. 6 September 2023: Wouter - Merge #931: Prevent warnings from -Wmissing-prototypes. diff --git a/sldns/wire2str.c b/sldns/wire2str.c index e6278ff56..ace478d73 100644 --- a/sldns/wire2str.c +++ b/sldns/wire2str.c @@ -199,6 +199,38 @@ static sldns_lookup_table sldns_edns_options_data[] = { }; sldns_lookup_table* sldns_edns_options = sldns_edns_options_data; +/* From RFC8914 5.2 Table 3, the "Extended DNS Error Codes" registry. */ +static sldns_lookup_table sldns_edns_ede_codes_data[] = { + { LDNS_EDE_NONE, "None" }, + { LDNS_EDE_OTHER, "Other Error" }, + { LDNS_EDE_UNSUPPORTED_DNSKEY_ALG, "Unsupported DNSKEY Algorithm" }, + { LDNS_EDE_UNSUPPORTED_DS_DIGEST, "Unsupported DS Digest Type" }, + { LDNS_EDE_STALE_ANSWER, "Stale Answer" }, + { LDNS_EDE_FORGED_ANSWER, "Forged Answer" }, + { LDNS_EDE_DNSSEC_INDETERMINATE, "DNSSEC Indeterminate" }, + { LDNS_EDE_DNSSEC_BOGUS, "DNSSEC Bogus" }, + { LDNS_EDE_SIGNATURE_EXPIRED, "Signature Expired" }, + { LDNS_EDE_SIGNATURE_NOT_YET_VALID, "Signature Not Yet Valid" }, + { LDNS_EDE_DNSKEY_MISSING, "DNSKEY Missing" }, + { LDNS_EDE_RRSIGS_MISSING, "RRSIGs Missing" }, + { LDNS_EDE_NO_ZONE_KEY_BIT_SET, "No Zone Key Bit Set" }, + { LDNS_EDE_NSEC_MISSING, "NSEC Missing" }, + { LDNS_EDE_CACHED_ERROR, "Cached Error" }, + { LDNS_EDE_NOT_READY, "Not Ready" }, + { LDNS_EDE_BLOCKED, "Blocked" }, + { LDNS_EDE_CENSORED, "Censored" }, + { LDNS_EDE_FILTERED, "Filtered" }, + { LDNS_EDE_PROHIBITED, "Prohibited" }, + { LDNS_EDE_STALE_NXDOMAIN_ANSWER, "Stale NXDOMAIN Answer" }, + { LDNS_EDE_NOT_AUTHORITATIVE, "Not Authoritative" }, + { LDNS_EDE_NOT_SUPPORTED, "Not Supported" }, + { LDNS_EDE_NO_REACHABLE_AUTHORITY, "No Reachable Authority" }, + { LDNS_EDE_NETWORK_ERROR, "Network Error" }, + { LDNS_EDE_INVALID_DATA, "Invalid Data" }, + { 0, NULL} +}; +sldns_lookup_table* sldns_edns_ede_codes = sldns_edns_ede_codes_data; + static sldns_lookup_table sldns_tsig_errors_data[] = { { LDNS_TSIG_ERROR_NOERROR, "NOERROR" }, { LDNS_RCODE_FORMERR, "FORMERR" }, @@ -2234,6 +2266,52 @@ static int sldns_wire2str_edns_keepalive_print(char** s, size_t* sl, return w; } +int sldns_wire2str_edns_ede_print(char** s, size_t* sl, + uint8_t* data, size_t len) +{ + uint16_t ede_code; + int w = 0; + sldns_lookup_table *lt; + size_t i; + int printable; + + if(len < 2) { + w += sldns_str_print(s, sl, "malformed ede "); + w += print_hex_buf(s, sl, data, len); + return w; + } + + ede_code = sldns_read_uint16(data); + lt = sldns_lookup_by_id(sldns_edns_ede_codes, (int)ede_code); + if(lt && lt->name) + w += sldns_str_print(s, sl, "%s", lt->name); + else w += sldns_str_print(s, sl, "%d", (int)ede_code); + + if(len == 2) + return w; + + w += sldns_str_print(s, sl, " "); + + /* If it looks like text, show it as text. */ + printable=1; + for(i=2; i Date: Thu, 7 Sep 2023 15:35:32 +0200 Subject: [PATCH 15/64] Fix #928 (1.18 doesn't start on macOS/SunOS) --- services/listen_dnsport.c | 4 ++++ util/fptr_wlist.c | 2 ++ util/netevent.c | 14 +++++--------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 60f9b41e5..259347a36 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1498,9 +1498,13 @@ listen_create(struct comm_base* base, struct listen_port* ports, } } else if(ports->ftype == listen_type_udpancil || ports->ftype == listen_type_udpancil_dnscrypt) { +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) cp = comm_point_create_udp_ancil(base, ports->fd, front->udp_buff, ports->pp2_enabled, cb, cb_arg, ports->socket); +#else + log_warn("This system does not support UDP ancilliary data."); +#endif } if(!cp) { log_err("can't create commpoint"); diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 3b88da235..43d38dc37 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -168,7 +168,9 @@ int fptr_whitelist_event(void (*fptr)(int, short, void *)) { if(fptr == &comm_point_udp_callback) return 1; +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) else if(fptr == &comm_point_udp_ancil_callback) return 1; +#endif else if(fptr == &comm_point_tcp_accept_callback) return 1; else if(fptr == &comm_point_tcp_handle_callback) return 1; else if(fptr == &comm_timer_callback) return 1; diff --git a/util/netevent.c b/util/netevent.c index 204e4883c..9f4a6e6c3 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -850,10 +850,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, return 1; } +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) void comm_point_udp_ancil_callback(int fd, short event, void* arg) { -#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) struct comm_reply rep; struct msghdr msg; struct iovec iov[1]; @@ -972,14 +972,8 @@ comm_point_udp_ancil_callback(int fd, short event, void* arg) if(!rep.c || rep.c->fd == -1) /* commpoint closed */ break; } -#else - (void)fd; - (void)event; - (void)arg; - fatal_exit("recvmsg: No support for IPV6_PKTINFO; IP_PKTINFO or IP_RECVDSTADDR. " - "Please disable interface-automatic"); -#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ } +#endif /* AF_INET6 && IPV6_PKTINFO && HAVE_RECVMSG */ void comm_point_udp_callback(int fd, short event, void* arg) @@ -3860,7 +3854,7 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, evbits = UB_EV_READ | UB_EV_PERSIST; /* ub_event stuff */ c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits, -#ifdef USE_WINSOCK +#if defined(USE_WINSOCK) || !(defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)) comm_point_udp_callback, c); #else comm_point_udp_ancil_callback, c); @@ -3879,6 +3873,7 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, return c; } +#if defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG) struct comm_point* comm_point_create_udp_ancil(struct comm_base *base, int fd, sldns_buffer* buffer, int pp2_enabled, @@ -3941,6 +3936,7 @@ comm_point_create_udp_ancil(struct comm_base *base, int fd, c->event_added = 1; return c; } +#endif static struct comm_point* comm_point_create_tcp_handler(struct comm_base *base, From 1c8f0e0fc5056e95fed87206ef6f81aed4d209d1 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Thu, 7 Sep 2023 16:23:11 +0200 Subject: [PATCH 16/64] Avoid calling comm_point_udp_ancil_callback from comm_point_create_udp --- services/listen_dnsport.c | 4 +++- util/netevent.c | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/services/listen_dnsport.c b/services/listen_dnsport.c index 259347a36..753550978 100644 --- a/services/listen_dnsport.c +++ b/services/listen_dnsport.c @@ -1327,7 +1327,9 @@ ports_create_if(const char* ifname, int do_auto, int do_udp, int do_tcp, log_warn("socket timestamping is not available"); } if(!port_insert(list, s, is_dnscrypt - ?listen_type_udp_dnscrypt:listen_type_udp, + ?listen_type_udp_dnscrypt : + (sock_queue_timeout ? + listen_type_udpancil:listen_type_udp), is_pp2, ub_sock)) { sock_close(s); if(ub_sock->addr) diff --git a/util/netevent.c b/util/netevent.c index 9f4a6e6c3..2c03cfd60 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -3854,11 +3854,7 @@ comm_point_create_udp(struct comm_base *base, int fd, sldns_buffer* buffer, evbits = UB_EV_READ | UB_EV_PERSIST; /* ub_event stuff */ c->ev->ev = ub_event_new(base->eb->base, c->fd, evbits, -#if defined(USE_WINSOCK) || !(defined(AF_INET6) && defined(IPV6_PKTINFO) && defined(HAVE_RECVMSG)) comm_point_udp_callback, c); -#else - comm_point_udp_ancil_callback, c); -#endif if(c->ev->ev == NULL) { log_err("could not baseset udp event"); comm_point_delete(c); From 0ee44ef384593ed0382d1ce6048d5a9c9440b45c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 8 Sep 2023 13:35:42 +0200 Subject: [PATCH 17/64] - Fix send of udp retries when ENOBUFS is returned. It stops looping and also waits for the condition to go away. Reported by Florian Obser. --- doc/Changelog | 5 +++ util/netevent.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index f45221936..fb955f5a3 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +8 September 2023: Wouter + - Fix send of udp retries when ENOBUFS is returned. It stops looping + and also waits for the condition to go away. Reported by Florian + Obser. + 7 September 2023: Wouter - Fix to scrub resource records of type A and AAAA that have an inappropriate size. They are removed from responses. diff --git a/util/netevent.c b/util/netevent.c index 2c03cfd60..f7a0302db 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -116,6 +116,8 @@ /** timeout in millisec to wait for write to unblock, packets dropped after.*/ #define SEND_BLOCKED_WAIT_TIMEOUT 200 +/** max number of times to wait for write to unblock, packets dropped after.*/ +#define SEND_BLOCKED_MAX_RETRY 5 /** Let's make timestamping code cleaner and redefine SO_TIMESTAMP* */ #ifndef SO_TIMESTAMP @@ -402,9 +404,10 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #endif + int retries = 0; /* if we set the fd blocking, other threads suddenly * have a blocking fd that they operate on */ - while(sent == -1 && ( + while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && ( #ifndef USE_WINSOCK errno == EAGAIN || errno == EINTR || # ifdef EWOULDBLOCK @@ -419,6 +422,13 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, #endif )) { #if defined(HAVE_POLL) || defined(USE_WINSOCK) + int send_nobufs = ( +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ); struct pollfd p; int pret; memset(&p, 0, sizeof(p)); @@ -457,8 +467,48 @@ comm_point_send_udp_msg(struct comm_point *c, sldns_buffer* packet, log_err("poll udp out failed: %s", sock_strerror(errno)); return 0; + } else if((pret < 0 && +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ) || (send_nobufs && retries > 0)) { + /* ENOBUFS, and poll returned without + * a timeout. Or the retried send call + * returned ENOBUFS. It is good to + * wait a bit for the error to clear. */ + /* The timeout is 20*(2^(retries+1)), + * it increases exponentially, starting + * at 40 msec. After 5 tries, 1240 msec + * have passed in total, when poll + * returned the error, and 1200 msec + * when send returned the errors. */ +#ifndef USE_WINSOCK + pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#else + pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#endif + if(pret < 0 && +#ifndef USE_WINSOCK + errno != EAGAIN && errno != EINTR && +# ifdef EWOULDBLOCK + errno != EWOULDBLOCK && +# endif + errno != ENOBUFS +#else + WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEINTR && + WSAGetLastError() != WSAENOBUFS && + WSAGetLastError() != WSAEWOULDBLOCK +#endif + ) { + log_err("poll udp out timer failed: %s", + sock_strerror(errno)); + } } #endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */ + retries++; if (!is_connected) { sent = sendto(c->fd, (void*)sldns_buffer_begin(packet), sldns_buffer_remaining(packet), 0, @@ -665,7 +715,8 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, WSAGetLastError() == WSAENOBUFS || WSAGetLastError() == WSAEWOULDBLOCK) { #endif - while(sent == -1 && ( + int retries = 0; + while(sent == -1 && retries < SEND_BLOCKED_MAX_RETRY && ( #ifndef USE_WINSOCK errno == EAGAIN || errno == EINTR || # ifdef EWOULDBLOCK @@ -680,6 +731,13 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, #endif )) { #if defined(HAVE_POLL) || defined(USE_WINSOCK) + int send_nobufs = ( +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ); struct pollfd p; int pret; memset(&p, 0, sizeof(p)); @@ -718,8 +776,48 @@ comm_point_send_udp_msg_if(struct comm_point *c, sldns_buffer* packet, log_err("poll udp out failed: %s", sock_strerror(errno)); return 0; + } else if((pret < 0 && +#ifndef USE_WINSOCK + errno == ENOBUFS +#else + WSAGetLastError() == WSAENOBUFS +#endif + ) || (send_nobufs && retries > 0)) { + /* ENOBUFS, and poll returned without + * a timeout. Or the retried send call + * returned ENOBUFS. It is good to + * wait a bit for the error to clear. */ + /* The timeout is 20*(2^(retries+1)), + * it increases exponentially, starting + * at 40 msec. After 5 tries, 1240 msec + * have passed in total, when poll + * returned the error, and 1200 msec + * when send returned the errors. */ +#ifndef USE_WINSOCK + pret = poll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#else + pret = WSAPoll(NULL, 0, (SEND_BLOCKED_WAIT_TIMEOUT/10)<<(retries+1)); +#endif + if(pret < 0 && +#ifndef USE_WINSOCK + errno != EAGAIN && errno != EINTR && +# ifdef EWOULDBLOCK + errno != EWOULDBLOCK && +# endif + errno != ENOBUFS +#else + WSAGetLastError() != WSAEINPROGRESS && + WSAGetLastError() != WSAEINTR && + WSAGetLastError() != WSAENOBUFS && + WSAGetLastError() != WSAEWOULDBLOCK +#endif + ) { + log_err("poll udp out timer failed: %s", + sock_strerror(errno)); + } } #endif /* defined(HAVE_POLL) || defined(USE_WINSOCK) */ + retries++; sent = sendmsg(c->fd, &msg, 0); } } From d1977c679b75336386aade3a9ac9cf136aead99f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 13 Sep 2023 13:11:53 +0200 Subject: [PATCH 18/64] - disable-edns-do, doc and add option disable-edns-do: no. --- doc/example.conf.in | 5 +++++ doc/unbound.conf.5.in | 7 +++++++ util/config_file.c | 3 +++ util/config_file.h | 2 ++ util/configlexer.lex | 1 + util/configparser.y | 13 +++++++++++-- 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/example.conf.in b/doc/example.conf.in index 0980212e1..44a19fc72 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -683,6 +683,11 @@ server: # that set CD but cannot validate themselves. # ignore-cd-flag: no + # Disable the DO flag in outgoing requests. It is helpful for upstream + # devices that cannot handle DNSSEC information. But do not enable it + # otherwise, because it would stop DNSSEC validation. + # disable-edns-do: no + # Serve expired responses from cache, with serve-expired-reply-ttl in # the response, and then attempt to fetch the data afresh. # serve-expired: no diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 84b903f49..bbc6ddae5 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1302,6 +1302,13 @@ servers that set the CD flag but cannot validate DNSSEC themselves are the clients, and then Unbound provides them with DNSSEC protection. The default value is "no". .TP +.B disable\-edns\-do: \fI +Disable the EDNS DO flag in upstream requests. This can be helpful for +devices that cannot handle DNSSEC information. But it should not be enabled +otherwise, because that would stop DNSSEC validation. The DNSSEC validation +would not work for Unbound itself, and also not for downstream users. +Default is no. +.TP .B serve\-expired: \fI If enabled, Unbound attempts to serve old responses from cache with a TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the diff --git a/util/config_file.c b/util/config_file.c index 454096342..8f7de905e 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -271,6 +271,7 @@ config_create(void) cfg->val_permissive_mode = 0; cfg->aggressive_nsec = 1; cfg->ignore_cd = 0; + cfg->disable_edns_do = 0; cfg->serve_expired = 0; cfg->serve_expired_ttl = 0; cfg->serve_expired_ttl_reset = 0; @@ -690,6 +691,7 @@ int config_set_option(struct config_file* cfg, const char* opt, else S_YNO("val-permissive-mode:", val_permissive_mode) else S_YNO("aggressive-nsec:", aggressive_nsec) else S_YNO("ignore-cd-flag:", ignore_cd) + else S_YNO("disable-edns-do:", disable_edns_do) else if(strcmp(opt, "serve-expired:") == 0) { IS_YES_OR_NO; cfg->serve_expired = (strcmp(val, "yes") == 0); SERVE_EXPIRED = cfg->serve_expired; } @@ -1149,6 +1151,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_YNO(opt, "val-permissive-mode", val_permissive_mode) else O_YNO(opt, "aggressive-nsec", aggressive_nsec) else O_YNO(opt, "ignore-cd-flag", ignore_cd) + else O_YNO(opt, "disable-edns-do", disable_edns_do) else O_YNO(opt, "serve-expired", serve_expired) else O_DEC(opt, "serve-expired-ttl", serve_expired_ttl) else O_YNO(opt, "serve-expired-ttl-reset", serve_expired_ttl_reset) diff --git a/util/config_file.h b/util/config_file.h index 452f3c6a7..d71d7ace5 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -409,6 +409,8 @@ struct config_file { int aggressive_nsec; /** ignore the CD flag in incoming queries and refuse them bogus data */ int ignore_cd; + /** disable EDNS DO flag in outgoing requests */ + int disable_edns_do; /** serve expired entries and prefetch them */ int serve_expired; /** serve expired entries until TTL after expiration */ diff --git a/util/configlexer.lex b/util/configlexer.lex index 3fcdfa62e..2b3141a42 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -403,6 +403,7 @@ val-clean-additional{COLON} { YDVAR(1, VAR_VAL_CLEAN_ADDITIONAL) } val-permissive-mode{COLON} { YDVAR(1, VAR_VAL_PERMISSIVE_MODE) } aggressive-nsec{COLON} { YDVAR(1, VAR_AGGRESSIVE_NSEC) } ignore-cd-flag{COLON} { YDVAR(1, VAR_IGNORE_CD_FLAG) } +disable-edns-do{COLON} { YDVAR(1, VAR_DISABLE_EDNS_DO) } serve-expired{COLON} { YDVAR(1, VAR_SERVE_EXPIRED) } serve-expired-ttl{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL) } serve-expired-ttl-reset{COLON} { YDVAR(1, VAR_SERVE_EXPIRED_TTL_RESET) } diff --git a/util/configparser.y b/util/configparser.y index d8f25a67e..cbea62b58 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -198,7 +198,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO -%token VAR_HARDEN_UNKNOWN_ADDITIONAL +%token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_DISABLE_EDNS_DO %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -332,7 +332,7 @@ content_server: server_num_threads | server_verbosity | server_port | server_tcp_reuse_timeout | server_tcp_auth_query_timeout | server_interface_automatic_ports | server_ede | server_proxy_protocol_port | server_statistics_inhibit_zero | - server_harden_unknown_additional + server_harden_unknown_additional | server_disable_edns_do ; stubstart: VAR_STUB_ZONE { @@ -2060,6 +2060,15 @@ server_ignore_cd_flag: VAR_IGNORE_CD_FLAG STRING_ARG free($2); } ; +server_disable_edns_do: VAR_DISABLE_EDNS_DO STRING_ARG + { + OUTYY(("P(server_disable_edns_do:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->disable_edns_do = (strcmp($2, "yes")==0); + free($2); + } + ; server_serve_expired: VAR_SERVE_EXPIRED STRING_ARG { OUTYY(("P(server_serve_expired:%s)\n", $2)); From 6e65343895e1a9cdf31e51e25bc5540851fb4b1a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 14 Sep 2023 11:37:49 +0200 Subject: [PATCH 19/64] - Fix authority zone answers for obscured DNAMEs and delegations. --- doc/Changelog | 3 ++ services/authzone.c | 11 ++-- testcode/unitauth.c | 128 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 4 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index fb955f5a3..fef0a4c7c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +14 September 2023: Wouter + - Fix authority zone answers for obscured DNAMEs and delegations. + 8 September 2023: Wouter - Fix send of udp retries when ENOBUFS is returned. It stops looping and also waits for the condition to go away. Reported by Florian diff --git a/services/authzone.c b/services/authzone.c index cd3ef8dbb..a1b3d2278 100644 --- a/services/authzone.c +++ b/services/authzone.c @@ -2475,6 +2475,7 @@ az_find_ce(struct auth_zone* z, struct query_info* qinfo, struct auth_rrset** rrset) { struct auth_data* n = node; + struct auth_rrset* lookrrset; *ce = NULL; *rrset = NULL; if(!node_exact) { @@ -2497,21 +2498,23 @@ az_find_ce(struct auth_zone* z, struct query_info* qinfo, /* see if the current candidate has issues */ /* not zone apex and has type NS */ if(n->namelen != z->namelen && - (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) && + (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_NS)) && /* delegate here, but DS at exact the dp has notype */ (qinfo->qtype != LDNS_RR_TYPE_DS || n->namelen != qinfo->qname_len)) { /* referral */ /* this is ce and the lowernode is nonexisting */ *ce = n; - return 0; + *rrset = lookrrset; + node_exact = 0; } /* not equal to qname and has type DNAME */ if(n->namelen != qinfo->qname_len && - (*rrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) { + (lookrrset=az_domain_rrset(n, LDNS_RR_TYPE_DNAME))) { /* this is ce and the lowernode is nonexisting */ *ce = n; - return 0; + *rrset = lookrrset; + node_exact = 0; } if(*ce == NULL && !domain_has_only_nsec3(n)) { diff --git a/testcode/unitauth.c b/testcode/unitauth.c index d193526b8..11eeb43b2 100644 --- a/testcode/unitauth.c +++ b/testcode/unitauth.c @@ -76,10 +76,18 @@ static const char* zone_example_com = "out.example.com. 3600 IN CNAME www.example.com.\n" "plan.example.com. 3600 IN CNAME nonexist.example.com.\n" "redir.example.com. 3600 IN DNAME redir.example.org.\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"obscured.redir2.example.com. 3600 IN A 10.0.0.12\n" +"under2.redir2.example.com. 3600 IN DNAME redir3.example.net.\n" +"doubleobscured.under2.redir2.example.com. 3600 IN A 10.0.0.13\n" "sub.example.com. 3600 IN NS ns1.sub.example.com.\n" "sub.example.com. 3600 IN NS ns2.sub.example.com.\n" "ns1.sub.example.com. 3600 IN A 10.0.0.6\n" "ns2.sub.example.com. 3600 IN AAAA 2001::7\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +"obscured.sub2.example.com. 3600 IN A 10.0.0.10\n" +"under.sub2.example.com. 3600 IN NS ns.under.sub2.example.com.\n" +"doubleobscured.under.sub2.example.com. 3600 IN A 10.0.0.11\n" "*.wild.example.com. 3600 IN A 10.0.0.8\n" "*.wild2.example.com. 3600 IN CNAME www.example.com.\n" "*.wild3.example.com. 3600 IN A 10.0.0.8\n" @@ -281,6 +289,54 @@ static struct q_ans example_com_queries[] = { "foo.abc.redir.example.com. 0 IN CNAME foo.abc.redir.example.org.\n" }, + { "example.com", "redir2.example.com. DNAME", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" + }, + + { "example.com", "abc.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"abc.redir2.example.com. 0 IN CNAME abc.redir2.example.org.\n" + }, + + { "example.com", "obscured.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"obscured.redir2.example.com. 0 IN CNAME obscured.redir2.example.org.\n" + }, + + { "example.com", "under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"under2.redir2.example.com. 0 IN CNAME under2.redir2.example.org.\n" + }, + + { "example.com", "doubleobscured.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"doubleobscured.under2.redir2.example.com. 0 IN CNAME doubleobscured.under2.redir2.example.org.\n" + }, + + { "example.com", "foo.doubleobscured.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"foo.doubleobscured.under2.redir2.example.com. 0 IN CNAME foo.doubleobscured.under2.redir2.example.org.\n" + }, + + { "example.com", "foo.under2.redir2.example.com. A", "", +";flags QR AA rcode NOERROR\n" +";answer section\n" +"redir2.example.com. 3600 IN DNAME redir2.example.org.\n" +"foo.under2.redir2.example.com. 0 IN CNAME foo.under2.redir2.example.org.\n" + }, + { "example.com", "sub.example.com. NS", "", ";flags QR rcode NOERROR\n" ";authority section\n" @@ -357,6 +413,78 @@ static struct q_ans example_com_queries[] = { "ns2.sub.example.com. 3600 IN AAAA 2001::7\n" }, + { "example.com", "sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "sub2.example.com. NS", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "obscured.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.obscured.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "under.sub2.example.com. NS", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "doubleobscured.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + + { "example.com", "abc.doubleobscured.under.sub2.example.com. A", "", +";flags QR rcode NOERROR\n" +";authority section\n" +"sub2.example.com. 3600 IN NS ns1.sub.example.com.\n" +";additional section\n" +"ns1.sub.example.com. 3600 IN A 10.0.0.6\n" + }, + { "example.com", "wild.example.com. A", "", ";flags QR AA rcode NOERROR\n" ";authority section\n" From 3abb32c3ba7886e0bbb892d045fed66371b7e083 Mon Sep 17 00:00:00 2001 From: Florian Obser Date: Fri, 15 Sep 2023 13:00:17 +0200 Subject: [PATCH 20/64] Check for c99 with autoconf versions prior to 2.70 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index f6f5f21fc..9aa781a12 100644 --- a/configure.ac +++ b/configure.ac @@ -280,7 +280,7 @@ ACX_CHECK_COMPILER_FLAG(g, [CFLAGS="$CFLAGS -g"]) ACX_CHECK_COMPILER_FLAG(O2, [CFLAGS="$CFLAGS -O2"]) default_cflags=yes fi -AC_PROG_CC +m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_STDC]) ACX_DEPFLAG ACX_DETERMINE_EXT_FLAGS_UNBOUND From 6bdecdbc5a7a33ea412e6e0d3373bc4c4b79bdc7 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 15 Sep 2023 13:24:20 +0200 Subject: [PATCH 21/64] Changelog note and autoconf for #936 - Merge #936: Check for c99 with autoconf versions prior to 2.70. --- configure | 589 +++++++++++++++----------------------------------- doc/Changelog | 3 + 2 files changed, 174 insertions(+), 418 deletions(-) diff --git a/configure b/configure index 4521e0a6e..8324e9b34 100755 --- a/configure +++ b/configure @@ -4605,450 +4605,186 @@ fi default_cflags=yes fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : + case $ac_cv_prog_cc_stdc in #( + no) : + ac_cv_prog_cc_c99=no; ac_cv_prog_cc_c89=no ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 +$as_echo_n "checking for $CC option to accept ISO C99... " >&6; } +if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +#include -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi +// Check varargs macros. These examples are taken from C99 6.10.3.5. +#define debug(...) fprintf (stderr, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + your preprocessor is broken; +#endif +#if BIG_OK +#else + your preprocessor is broken; +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; - test -n "$ac_ct_CC" && break -done +struct incomplete_array +{ + int datasize; + double data[]; +}; - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi +struct named_init { + int number; + const wchar_t *name; + double average; +}; -fi +typedef const char *ccp; +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\0'; ++i) + continue; + return 0; +} -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } +// Check varargs and va_copy. +static void +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done + const char *str; + int number; + float fnumber; -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + while (*format) + { + switch (*format++) + { + case 's': // string + str = va_arg (args_copy, const char *); + break; + case 'd': // int + number = va_arg (args_copy, int); + break; + case 'f': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); +} int main () { -#ifndef __GNUC__ - choke me -#endif - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu + // Check bool. + _Bool success = false; -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; -int -main () -{ + // Check varargs. + test_varargs ("s, d' f .", "string", 65, 34.234); + test_varargs_macros (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; -int -main () -{ + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : + ni.number = 58; -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ + int dynamic_array[ni.number]; + dynamic_array[ni.number - 1] = 543; -int -main () -{ + // work around unused variable warnings + return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' + || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c99=$ac_arg fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c99" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c99" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +$as_echo "$ac_cv_prog_cc_c99" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c99" != xno; then : + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 @@ -5135,14 +4871,31 @@ $as_echo "unsupported" >&6; } ;; $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 +else + ac_cv_prog_cc_stdc=no +fi +fi + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO Standard C" >&5 +$as_echo_n "checking for $CC option to accept ISO Standard C... " >&6; } + if ${ac_cv_prog_cc_stdc+:} false; then : + $as_echo_n "(cached) " >&6 fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu + case $ac_cv_prog_cc_stdc in #( + no) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; #( + '') : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_stdc" >&5 +$as_echo "$ac_cv_prog_cc_stdc" >&6; } ;; +esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking $CC dependency flag" >&5 diff --git a/doc/Changelog b/doc/Changelog index fef0a4c7c..edec31aca 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +15 September 2023: Wouter + - Merge #936: Check for c99 with autoconf versions prior to 2.70. + 14 September 2023: Wouter - Fix authority zone answers for obscured DNAMEs and delegations. From 31218166fcd2a56ccc65d359071d5831c16a18a2 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 15 Sep 2023 13:30:30 +0200 Subject: [PATCH 22/64] - Fix to remove two c99 notations. --- doc/Changelog | 1 + dynlibmod/dynlibmod.c | 3 ++- util/net_help.c | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index edec31aca..998b0d33c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,5 +1,6 @@ 15 September 2023: Wouter - Merge #936: Check for c99 with autoconf versions prior to 2.70. + - Fix to remove two c99 notations. 14 September 2023: Wouter - Fix authority zone answers for obscured DNAMEs and delegations. diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index ffac7ff30..1e040a30e 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -75,6 +75,7 @@ int dynlibmod_init(struct module_env* env, int id) { struct config_strlist* cfg_item = env->cfg->dynlib_file; struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env)); __DYNMOD dynamic_library; + int i; if (!de) { log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx); @@ -84,7 +85,7 @@ int dynlibmod_init(struct module_env* env, int id) { env->modinfo[id] = (void*) de; de->fname = NULL; - for(int i = dynlib_mod_idx; + for(i = dynlib_mod_idx; i != 0 && cfg_item != NULL; i--, cfg_item = cfg_item->next) {} diff --git a/util/net_help.c b/util/net_help.c index e559c9b2f..dec46da20 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -809,6 +809,7 @@ addr_to_nat64(const struct sockaddr_storage* addr, struct sockaddr_in *sin = (struct sockaddr_in *)addr; struct sockaddr_in6 *sin6; uint8_t *v4_byte; + int i; /* This needs to be checked by the caller */ log_assert(addr->ss_family == AF_INET); @@ -826,7 +827,7 @@ addr_to_nat64(const struct sockaddr_storage* addr, nat64_prefixnet = nat64_prefixnet / 8; v4_byte = (uint8_t *)&sin->sin_addr.s_addr; - for(int i = 0; i < 4; i++) { + for(i = 0; i < 4; i++) { if(nat64_prefixnet == 8) { /* bits 64...71 are MBZ */ sin6->sin6_addr.s6_addr[nat64_prefixnet++] = 0; From bd5dc855af122de1274cf7cdd2b52563c064702e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 18 Sep 2023 09:55:39 +0200 Subject: [PATCH 23/64] - Fix rpz tcp-only action with rpz triggers nsdname and nsip. --- doc/Changelog | 3 ++ services/mesh.c | 5 ++- services/rpz.c | 6 +-- testdata/rpz_nsdname.rpl | 81 ++++++++++++++++++++++++++++++++++++++++ util/module.h | 2 + 5 files changed, 92 insertions(+), 5 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 998b0d33c..1ff496d82 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +18 September 2023: Wouter + - Fix rpz tcp-only action with rpz triggers nsdname and nsip. + 15 September 2023: Wouter - Merge #936: Check for c99 with autoconf versions prior to 2.70. - Fix to remove two c99 notations. diff --git a/services/mesh.c b/services/mesh.c index 52d14a2d1..be968e422 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1224,11 +1224,12 @@ static inline int mesh_is_rpz_respip_tcponly_action(struct mesh_state const* m) { struct respip_action_info const* respip_info = m->s.respip_action_info; - return respip_info == NULL + return (respip_info == NULL ? 0 : (respip_info->rpz_used && !respip_info->rpz_disabled - && respip_info->action == respip_truncate); + && respip_info->action == respip_truncate)) + || m->s.tcp_required; } static inline int diff --git a/services/rpz.c b/services/rpz.c index 6ce83cb66..7f7a2fa9b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2162,7 +2162,7 @@ rpz_apply_nsip_trigger(struct module_qstate* ms, struct rpz* r, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: @@ -2217,7 +2217,7 @@ rpz_apply_nsdname_trigger(struct module_qstate* ms, struct rpz* r, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: @@ -2428,7 +2428,7 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, case RPZ_TCP_ONLY_ACTION: /* basically a passthru here but the tcp-only will be * honored before the query gets sent. */ - ms->respip_action_info->action = respip_truncate; + ms->tcp_required = 1; ret = NULL; break; case RPZ_DROP_ACTION: diff --git a/testdata/rpz_nsdname.rpl b/testdata/rpz_nsdname.rpl index 1c678cc13..a4e9bb31d 100644 --- a/testdata/rpz_nsdname.rpl +++ b/testdata/rpz_nsdname.rpl @@ -225,6 +225,36 @@ ENTRY_END RANGE_END +; dd. ------------------------------------------------------------------------ +RANGE_BEGIN 0 100 + ADDRESS 8.8.3.8 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +dd. IN NS +SECTION ANSWER +dd. IN NS ns1.dd. +SECTION ADDITIONAL +ns1.dd. IN A 8.8.3.8 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION AUTHORITY +gotham.dd. IN NS ns1.gotham.dd. +SECTION ADDITIONAL +ns1.gotham.dd. IN A 192.0.3.1 +ENTRY_END + +RANGE_END + ; ff. ------------------------------------------------------------------------ RANGE_BEGIN 0 100 ADDRESS 8.8.6.8 @@ -303,6 +333,22 @@ ENTRY_END RANGE_END +; ns1.gotham.dd. ------------------------------------------------------------- +RANGE_BEGIN 0 100 + ADDRESS 192.0.3.1 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +gotham.dd. IN A 192.0.3.2 +ENTRY_END + +RANGE_END + ; ns1.gotham.ff. ------------------------------------------------------------- RANGE_BEGIN 0 100 ADDRESS 192.0.5.1 @@ -387,4 +433,39 @@ SECTION ANSWER gotham.ff. IN A 127.0.0.1 ENTRY_END +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +gotham.dd. IN A +ENTRY_END + +; should come back truncated because TCP is required. +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA TC NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +ENTRY_END + +STEP 42 QUERY +ENTRY_BEGIN +MATCH TCP +REPLY RD +SECTION QUESTION +gotham.dd. IN A +ENTRY_END + +STEP 43 CHECK_ANSWER +ENTRY_BEGIN +MATCH all TCP +REPLY QR RD RA NOERROR +SECTION QUESTION +gotham.dd. IN A +SECTION ANSWER +gotham.dd. IN A 192.0.3.2 +ENTRY_END + SCENARIO_END diff --git a/util/module.h b/util/module.h index d25bebd15..8a9da3f93 100644 --- a/util/module.h +++ b/util/module.h @@ -691,6 +691,8 @@ struct module_qstate { struct respip_action_info* respip_action_info; /** if the query is rpz passthru, no further rpz processing for it */ int rpz_passthru; + /* Flag tcp required. */ + int tcp_required; /** whether the reply should be dropped */ int is_drop; From 9aaafddf04ddec500230a18e471376bc4f401c1a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 20 Sep 2023 13:28:06 +0200 Subject: [PATCH 24/64] - disable-edns-do, the option turns of the EDNS DO flag when a message is sent from the iterator. --- iterator/iterator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iterator/iterator.c b/iterator/iterator.c index 9f78aa17d..edb954cb5 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2875,7 +2875,8 @@ processQueryTargets(struct module_qstate* qstate, struct iter_qstate* iq, /* unset CD if to forwarder(RD set) and not dnssec retry * (blacklist nonempty) and no trust-anchors are configured * above the qname or on the first attempt when dnssec is on */ - EDNS_DO| ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&& + (qstate->env->cfg->disable_edns_do?0:EDNS_DO)| + ((iq->chase_to_rd||(iq->chase_flags&BIT_RD)!=0)&& !qstate->blacklist&&(!iter_qname_indicates_dnssec(qstate->env, &iq->qinfo_out)||target->attempts==1)?0:BIT_CD), iq->dnssec_expected, iq->caps_fallback || is_caps_whitelisted( From 4e5b0b7eec224c0fd369dfd760d07361874f238c Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 22 Sep 2023 11:39:39 +0200 Subject: [PATCH 25/64] - disable-edns-do, unit test checks lookup without EDNS DO flag. --- testcode/fake_event.c | 2 +- testdata/disable_edns_do.rpl | 164 +++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 testdata/disable_edns_do.rpl diff --git a/testcode/fake_event.c b/testcode/fake_event.c index 2140b212a..13970c377 100644 --- a/testcode/fake_event.c +++ b/testcode/fake_event.c @@ -1249,7 +1249,7 @@ struct serviced_query* outnet_serviced_query(struct outside_network* outnet, edns.edns_version = EDNS_ADVERTISED_VERSION; edns.udp_size = EDNS_ADVERTISED_SIZE; edns.bits = 0; - if(dnssec) + if((dnssec & EDNS_DO)) edns.bits = EDNS_DO; edns.padding_block_size = 0; edns.cookie_present = 0; diff --git a/testdata/disable_edns_do.rpl b/testdata/disable_edns_do.rpl new file mode 100644 index 000000000..86b0c32a9 --- /dev/null +++ b/testdata/disable_edns_do.rpl @@ -0,0 +1,164 @@ +; config options +; The island of trust is at example.com +server: + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: "no" + trust-anchor-signaling: no + minimal-responses: no + disable-edns-do: yes + +stub-zone: + name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test lookup with disable-edns-do + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 100 + ADDRESS 192.5.6.30 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +com. IN NS +SECTION ANSWER +com. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +; ns.example.com. +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN NS +SECTION ANSWER +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to DNSKEY priming query +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +example.com. IN DNSKEY +SECTION ANSWER +example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b} +example.com. 3600 IN RRSIG DNSKEY 3 2 3600 20070926134802 20070829134802 2854 example.com. MCwCFG1yhRNtTEa3Eno2zhVVuy2EJX3wAhQeLyUp6+UXcpC5qGNu9tkrTEgPUg== ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854} +ENTRY_END + +; response to query of interest, when sent with EDNS DO +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCQMyTjn7WWwpwAR1LlVeLpRgZGuQIUCcJDEkwAuzytTDRlYK7nIMwH1CM= ;{id = 2854} +SECTION AUTHORITY +example.com. IN NS ns.example.com. +example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854} +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +www.example.com. 3600 IN RRSIG A 3 3 3600 20070926134150 20070829134150 2854 example.com. MC0CFC99iE9K5y2WNgI0gFvBWaTi9wm6AhUAoUqOpDtG5Zct+Qr9F3mSdnbc6V4= ;{id = 2854} +ENTRY_END + +; response to query of interest, when sent without DO +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +ENTRY_END + +; recursion happens here. +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN A 10.20.30.40 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +SCENARIO_END From 654a7eab62cbd1844d483cc4a0f2cf2fbcbaf00a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 22 Sep 2023 13:15:35 +0200 Subject: [PATCH 26/64] - Fix #942: 1.18.0 libunbound DNS regression when built without OpenSSL. --- doc/Changelog | 4 ++++ libunbound/libworker.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 1ff496d82..9b3ccf8ea 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +22 September 2023: Wouter + - Fix #942: 1.18.0 libunbound DNS regression when built without + OpenSSL. + 18 September 2023: Wouter - Fix rpz tcp-only action with rpz triggers nsdname and nsip. diff --git a/libunbound/libworker.c b/libunbound/libworker.c index 104244937..865c71438 100644 --- a/libunbound/libworker.c +++ b/libunbound/libworker.c @@ -168,6 +168,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) hints_delete(w->env->hints); w->env->hints = NULL; } +#ifdef HAVE_SSL w->sslctx = connect_sslctx_create(NULL, NULL, cfg->tls_cert_bundle, cfg->tls_win_cert); if(!w->sslctx) { @@ -175,6 +176,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb) hints_delete(w->env->hints); w->env->hints = NULL; } +#endif if(!w->is_bg || w->is_bg_thread) { lock_basic_unlock(&ctx->cfglock); } From 0102360e920d1b26fde0120f2ac1d7eb770fa3c4 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 29 Sep 2023 16:03:08 +0200 Subject: [PATCH 27/64] root-zonemd-test, add test for ZONEMD usage from the root zone, currently with the unsupported algorithm. --- testdata/root_zonemd.tdir/root_zonemd.conf | 34 +++++++++++++ testdata/root_zonemd.tdir/root_zonemd.dsc | 16 ++++++ testdata/root_zonemd.tdir/root_zonemd.post | 14 ++++++ testdata/root_zonemd.tdir/root_zonemd.pre | 50 +++++++++++++++++++ testdata/root_zonemd.tdir/root_zonemd.test | 51 ++++++++++++++++++++ testdata/root_zonemd.tdir/root_zonemd.testns | 9 ++++ 6 files changed, 174 insertions(+) create mode 100644 testdata/root_zonemd.tdir/root_zonemd.conf create mode 100644 testdata/root_zonemd.tdir/root_zonemd.dsc create mode 100644 testdata/root_zonemd.tdir/root_zonemd.post create mode 100644 testdata/root_zonemd.tdir/root_zonemd.pre create mode 100644 testdata/root_zonemd.tdir/root_zonemd.test create mode 100644 testdata/root_zonemd.tdir/root_zonemd.testns diff --git a/testdata/root_zonemd.tdir/root_zonemd.conf b/testdata/root_zonemd.tdir/root_zonemd.conf new file mode 100644 index 000000000..befb4fbe9 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.conf @@ -0,0 +1,34 @@ +server: + verbosity: 7 + # num-threads: 1 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + do-not-query-localhost: no + # for the test, so that DNSSEC verification works. + #val-override-date: 20230929090000 + trust-anchor: ". DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D" + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +# for the test, an upstream server in the test setup. +stub-zone: + name: "." + stub-addr: 127.0.0.1@@TOPORT@ + +# hyperlocal root zone +auth-zone: + name: "." + fallback-enabled: yes + for-downstream: no + for-upstream: yes + zonefile: "root.zone" + zonemd-check: yes + zonemd-reject-absence: yes diff --git a/testdata/root_zonemd.tdir/root_zonemd.dsc b/testdata/root_zonemd.tdir/root_zonemd.dsc new file mode 100644 index 000000000..8015ac2d1 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.dsc @@ -0,0 +1,16 @@ +BaseName: root_zonemd +Version: 1.0 +Description: ZONEMD check for root zone +CreationDate: Fri 29 Sep 09:00:00 CEST 2023 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: root_zonemd.pre +Post: root_zonemd.post +Test: root_zonemd.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/root_zonemd.tdir/root_zonemd.post b/testdata/root_zonemd.tdir/root_zonemd.post new file mode 100644 index 000000000..a28599faf --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.post @@ -0,0 +1,14 @@ +# #-- root_zonemd.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 +echo "> cat logfiles" +cat fwd.log +cat unbound.log +kill_pid $FWD_PID +kill_pid $UNBOUND_PID +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID diff --git a/testdata/root_zonemd.tdir/root_zonemd.pre b/testdata/root_zonemd.tdir/root_zonemd.pre new file mode 100644 index 000000000..fe369bb20 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.pre @@ -0,0 +1,50 @@ +# #-- root_zonemd.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 + +# attempt to download the root zone +from=k.root-servers.net +dig @$from . AXFR > root.txt +if test $? -ne 0; then + echo "could not fetch root zone" + skip_test "could not fetch root zone" +fi +grep " SOA " root.txt | head -1 > root.soa +cat root.soa >> root.zone +grep -v " SOA " root.txt >> root.zone +echo "fetched root.zone" +ls -l root.zone +cat root.soa + +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 root_zonemd.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < root_zonemd.conf > ub.conf +# start unbound in the background +PRE="../.." +$PRE/unbound -d -c ub.conf >unbound.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log + diff --git a/testdata/root_zonemd.tdir/root_zonemd.test b/testdata/root_zonemd.tdir/root_zonemd.test new file mode 100644 index 000000000..da64ab6e9 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.test @@ -0,0 +1,51 @@ +# #-- root_zonemd.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 + +PRE="../.." +# do the test +echo "> dig www.example.com." +dig @localhost -p $UNBOUND_PORT . SOA | tee outfile +echo "> check answer" +if grep root-servers outfile | grep "nstld.verisign-grs.com"; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +echo "> unbound-control status" +$PRE/unbound-control -c ub.conf status +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +# This is the output when an unsupported algorithm is used. +if grep "auth zone . ZONEMD unsupported algorithm" unbound.log; then + echo "OK" +else + echo "ZONEMD verification not OK" + exit 1 +fi + +echo "> unbound-control auth_zone_reload ." +$PRE/unbound-control -c ub.conf auth_zone_reload . 2>&1 | tee outfile +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +fi +# The output of the reload can be checked. +#echo "> check unbound-control output" +#if grep "example.com: ZONEMD verification successful" outfile; then + #echo "OK" +#else + #echo "Not OK" + #exit 1 +#fi + +exit 0 diff --git a/testdata/root_zonemd.tdir/root_zonemd.testns b/testdata/root_zonemd.tdir/root_zonemd.testns new file mode 100644 index 000000000..d538f2215 --- /dev/null +++ b/testdata/root_zonemd.tdir/root_zonemd.testns @@ -0,0 +1,9 @@ +# reply to everything +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR SERVFAIL +SECTION QUESTION +example.com. IN SOA +SECTION ANSWER +ENTRY_END From f804c087e41d47aad0999984d35e52672f4dccaa Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Fri, 29 Sep 2023 17:31:52 +0200 Subject: [PATCH 28/64] proxy-protocol, review comments: - more generic switch statement for address families; - comment the protocol values as such in their definitions; - less hardcoded values for address family and protocol combinations. --- util/netevent.c | 12 ++++++------ util/proxy_protocol.c | 27 ++++++++++++++++----------- util/proxy_protocol.h | 21 +++++++++++++++++---- 3 files changed, 39 insertions(+), 21 deletions(-) diff --git a/util/netevent.c b/util/netevent.c index edb9da8b5..6a455e858 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -772,7 +772,7 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, * No need to do anything with addresses. */ goto done; } - if(header->fam_prot == 0x00) { + if(header->fam_prot == PP2_UNSPEC_UNSPEC) { /* Unspecified family and protocol. This could be used for * health checks by proxies. * No need to do anything with addresses. */ @@ -780,8 +780,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Read the proxied address */ switch(header->fam_prot) { - case 0x11: /* AF_INET|STREAM */ - case 0x12: /* AF_INET|DGRAM */ + case PP2_INET_STREAM: + case PP2_INET_DGRAM: { struct sockaddr_in* addr = (struct sockaddr_in*)&rep->client_addr; @@ -792,8 +792,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, } /* Ignore the destination address; it should be us. */ break; - case 0x21: /* AF_INET6|STREAM */ - case 0x22: /* AF_INET6|DGRAM */ + case PP2_INET6_STREAM: + case PP2_INET6_DGRAM: { struct sockaddr_in6* addr = (struct sockaddr_in6*)&rep->client_addr; @@ -808,7 +808,7 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, break; default: log_err("proxy_protocol: unsupported family and " - "protocol"); + "protocol 0x%x", (int)header->fam_prot); return 0; } rep->is_proxied = 1; diff --git a/util/proxy_protocol.c b/util/proxy_protocol.c index 03db06037..a18804974 100644 --- a/util/proxy_protocol.c +++ b/util/proxy_protocol.c @@ -105,7 +105,8 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, /* version and command */ *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY; buf++; - if(af==AF_INET) { + switch(af) { + case AF_INET: /* family and protocol */ *buf = (PP2_AF_INET<<4) | (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); @@ -127,8 +128,9 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, /* dst addr */ /* dst port */ (*pp_data.write_uint16)(buf, 12); - } else { + break; #ifdef INET6 + case AF_INET6: /* family and protocol */ *buf = (PP2_AF_INET6<<4) | (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM); @@ -148,9 +150,12 @@ pp2_write_to_buf(uint8_t* buf, size_t buflen, buf += 2; /* dst port */ (*pp_data.write_uint16)(buf, 0); -#else - return 0; + break; #endif /* INET6 */ + case AF_UNIX: + /* fallthrough */ + default: + return 0; } return expected_size; } @@ -180,13 +185,13 @@ pp2_read_header(uint8_t* buf, size_t buflen) return PP_PARSE_UNKNOWN_CMD; } /* Check for supported family and protocol */ - if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ && - header->fam_prot != 0x11 /* AF_INET|STREAM */ && - header->fam_prot != 0x12 /* AF_INET|DGRAM */ && - header->fam_prot != 0x21 /* AF_INET6|STREAM */ && - header->fam_prot != 0x22 /* AF_INET6|DGRAM */ && - header->fam_prot != 0x31 /* AF_UNIX|STREAM */ && - header->fam_prot != 0x32 /* AF_UNIX|DGRAM */) { + if(header->fam_prot != PP2_UNSPEC_UNSPEC && + header->fam_prot != PP2_INET_STREAM && + header->fam_prot != PP2_INET_DGRAM && + header->fam_prot != PP2_INET6_STREAM && + header->fam_prot != PP2_INET6_DGRAM && + header->fam_prot != PP2_UNIX_STREAM && + header->fam_prot != PP2_UNIX_DGRAM) { return PP_PARSE_UNKNOWN_FAM_PROT; } /* We have a correct header */ diff --git a/util/proxy_protocol.h b/util/proxy_protocol.h index 58d3f8d57..ca81065bf 100644 --- a/util/proxy_protocol.h +++ b/util/proxy_protocol.h @@ -51,11 +51,11 @@ #define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" #define PP2_SIG_LEN 12 -/** PROXYv2 version */ +/** PROXYv2 version (protocol value) */ #define PP2_VERSION 0x2 /** - * PROXYv2 command. + * PROXYv2 command (protocol value). */ enum pp2_command { PP2_CMD_LOCAL = 0x0, @@ -63,7 +63,7 @@ enum pp2_command { }; /** - * PROXYv2 address family. + * PROXYv2 address family (protocol value). */ enum pp2_af { PP2_AF_UNSPEC = 0x0, @@ -73,7 +73,7 @@ enum pp2_af { }; /** - * PROXYv2 protocol. + * PROXYv2 protocol (protocol value). */ enum pp2_protocol { PP2_PROT_UNSPEC = 0x0, @@ -81,6 +81,19 @@ enum pp2_protocol { PP2_PROT_DGRAM = 0x2 }; +/** + * Expected combinations of address family and protocol values used in checks. + */ +enum pp2_af_protocol_combination { + PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC, + PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM, + PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM, + PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM, + PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM, + PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM, + PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM +}; + /** * PROXYv2 header. */ From 9342bf685e0742e1147bfee81b2985affc0b60bc Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 2 Oct 2023 16:13:23 +0200 Subject: [PATCH 29/64] - Fix misplaced comment. --- doc/Changelog | 3 +++ services/outside_network.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/Changelog b/doc/Changelog index 9b3ccf8ea..cc79cc3c8 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +2 October 2023: George + - Fix misplaced comment. + 22 September 2023: Wouter - Fix #942: 1.18.0 libunbound DNS regression when built without OpenSSL. diff --git a/services/outside_network.c b/services/outside_network.c index 2a219cbc6..12923f07d 100644 --- a/services/outside_network.c +++ b/services/outside_network.c @@ -550,7 +550,6 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, log_assert(&key_p.reuse != (struct reuse_tcp*)result); log_assert(&key_p != ((struct reuse_tcp*)result)->pending); } - /* not found, return null */ /* It is possible that we search for something before the first element * in the tree. Replace a null pointer with the first element. @@ -560,6 +559,7 @@ reuse_tcp_find(struct outside_network* outnet, struct sockaddr_storage* addr, result = rbtree_first(&outnet->tcp_reuse); } + /* not found, return null */ if(!result || result == RBTREE_NULL) return NULL; From 39df4f09234448219c10870180376a0f88ae0e69 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 4 Oct 2023 13:54:05 +0200 Subject: [PATCH 30/64] - disable-edns-do, queriers receive no EDNS in response if the disable-edns-do option is enabled and they set the DO flag. And unit test for that. --- daemon/worker.c | 6 ++++++ doc/unbound.conf.5.in | 2 ++ services/mesh.c | 4 ++++ testdata/disable_edns_do.rpl | 2 +- util/data/msgencode.c | 4 +++- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/daemon/worker.c b/daemon/worker.c index 2433f97dd..8ae05eb67 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -543,6 +543,8 @@ answer_norec_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; if(!inplace_cb_reply_cache_call(&worker->env, qinfo, NULL, msg->rep, (int)(flags&LDNS_RCODE_MASK), edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -703,6 +705,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; if(!inplace_cb_reply_servfail_call(&worker->env, qinfo, NULL, rep, LDNS_RCODE_SERVFAIL, edns, repinfo, worker->scratchpad, worker->env.now_tv)) @@ -743,6 +747,8 @@ answer_from_cache(struct worker* worker, struct query_info* qinfo, edns->udp_size = EDNS_ADVERTISED_SIZE; edns->ext_rcode = 0; edns->bits &= EDNS_DO; + if(worker->env.cfg->disable_edns_do && (edns->bits & EDNS_DO)) + edns->edns_present = 0; *alias_rrset = NULL; /* avoid confusion if caller set it to non-NULL */ if((worker->daemon->use_response_ip || worker->daemon->use_rpz) && !partial_rep && !apply_respip_action(worker, qinfo, cinfo, rep, diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index bbc6ddae5..b56fe20bb 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1307,6 +1307,8 @@ Disable the EDNS DO flag in upstream requests. This can be helpful for devices that cannot handle DNSSEC information. But it should not be enabled otherwise, because that would stop DNSSEC validation. The DNSSEC validation would not work for Unbound itself, and also not for downstream users. +When the option is enabled, queriers that set the DO flag receive no EDNS +record in the response to indicate the lack of support to them. Default is no. .TP .B serve\-expired: \fI diff --git a/services/mesh.c b/services/mesh.c index be968e422..509bee36a 100644 --- a/services/mesh.c +++ b/services/mesh.c @@ -1197,6 +1197,8 @@ mesh_do_callback(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; + if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) + r->edns.edns_present = 0; if(!inplace_cb_reply_call(m->s.env, &m->s.qinfo, &m->s, rep, LDNS_RCODE_NOERROR, &r->edns, NULL, m->s.region, start_time) || @@ -1372,6 +1374,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep, r->edns.udp_size = EDNS_ADVERTISED_SIZE; r->edns.ext_rcode = 0; r->edns.bits &= EDNS_DO; + if(m->s.env->cfg->disable_edns_do && (r->edns.bits&EDNS_DO)) + r->edns.edns_present = 0; m->s.qinfo.qname = r->qname; m->s.qinfo.local_alias = r->local_alias; diff --git a/testdata/disable_edns_do.rpl b/testdata/disable_edns_do.rpl index 86b0c32a9..82a16da06 100644 --- a/testdata/disable_edns_do.rpl +++ b/testdata/disable_edns_do.rpl @@ -150,7 +150,7 @@ ENTRY_END STEP 10 CHECK_ANSWER ENTRY_BEGIN MATCH all -REPLY QR RD RA DO NOERROR +REPLY QR RD RA NOERROR SECTION QUESTION www.example.com. IN A SECTION ANSWER diff --git a/util/data/msgencode.c b/util/data/msgencode.c index a170eb7b8..80ae33a38 100644 --- a/util/data/msgencode.c +++ b/util/data/msgencode.c @@ -1012,8 +1012,10 @@ reply_info_answer_encode(struct query_info* qinf, struct reply_info* rep, ede_size = calc_ede_option_size(edns, &ede_txt_size); if(sldns_buffer_capacity(pkt) < udpsize) udpsize = sldns_buffer_capacity(pkt); + if(!edns || !edns->edns_present) { + attach_edns = 0; /* EDEs are optional, try to fit anything else before them */ - if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { + } else if(udpsize < LDNS_HEADER_SIZE + edns_field_size - ede_size) { /* packet too small to contain edns, omit it. */ attach_edns = 0; } else { From 2e7714e80cdbf8491cc325ba2901abd7a4700d68 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 4 Oct 2023 15:28:52 +0200 Subject: [PATCH 31/64] - disable-edns-do, unbound-checkconf warns about disable-edns-do and DNSSEC validation that is enabled, and suggests to turn one off. --- smallapp/unbound-checkconf.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/smallapp/unbound-checkconf.c b/smallapp/unbound-checkconf.c index ff8043711..8b45578fa 100644 --- a/smallapp/unbound-checkconf.c +++ b/smallapp/unbound-checkconf.c @@ -707,6 +707,23 @@ morechecks(struct config_file* cfg) cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg); check_chroot_filelist_wild("trusted-keys-file", cfg->trusted_keys_file_list, cfg->chrootdir, cfg); + if(cfg->disable_edns_do && strstr(cfg->module_conf, "validator") + && (cfg->trust_anchor_file_list + || cfg->trust_anchor_list + || cfg->auto_trust_anchor_file_list + || cfg->trusted_keys_file_list)) { + char* key = NULL; + if(cfg->auto_trust_anchor_file_list) + key = cfg->auto_trust_anchor_file_list->str; + if(!key && cfg->trust_anchor_file_list) + key = cfg->trust_anchor_file_list->str; + if(!key && cfg->trust_anchor_list) + key = cfg->trust_anchor_list->str; + if(!key && cfg->trusted_keys_file_list) + key = cfg->trusted_keys_file_list->str; + if(!key) key = ""; + fatal_exit("disable-edns-do does not allow DNSSEC to work, but the validator module uses a trust anchor %s, turn off disable-edns-do or disable validation", key); + } #ifdef USE_IPSECMOD if(cfg->ipsecmod_enabled && strstr(cfg->module_conf, "ipsecmod")) { /* only check hook if enabled */ From b865aca03a5c653356334c789b54e70c0bd0e08d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 4 Oct 2023 18:16:22 +0200 Subject: [PATCH 32/64] - Fix #946: Forwarder returns servfail on upstream response noerror no data. --- Makefile.in | 2 +- doc/Changelog | 4 +++ iterator/iter_resptype.c | 25 ++++++++++++----- iterator/iter_resptype.h | 4 ++- iterator/iterator.c | 19 +++++++++++-- iterator/iterator.h | 7 +++++ testdata/iter_ignore_empty.rpl | 50 ++++++++++++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 12 deletions(-) diff --git a/Makefile.in b/Makefile.in index 627a650f6..22fb75c12 100644 --- a/Makefile.in +++ b/Makefile.in @@ -793,7 +793,7 @@ iter_priv.lo iter_priv.o: $(srcdir)/iterator/iter_priv.c config.h $(srcdir)/iter $(srcdir)/util/data/msgparse.h $(srcdir)/sldns/pkthdr.h $(srcdir)/sldns/rrdef.h $(srcdir)/util/net_help.h \ $(srcdir)/util/storage/dnstree.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/sbuffer.h iter_resptype.lo iter_resptype.o: $(srcdir)/iterator/iter_resptype.c config.h \ - $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/util/log.h \ + $(srcdir)/iterator/iter_resptype.h $(srcdir)/iterator/iter_delegpt.h $(srcdir)/iterator/iterator.h $(srcdir)/util/log.h \ $(srcdir)/services/cache/dns.h $(srcdir)/util/storage/lruhash.h $(srcdir)/util/locks.h \ $(srcdir)/util/data/msgreply.h $(srcdir)/util/data/packed_rrset.h $(srcdir)/util/net_help.h \ $(srcdir)/util/data/dname.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/pkthdr.h diff --git a/doc/Changelog b/doc/Changelog index 22029a89b..f18f97374 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +4 October 2023: Wouter + - Fix #946: Forwarder returns servfail on upstream response noerror no + data. + 3 October 2023: George - Merge #881: Generalise the proxy protocol code. diff --git a/iterator/iter_resptype.c b/iterator/iter_resptype.c index e85595b84..38e186e79 100644 --- a/iterator/iter_resptype.c +++ b/iterator/iter_resptype.c @@ -42,6 +42,7 @@ #include "config.h" #include "iterator/iter_resptype.h" #include "iterator/iter_delegpt.h" +#include "iterator/iterator.h" #include "services/cache/dns.h" #include "util/net_help.h" #include "util/data/dname.h" @@ -105,7 +106,8 @@ response_type_from_cache(struct dns_msg* msg, enum response_type response_type_from_server(int rdset, - struct dns_msg* msg, struct query_info* request, struct delegpt* dp) + struct dns_msg* msg, struct query_info* request, struct delegpt* dp, + int* empty_nodata_found) { uint8_t* origzone = (uint8_t*)"\000"; /* the default */ struct ub_packed_rrset_key* s; @@ -284,13 +286,22 @@ response_type_from_server(int rdset, /* If we've gotten this far, this is NOERROR/NODATA (which could * be an entirely empty message) */ - /* but ignore entirely empty messages, noerror/nodata has a soa - * negative ttl value in the authority section, this makes it try - * again at another authority. And turns it from a 5 second empty - * message into a 5 second servfail response. */ + /* For entirely empty messages, try again, at first, then accept + * it it happens more. A regular noerror/nodata response has a soa + * negative ttl value in the authority section. This makes it try + * again at another authority. And decides between storing a 5 second + * empty message or a 5 second servfail response. */ if(msg->rep->an_numrrsets == 0 && msg->rep->ns_numrrsets == 0 && - msg->rep->ar_numrrsets == 0) - return RESPONSE_TYPE_THROWAWAY; + msg->rep->ar_numrrsets == 0) { + if(empty_nodata_found) { + /* detect as throwaway at first, but accept later. */ + (*empty_nodata_found)++; + if(*empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) + return RESPONSE_TYPE_THROWAWAY; + return RESPONSE_TYPE_ANSWER; + } + return RESPONSE_TYPE_ANSWER; + } /* check if recursive answer; saying it has empty cache */ if( (msg->rep->flags&BIT_RA) && !(msg->rep->flags&BIT_AA) && !rdset) return RESPONSE_TYPE_REC_LAME; diff --git a/iterator/iter_resptype.h b/iterator/iter_resptype.h index fee9ef35f..bfd4b664f 100644 --- a/iterator/iter_resptype.h +++ b/iterator/iter_resptype.h @@ -119,9 +119,11 @@ enum response_type response_type_from_cache(struct dns_msg* msg, * @param request: the request that generated the response. * @param dp: The delegation point that was being queried * when the response was returned. + * @param empty_nodata_found: flag to keep track of empty nodata detection. * @return the response type (CNAME or ANSWER). */ enum response_type response_type_from_server(int rdset, - struct dns_msg* msg, struct query_info* request, struct delegpt* dp); + struct dns_msg* msg, struct query_info* request, struct delegpt* dp, + int* empty_nodata_found); #endif /* ITERATOR_ITER_RESPTYPE_H */ diff --git a/iterator/iterator.c b/iterator/iterator.c index 9f78aa17d..106e2877e 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -2940,7 +2940,7 @@ static int processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, struct iter_env* ie, int id) { - int dnsseclame = 0, origtypecname = 0; + int dnsseclame = 0, origtypecname = 0, orig_empty_nodata_found; enum response_type type; iq->num_current_queries--; @@ -2960,12 +2960,25 @@ processQueryResponse(struct module_qstate* qstate, struct iter_qstate* iq, return next_state(iq, QUERYTARGETS_STATE); } iq->timeout_count = 0; + orig_empty_nodata_found = iq->empty_nodata_found; type = response_type_from_server( (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), - iq->response, &iq->qinfo_out, iq->dp); + iq->response, &iq->qinfo_out, iq->dp, &iq->empty_nodata_found); iq->chase_to_rd = 0; /* remove TC flag, if this is erroneously set by TCP upstream */ iq->response->rep->flags &= ~BIT_TC; + if(orig_empty_nodata_found != iq->empty_nodata_found && + iq->empty_nodata_found < EMPTY_NODATA_RETRY_COUNT) { + /* try to search at another server */ + if(qstate->reply) { + struct delegpt_addr* a = delegpt_find_addr( + iq->dp, &qstate->reply->remote_addr, + qstate->reply->remote_addrlen); + /* make selection disprefer it */ + if(a) a->lame = 1; + } + return next_state(iq, QUERYTARGETS_STATE); + } if(type == RESPONSE_TYPE_REFERRAL && (iq->chase_flags&BIT_RD) && !iq->auth_zone_response) { /* When forwarding (RD bit is set), we handle referrals @@ -3501,7 +3514,7 @@ processPrimeResponse(struct module_qstate* qstate, int id) iq->response->rep->flags &= ~(BIT_RD|BIT_RA); /* ignore rec-lame */ type = response_type_from_server( (int)((iq->chase_flags&BIT_RD) || iq->chase_to_rd), - iq->response, &iq->qchase, iq->dp); + iq->response, &iq->qchase, iq->dp, NULL); if(type == RESPONSE_TYPE_ANSWER) { qstate->return_rcode = LDNS_RCODE_NOERROR; qstate->return_msg = iq->response; diff --git a/iterator/iterator.h b/iterator/iterator.h index fad7f03e6..e253f3f7e 100644 --- a/iterator/iterator.h +++ b/iterator/iterator.h @@ -101,6 +101,8 @@ extern int BLACKLIST_PENALTY; * Chosen so that the UNKNOWN_SERVER_NICENESS falls within the band of a * fast server, this causes server exploration as a side benefit. msec. */ #define RTT_BAND 400 +/** Number of retries for empty nodata packets before it is accepted. */ +#define EMPTY_NODATA_RETRY_COUNT 2 /** * Global state for the iterator. @@ -415,6 +417,11 @@ struct iter_qstate { */ int refetch_glue; + /** + * This flag detects that a completely empty nodata was received, + * already so that it is accepted later. */ + int empty_nodata_found; + /** list of pending queries to authoritative servers. */ struct outbound_list outlist; diff --git a/testdata/iter_ignore_empty.rpl b/testdata/iter_ignore_empty.rpl index c70dd7e8d..4b2f695b8 100644 --- a/testdata/iter_ignore_empty.rpl +++ b/testdata/iter_ignore_empty.rpl @@ -78,6 +78,18 @@ example2.com. IN NS ns2.example2.com. SECTION ADDITIONAL ns2.example2.com. IN A 1.2.3.5 ENTRY_END + +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +foo.com. IN NS +SECTION AUTHORITY +foo.com. IN NS ns.foo.com. +SECTION ADDITIONAL +ns.foo.com. IN A 1.2.3.5 +ENTRY_END RANGE_END ; ns.example.com. @@ -172,6 +184,27 @@ www.example.com. IN A SECTION ANSWER www.example.com. IN A 10.20.30.40 ENTRY_END + +; foo.com +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +ns.foo.com. IN AAAA +SECTION ANSWER +SECTION AUTHORITY +;foo.com. IN SOA ns2.foo.com root.foo.com 4 14400 3600 604800 3600 +ENTRY_END RANGE_END STEP 1 QUERY @@ -195,4 +228,21 @@ ENTRY_END ; wait for pending nameserver lookups. STEP 20 TRAFFIC +; Test that a nodata stays a nodata. +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +www.foo.com. IN A +ENTRY_END + +STEP 40 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +www.foo.com. IN A +SECTION ANSWER +ENTRY_END + SCENARIO_END From b624ed505064098a1002b23578f4862d1542f2ce Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 5 Oct 2023 14:33:22 +0200 Subject: [PATCH 33/64] - disable-edns-do, validator init prints warning when disable-edns-do is turned on, but there are trust anchors, and then turns off disable-edns-do. --- validator/val_anchor.c | 21 +++++++++++++++++++++ validator/val_anchor.h | 8 ++++++++ validator/validator.c | 11 +++++++++++ 3 files changed, 40 insertions(+) diff --git a/validator/val_anchor.c b/validator/val_anchor.c index b1a54e1f0..8466a8923 100644 --- a/validator/val_anchor.c +++ b/validator/val_anchor.c @@ -1322,3 +1322,24 @@ anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, free(taglist); return 0; } + +struct trust_anchor* +anchors_find_any_noninsecure(struct val_anchors* anchors) +{ + struct trust_anchor* ta, *next; + lock_basic_lock(&anchors->lock); + ta=(struct trust_anchor*)rbtree_first(anchors->tree); + while((rbnode_type*)ta != RBTREE_NULL) { + next = (struct trust_anchor*)rbtree_next(&ta->node); + lock_basic_lock(&ta->lock); + if(ta->numDS != 0 || ta->numDNSKEY != 0) { + /* not an insecurepoint */ + lock_basic_unlock(&anchors->lock); + return ta; + } + lock_basic_unlock(&ta->lock); + ta = next; + } + lock_basic_unlock(&anchors->lock); + return NULL; +} diff --git a/validator/val_anchor.h b/validator/val_anchor.h index 1597a7d62..02e7e17b5 100644 --- a/validator/val_anchor.h +++ b/validator/val_anchor.h @@ -240,4 +240,12 @@ size_t anchor_list_keytags(struct trust_anchor* ta, uint16_t* list, size_t num); int anchor_has_keytag(struct val_anchors* anchors, uint8_t* name, int namelabs, size_t namelen, uint16_t dclass, uint16_t keytag); +/** + * Find an anchor that is not an insecure point, if any, or there are no + * DNSSEC verification anchors if none. + * @param anchors: anchor storage + * @return trust anchor or NULL. It is locked. + */ +struct trust_anchor* anchors_find_any_noninsecure(struct val_anchors* anchors); + #endif /* VALIDATOR_VAL_ANCHOR_H */ diff --git a/validator/validator.c b/validator/validator.c index 9de9d54db..e83948492 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -200,6 +200,17 @@ val_init(struct module_env* env, int id) log_err("validator: could not apply configuration settings."); return 0; } + if(env->cfg->disable_edns_do) { + struct trust_anchor* anchor = anchors_find_any_noninsecure( + env->anchors); + if(anchor) { + char b[257]; + dname_str(anchor->name, b); + log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b); + lock_basic_unlock(&anchor->lock); + env->cfg->disable_edns_do = 0; + } + } return 1; } From ae96aa0a6d147de64531d36af25e86caf2905174 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 6 Oct 2023 13:22:10 +0200 Subject: [PATCH 34/64] - cachedb-no-store, implement `cachedb-no-store: yes` configuration option. --- cachedb/cachedb.c | 5 +++++ util/config_file.c | 5 +++++ util/config_file.h | 2 ++ util/configlexer.lex | 1 + util/configparser.y | 18 ++++++++++++++++-- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 30645268c..01f4a6d84 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -815,6 +815,11 @@ cachedb_handle_response(struct module_qstate* qstate, qstate->ext_state[id] = module_finished; return; } + if(qstate->env->cfg->cachedb_no_store) { + /* do not store the item in the external cache */ + qstate->ext_state[id] = module_finished; + return; + } /* store the item into the backend cache */ cachedb_extcache_store(qstate, ie); diff --git a/util/config_file.c b/util/config_file.c index 454096342..e33cd9d5a 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -381,6 +381,7 @@ config_create(void) #ifdef USE_CACHEDB if(!(cfg->cachedb_backend = strdup("testframe"))) goto error_exit; if(!(cfg->cachedb_secret = strdup("default"))) goto error_exit; + cfg->cachedb_no_store = 0; #ifdef USE_REDIS if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit; cfg->redis_server_path = NULL; @@ -819,6 +820,9 @@ int config_set_option(struct config_file* cfg, const char* opt, { IS_NUMBER_OR_ZERO; cfg->ipsecmod_max_ttl = atoi(val); } else S_YNO("ipsecmod-strict:", ipsecmod_strict) #endif +#ifdef USE_CACHEDB + else S_YNO("cachedb-no-store:", cachedb_no_store) +#endif /* USE_CACHEDB */ else if(strcmp(opt, "define-tag:") ==0) { return config_add_tag(cfg, val); /* val_sig_skew_min, max and val_max_restart are copied into val_env @@ -1306,6 +1310,7 @@ config_get_option(struct config_file* cfg, const char* opt, #ifdef USE_CACHEDB else O_STR(opt, "backend", cachedb_backend) else O_STR(opt, "secret-seed", cachedb_secret) + else O_YNO(opt, "cachedb-no-store", cachedb_no_store) #ifdef USE_REDIS else O_STR(opt, "redis-server-host", redis_server_host) else O_DEC(opt, "redis-server-port", redis_server_port) diff --git a/util/config_file.h b/util/config_file.h index 452f3c6a7..ce6507985 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -699,6 +699,8 @@ struct config_file { char* cachedb_backend; /** secret seed for hash key calculation */ char* cachedb_secret; + /** cachedb that does not store, but only reads from database, if on */ + int cachedb_no_store; #ifdef USE_REDIS /** redis server's IP address or host name */ char* redis_server_host; diff --git a/util/configlexer.lex b/util/configlexer.lex index 3fcdfa62e..144817879 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -557,6 +557,7 @@ ipsecmod-strict{COLON} { YDVAR(1, VAR_IPSECMOD_STRICT) } cachedb{COLON} { YDVAR(0, VAR_CACHEDB) } backend{COLON} { YDVAR(1, VAR_CACHEDB_BACKEND) } secret-seed{COLON} { YDVAR(1, VAR_CACHEDB_SECRETSEED) } +cachedb-no-store{COLON} { YDVAR(1, VAR_CACHEDB_NO_STORE) } redis-server-host{COLON} { YDVAR(1, VAR_CACHEDB_REDISHOST) } redis-server-port{COLON} { YDVAR(1, VAR_CACHEDB_REDISPORT) } redis-server-path{COLON} { YDVAR(1, VAR_CACHEDB_REDISPATH) } diff --git a/util/configparser.y b/util/configparser.y index d8f25a67e..4cc52cfb9 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -198,7 +198,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_INTERFACE_ACTION VAR_INTERFACE_VIEW VAR_INTERFACE_TAG %token VAR_INTERFACE_TAG_ACTION VAR_INTERFACE_TAG_DATA %token VAR_PROXY_PROTOCOL_PORT VAR_STATISTICS_INHIBIT_ZERO -%token VAR_HARDEN_UNKNOWN_ADDITIONAL +%token VAR_HARDEN_UNKNOWN_ADDITIONAL VAR_CACHEDB_NO_STORE %% toplevelvars: /* empty */ | toplevelvars toplevelvar ; @@ -3701,7 +3701,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 + redis_expire_records | redis_server_path | redis_server_password | + cachedb_no_store ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3727,6 +3728,19 @@ cachedb_secret_seed: VAR_CACHEDB_SECRETSEED STRING_ARG #endif } ; +cachedb_no_store: VAR_CACHEDB_NO_STORE STRING_ARG + { + #ifdef USE_CACHEDB + OUTYY(("P(cachedb_no_store:%s)\n", $2)); + if(strcmp($2, "yes") != 0 && strcmp($2, "no") != 0) + yyerror("expected yes or no."); + else cfg_parser->cfg->cachedb_no_store = (strcmp($2, "yes")==0); + #else + OUTYY(("P(Compiled without cachedb, ignoring)\n")); + #endif + free($2); + } + ; redis_server_host: VAR_CACHEDB_REDISHOST STRING_ARG { #if defined(USE_CACHEDB) && defined(USE_REDIS) From c8ae3de61009f5b4ebbd894e9b5d5942a895c477 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 6 Oct 2023 16:39:33 +0200 Subject: [PATCH 35/64] Update validator/validator.c Co-authored-by: Yorgos Thessalonikefs --- validator/validator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/validator.c b/validator/validator.c index e83948492..6cd15cfc1 100644 --- a/validator/validator.c +++ b/validator/validator.c @@ -204,7 +204,7 @@ val_init(struct module_env* env, int id) struct trust_anchor* anchor = anchors_find_any_noninsecure( env->anchors); if(anchor) { - char b[257]; + char b[LDNS_MAX_DOMAINLEN+2]; dname_str(anchor->name, b); log_warn("validator: disable-edns-do is enabled, but there is a trust anchor for '%s'. Since DNSSEC could not work, the disable-edns-do setting is turned off. Continuing without it.", b); lock_basic_unlock(&anchor->lock); From b05154218cdbe59f36e81f5a510a9d0547b5fb05 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 6 Oct 2023 16:40:34 +0200 Subject: [PATCH 36/64] Update doc/unbound.conf.5.in Co-authored-by: Yorgos Thessalonikefs --- doc/unbound.conf.5.in | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index b56fe20bb..e709725b1 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -1303,12 +1303,17 @@ the clients, and then Unbound provides them with DNSSEC protection. The default value is "no". .TP .B disable\-edns\-do: \fI -Disable the EDNS DO flag in upstream requests. This can be helpful for -devices that cannot handle DNSSEC information. But it should not be enabled -otherwise, because that would stop DNSSEC validation. The DNSSEC validation -would not work for Unbound itself, and also not for downstream users. -When the option is enabled, queriers that set the DO flag receive no EDNS +Disable the EDNS DO flag in upstream requests. +It breaks DNSSEC validation for Unbound's clients. +This results in the upstream name servers to not include DNSSEC records in +their replies and could be helpful for devices that cannot handle DNSSEC +information. +When the option is enabled, clients that set the DO flag receive no EDNS record in the response to indicate the lack of support to them. +If this option is enabled but Unbound is already configured for DNSSEC +validation (i.e., the validator module is enabled; default) this option is +implicitly turned off with a warning as to not break DNSSEC validation in +Unbound. Default is no. .TP .B serve\-expired: \fI From 6d0812b56731af130e8bc7e1572388934beb9b3b Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 9 Oct 2023 12:21:22 +0200 Subject: [PATCH 37/64] - Fix edns subnet so that queries with a source prefix of zero cause the recursor send no edns subnet option to the upstream. --- doc/Changelog | 4 + edns-subnet/subnetmod.c | 30 ++++++- edns-subnet/subnetmod.h | 7 ++ testdata/subnet_prezero.crpl | 155 +++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 testdata/subnet_prezero.crpl diff --git a/doc/Changelog b/doc/Changelog index f18f97374..62ce262d2 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +9 October 2023: Wouter + - Fix edns subnet so that queries with a source prefix of zero cause + the recursor send no edns subnet option to the upstream. + 4 October 2023: Wouter - Fix #946: Forwarder returns servfail on upstream response noerror no data. diff --git a/edns-subnet/subnetmod.c b/edns-subnet/subnetmod.c index 13fd669b5..cefde84e5 100644 --- a/edns-subnet/subnetmod.c +++ b/edns-subnet/subnetmod.c @@ -156,6 +156,7 @@ int ecs_whitelist_check(struct query_info* qinfo, qstate->no_cache_store = 0; } + sq->subnet_sent_no_subnet = 0; if(sq->ecs_server_out.subnet_validdata && ((sq->subnet_downstream && qstate->env->cfg->client_subnet_always_forward) || ecs_is_whitelisted(sn_env->whitelist, @@ -166,6 +167,14 @@ int ecs_whitelist_check(struct query_info* qinfo, * set. */ if(!edns_opt_list_find(qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode)) { + /* if the client is not wanting an EDNS subnet option, + * omit it and store that we omitted it but actually + * are doing EDNS subnet to the server. */ + if(sq->ecs_server_out.subnet_source_mask == 0) { + sq->subnet_sent_no_subnet = 1; + sq->subnet_sent = 0; + return 1; + } subnet_ecs_opt_list_append(&sq->ecs_server_out, &qstate->edns_opts_back_out, qstate, region); } @@ -515,7 +524,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) } /* We have not asked for subnet data */ - if (!sq->subnet_sent) { + if (!sq->subnet_sent && !sq->subnet_sent_no_subnet) { if (s_in->subnet_validdata) verbose(VERB_QUERY, "subnetcache: received spurious data"); if (sq->subnet_downstream) /* Copy back to client */ @@ -524,7 +533,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) } /* subnet sent but nothing came back */ - if (!s_in->subnet_validdata) { + if (!s_in->subnet_validdata && !sq->subnet_sent_no_subnet) { /* The authority indicated no support for edns subnet. As a * consequence the answer ended up in the regular cache. It * is still useful to put it in the edns subnet cache for @@ -540,6 +549,18 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) return module_finished; } + /* Purposefully there was no sent subnet, and there is consequently + * no subnet in the answer. If there was, use the subnet in the answer + * anyway. But if there is not, treat it as a prefix 0 answer. */ + if(sq->subnet_sent_no_subnet && !s_in->subnet_validdata) { + /* Fill in 0.0.0.0/0 scope 0, or ::0/0 scope 0, for caching. */ + s_in->subnet_addr_fam = s_out->subnet_addr_fam; + s_in->subnet_source_mask = 0; + s_in->subnet_scope_mask = 0; + memset(s_in->subnet_addr, 0, INET6_SIZE); + s_in->subnet_validdata = 1; + } + /* Being here means we have asked for and got a subnet specific * answer. Also, the answer from the authority is not yet cached * anywhere. */ @@ -556,6 +577,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq) (void)edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; + sq->subnet_sent_no_subnet = 0; return module_restart_next; } @@ -676,6 +698,7 @@ ecs_query_response(struct module_qstate* qstate, struct dns_msg* response, edns_opt_list_remove(&qstate->edns_opts_back_out, qstate->env->cfg->client_subnet_opcode); sq->subnet_sent = 0; + sq->subnet_sent_no_subnet = 0; memset(&sq->ecs_server_out, 0, sizeof(sq->ecs_server_out)); } else if (!sq->track_max_scope && FLAGS_GET_RCODE(response->rep->flags) == LDNS_RCODE_NOERROR && @@ -737,6 +760,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id, sq->ecs_server_in.subnet_scope_mask > sq->max_scope)) sq->max_scope = sq->ecs_server_in.subnet_scope_mask; + } else if(sq->subnet_sent_no_subnet) { + /* The answer can be stored as scope 0, not in global cache. */ + qstate->no_cache_store = 1; } return 1; diff --git a/edns-subnet/subnetmod.h b/edns-subnet/subnetmod.h index f0bcaad33..1ff8a23ec 100644 --- a/edns-subnet/subnetmod.h +++ b/edns-subnet/subnetmod.h @@ -85,6 +85,13 @@ struct subnet_qstate { struct ecs_data ecs_server_out; int subnet_downstream; int subnet_sent; + /** + * If there was no subnet sent because the client used source prefix + * length 0 for omitting the information. Then the answer is cached + * like subnet was a /0 scope. Like the subnet_sent flag, but when + * the EDNS subnet option is omitted because the client asked. + */ + int subnet_sent_no_subnet; /** keep track of longest received scope, set after receiving CNAME for * incoming QNAME. */ int track_max_scope; diff --git a/testdata/subnet_prezero.crpl b/testdata/subnet_prezero.crpl new file mode 100644 index 000000000..22cdfffb0 --- /dev/null +++ b/testdata/subnet_prezero.crpl @@ -0,0 +1,155 @@ +; subnet unit test +server: + trust-anchor-signaling: no + send-client-subnet: 1.2.3.4 + send-client-subnet: 1.2.3.5 + target-fetch-policy: "0 0 0 0 0" + module-config: "subnetcache validator iterator" + qname-minimisation: no + minimal-responses: no + +stub-zone: + name: "example.com" + stub-addr: 1.2.3.4 +CONFIG_END + +SCENARIO_BEGIN Test subnetcache source prefix zero from client. +; In RFC7871 section-7.1.2 (para. 2). +; It says that the recursor must send no EDNS subnet or its own address +; in the EDNS subnet to the upstream server. And use that answer for the +; source prefix length zero query. That type of query is for privacy. +; The authority server is then going to use the resolver's IP, if any, to +; tailor the answer to the query source address. + +; ns.example.com +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.4 + +; reply with 0.0.0.0/0 in reply +; For the test the answers for 0.0.0.0/0 queries are SERVFAIL, the normal +; answers are NOERROR. +ENTRY_BEGIN +MATCH opcode qtype qname ednsdata +ADJUST copy_id +REPLY QR AA DO SERVFAIL +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; reply without subnet +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +ENTRY_END + +; delegation answer for c10r.example.com, with subnet /0 +ENTRY_BEGIN +MATCH opcode subdomain ednsdata +ADJUST copy_id copy_query +REPLY QR DO SERVFAIL +SECTION QUESTION +c10r.example.com. IN NS +SECTION AUTHORITY +c10r.example.com. IN NS ns.c10r.example.com. +SECTION ADDITIONAL +ns.c10r.example.com. IN A 1.2.3.5 +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; delegation answer for c10r.example.com, without subnet +ENTRY_BEGIN +MATCH opcode subdomain +ADJUST copy_id copy_query +REPLY QR DO NOERROR +SECTION QUESTION +c10r.example.com. IN NS +SECTION AUTHORITY +c10r.example.com. IN NS ns.c10r.example.com. +SECTION ADDITIONAL +ns.c10r.example.com. IN A 1.2.3.5 +ENTRY_END +RANGE_END + +; ns.c10r.example.com +RANGE_BEGIN 0 100 + ADDRESS 1.2.3.5 + +; reply with 0.0.0.0/0 in reply +ENTRY_BEGIN +MATCH opcode qtype qname ednsdata +ADJUST copy_id +REPLY QR AA DO SERVFAIL +SECTION QUESTION +star.c10r.example.com. IN A +SECTION ANSWER +star.c10r.example.com. IN A 1.2.3.6 +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +; reply without subnet +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA DO NOERROR +SECTION QUESTION +star.c10r.example.com. IN A +SECTION ANSWER +star.c10r.example.com. IN A 1.2.3.6 +ENTRY_END +RANGE_END + +; ask for www.example.com +; server answers with CNAME to a delegation, that then +; returns a /24 answer. +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +www.example.com. IN A +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END + +STEP 10 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ednsdata +REPLY QR RD RA DO NOERROR +SECTION QUESTION +www.example.com. IN A +SECTION ANSWER +www.example.com. IN CNAME star.c10r.example.com. +star.c10r.example.com. IN A 1.2.3.6 +SECTION ADDITIONAL +HEX_EDNSDATA_BEGIN + 00 08 00 04 ; OPCODE=subnet, optlen + 00 01 00 00 ; ip4, scope 0, source 0 + ; 0.0.0.0/0 +HEX_EDNSDATA_END +ENTRY_END +SCENARIO_END From c09320c651290bbb2a5fd20a33feeabfc5c0eef1 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Mon, 9 Oct 2023 12:36:54 +0200 Subject: [PATCH 38/64] - Fix that printout of EDNS options shows the EDNS cookie option by name. --- doc/Changelog | 2 ++ sldns/wire2str.c | 1 + 2 files changed, 3 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 62ce262d2..62b101dd2 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,6 +1,8 @@ 9 October 2023: Wouter - Fix edns subnet so that queries with a source prefix of zero cause the recursor send no edns subnet option to the upstream. + - Fix that printout of EDNS options shows the EDNS cookie option by + name. 4 October 2023: Wouter - Fix #946: Forwarder returns servfail on upstream response noerror no diff --git a/sldns/wire2str.c b/sldns/wire2str.c index ace478d73..2b5dc0513 100644 --- a/sldns/wire2str.c +++ b/sldns/wire2str.c @@ -192,6 +192,7 @@ static sldns_lookup_table sldns_edns_options_data[] = { { 6, "DHU" }, { 7, "N3U" }, { 8, "edns-client-subnet" }, + { 10, "COOKIE" }, { 11, "edns-tcp-keepalive"}, { 12, "Padding" }, { 15, "EDE"}, From 516f90abdb0f27d46c2bf7239c196ad08621df29 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Tue, 10 Oct 2023 15:17:48 +0200 Subject: [PATCH 39/64] - Fix infinite loop when reading multiple lines of input on a broken remote control socket. Addesses #947 and #948. --- daemon/remote.c | 2 +- doc/Changelog | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 4990fc8e9..0460d5308 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -590,7 +590,7 @@ ssl_read_line(RES* res, char* buf, size_t max) while(1) { ssize_t rr = recv(res->fd, buf+len, 1, 0); if(rr <= 0) { - if(rr == 0) { + if(rr == 0 && len != 0) { buf[len] = 0; return 1; } diff --git a/doc/Changelog b/doc/Changelog index 62b101dd2..a943cce57 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +10 October 2023: George + - Fix infinite loop when reading multiple lines of input on a broken + remote control socket. Addesses #947 and #948. + 9 October 2023: Wouter - Fix edns subnet so that queries with a source prefix of zero cause the recursor send no edns subnet option to the upstream. From e98b89651e2b87f8bbf8aa061133c6748ecb02a3 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Wed, 11 Oct 2023 11:44:55 +0200 Subject: [PATCH 40/64] - Fix #850: [FR] Ability to use specific database in Redis, with new redis-logical-db configuration option. --- cachedb/redis.c | 68 ++++++++++++++++++++++++++++++------------- doc/Changelog | 4 +++ doc/example.conf.in | 2 ++ doc/unbound.conf.5.in | 11 +++++++ util/config_file.c | 2 ++ util/config_file.h | 2 ++ util/configlexer.lex | 1 + util/configparser.y | 19 +++++++++++- 8 files changed, 87 insertions(+), 22 deletions(-) diff --git a/cachedb/redis.c b/cachedb/redis.c index 93a575a4c..6cc975901 100644 --- a/cachedb/redis.c +++ b/cachedb/redis.c @@ -59,11 +59,28 @@ struct redis_moddata { const char* server_path; /* server's unix path, or "", NULL if unused */ const char* server_password; /* server's AUTH password, or "", NULL if unused */ struct timeval timeout; /* timeout for connection setup and commands */ + int logical_db; /* the redis logical database to use */ }; static redisReply* redis_command(struct module_env*, struct cachedb_env*, const char*, const uint8_t*, size_t); +static void +moddata_clean(struct redis_moddata** moddata) { + if(!moddata || !*moddata) + return; + if((*moddata)->ctxs) { + int i; + for(i = 0; i < (*moddata)->numctxs; i++) { + if((*moddata)->ctxs[i]) + redisFree((*moddata)->ctxs[i]); + } + free((*moddata)->ctxs); + } + free(*moddata); + *moddata = NULL; +} + static redisContext* redis_connect(const struct redis_moddata* moddata) { @@ -97,10 +114,21 @@ redis_connect(const struct redis_moddata* moddata) } freeReplyObject(rep); } + if(moddata->logical_db > 0) { + redisReply* rep; + rep = redisCommand(ctx, "SELECT %d", moddata->logical_db); + if(!rep || rep->type == REDIS_REPLY_ERROR) { + log_err("failed to set logical database (%d)", + moddata->logical_db); + freeReplyObject(rep); + goto fail; + } + freeReplyObject(rep); + } verbose(VERB_OPS, "Connection to Redis established"); return ctx; - fail: +fail: if(ctx) redisFree(ctx); return NULL; @@ -117,14 +145,13 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) moddata = calloc(1, sizeof(struct redis_moddata)); if(!moddata) { log_err("out of memory"); - return 0; + goto fail; } moddata->numctxs = env->cfg->num_threads; moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*)); if(!moddata->ctxs) { log_err("out of memory"); - free(moddata); - return 0; + goto fail; } /* note: server_host is a shallow reference to configured string. * we don't have to free it in this module. */ @@ -134,8 +161,15 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) moddata->server_password = env->cfg->redis_server_password; moddata->timeout.tv_sec = env->cfg->redis_timeout / 1000; moddata->timeout.tv_usec = (env->cfg->redis_timeout % 1000) * 1000; - for(i = 0; i < moddata->numctxs; i++) - moddata->ctxs[i] = redis_connect(moddata); + moddata->logical_db = env->cfg->redis_logical_db; + for(i = 0; i < moddata->numctxs; i++) { + redisContext* ctx = redis_connect(moddata); + if(!ctx) { + log_err("redis_init: failed to init redis"); + goto fail; + } + moddata->ctxs[i] = ctx; + } cachedb_env->backend_data = moddata; if(env->cfg->redis_expire_records) { redisReply* rep = NULL; @@ -148,7 +182,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) log_err("redis_init: failed to init redis, the " "redis-expire-records option requires the SETEX command " "(redis >= 2.0.0)"); - return 0; + goto fail; } redis_reply_type = rep->type; freeReplyObject(rep); @@ -160,11 +194,14 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env) log_err("redis_init: failed to init redis, the " "redis-expire-records option requires the SETEX command " "(redis >= 2.0.0)"); - return 0; + goto fail; } } - return 1; + +fail: + moddata_clean(&moddata); + return 0; } static void @@ -175,18 +212,7 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env) (void)env; verbose(VERB_OPS, "Redis deinitialization"); - - if(!moddata) - return; - if(moddata->ctxs) { - int i; - for(i = 0; i < moddata->numctxs; i++) { - if(moddata->ctxs[i]) - redisFree(moddata->ctxs[i]); - } - free(moddata->ctxs); - } - free(moddata); + moddata_clean(&moddata); } /* diff --git a/doc/Changelog b/doc/Changelog index a943cce57..40da8e55c 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +11 October 2023: George + - Fix #850: [FR] Ability to use specific database in Redis, with new + redis-logical-db configuration option. + 10 October 2023: George - Fix infinite loop when reading multiple lines of input on a broken remote control socket. Addesses #947 and #948. diff --git a/doc/example.conf.in b/doc/example.conf.in index 0980212e1..ca8f929e4 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1236,6 +1236,8 @@ remote-control: # redis-timeout: 100 # # set timeout on redis records based on DNS response TTL # redis-expire-records: no +# # redis logical database to use, 0 is the default database. +# redis-logical-db: 0 # IPSet # Add specify domain into set via ipset. diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 84b903f49..2992e0a4a 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2707,6 +2707,17 @@ Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0, this option is internally reverted to "no". Redis SETEX support is required for this option (Redis >= 2.0.0). This option defaults to no. +.TP +.B redis-logical-db: \fI +The logical database in Redis to use. +These are databases in the same Redis instance sharing the same configuration +and persisted in the same RDB/AOF file. +If unsure about using this option, Redis documentation +(https://redis.io/commands/select/) suggests not to use a single Redis instance +for multiple unrelated applications. +The default database in Redis is 0 while other logical databases need to be +explicitly SELECT'ed upon connecting. +This option defaults to 0. .SS DNSTAP Logging Options DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled in the \fBdnstap:\fR section. diff --git a/util/config_file.c b/util/config_file.c index 454096342..80003e632 100644 --- a/util/config_file.c +++ b/util/config_file.c @@ -388,6 +388,7 @@ config_create(void) cfg->redis_timeout = 100; cfg->redis_server_port = 6379; cfg->redis_expire_records = 0; + cfg->redis_logical_db = 0; #endif /* USE_REDIS */ #endif /* USE_CACHEDB */ #ifdef USE_IPSET @@ -1313,6 +1314,7 @@ config_get_option(struct config_file* cfg, const char* opt, else O_STR(opt, "redis-server-password", redis_server_password) else O_DEC(opt, "redis-timeout", redis_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 diff --git a/util/config_file.h b/util/config_file.h index 452f3c6a7..588012f82 100644 --- a/util/config_file.h +++ b/util/config_file.h @@ -712,6 +712,8 @@ struct config_file { int redis_timeout; /** set timeout on redis records based on DNS response ttl */ int redis_expire_records; + /** set the redis logical database upon connection */ + int redis_logical_db; #endif #endif /** Downstream DNS Cookies */ diff --git a/util/configlexer.lex b/util/configlexer.lex index 3fcdfa62e..142810a2f 100644 --- a/util/configlexer.lex +++ b/util/configlexer.lex @@ -563,6 +563,7 @@ 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-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) } diff --git a/util/configparser.y b/util/configparser.y index d8f25a67e..6237d0c08 100644 --- a/util/configparser.y +++ b/util/configparser.y @@ -179,6 +179,7 @@ extern struct config_parser_state* cfg_parser; %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED %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_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 @@ -3701,7 +3702,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 + redis_expire_records | redis_server_path | redis_server_password | + redis_logical_db ; cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG { @@ -3804,6 +3806,21 @@ redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG free($2); } ; +redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG + { + #if defined(USE_CACHEDB) && defined(USE_REDIS) + int db; + OUTYY(("P(redis_logical_db:%s)\n", $2)); + db = atoi($2); + if((db == 0 && strcmp($2, "0") != 0) || db < 0) + yyerror("valid redis logical database index expected"); + else cfg_parser->cfg->redis_logical_db = db; + #else + OUTYY(("P(Compiled without cachedb or redis, ignoring)\n")); + #endif + free($2); + } + ; server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG { OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3)); From d5954aff084f19ec239ffca91415ec3d87f28c27 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 11 Oct 2023 11:59:26 +0200 Subject: [PATCH 41/64] - Fix #949: "could not create control compt". --- doc/Changelog | 3 +++ libunbound/python/libunbound.i | 3 +++ 2 files changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 40da8e55c..d1e3b6846 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,9 @@ - Fix #850: [FR] Ability to use specific database in Redis, with new redis-logical-db configuration option. +11 October 2023: Wouter + - Fix #949: "could not create control compt". + 10 October 2023: George - Fix infinite loop when reading multiple lines of input on a broken remote control socket. Addesses #947 and #948. diff --git a/libunbound/python/libunbound.i b/libunbound/python/libunbound.i index 0cdb3d7e5..dc125146c 100644 --- a/libunbound/python/libunbound.i +++ b/libunbound/python/libunbound.i @@ -863,6 +863,9 @@ Result: ['74.125.43.147', '74.125.43.99', '74.125.43.103', '74.125.43.104'] %inline %{ //SWIG will see the ub_ctx as a class struct ub_ctx { + /* Dummy member, so the struct is not empty, MSVC complains about + * that. */ + int dummy; }; %} From 935bc162e13345069484538f32253ec6d8f1ff2f Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 11 Oct 2023 12:01:55 +0200 Subject: [PATCH 42/64] - cachedb-no-store, unit test cachedb_no_store.tdir. --- .../cachedb_no_store.conf | 29 ++++ .../cachedb_no_store.dsc | 16 +++ .../cachedb_no_store.post | 21 +++ .../cachedb_no_store.pre | 36 +++++ .../cachedb_no_store.servfail.testns | 8 ++ .../cachedb_no_store.test | 133 ++++++++++++++++++ .../cachedb_no_store.testns | 9 ++ 7 files changed, 252 insertions(+) create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.conf create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.dsc create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.post create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.pre create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.test create mode 100644 testdata/cachedb_no_store.tdir/cachedb_no_store.testns diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.conf b/testdata/cachedb_no_store.tdir/cachedb_no_store.conf new file mode 100644 index 000000000..ff76cc379 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.conf @@ -0,0 +1,29 @@ +server: + verbosity: 4 + interface: 127.0.0.1 + port: @PORT@ + use-syslog: no + directory: "" + pidfile: "unbound.pid" + chroot: "" + username: "" + module-config: "cachedb iterator" + do-not-query-localhost: no + qname-minimisation: no + +forward-zone: + name: "." + forward-addr: 127.0.0.1@@TOPORT@ + +stub-zone: + name: "example.com" + stub-addr: 127.0.0.1@@TOPORT@ + +remote-control: + control-enable: yes + control-interface: @CONTROL_PATH@/controlpipe.@CONTROL_PID@ + control-use-cert: no + +cachedb: + backend: "testframe" + secret-seed: "testvalue" diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc b/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc new file mode 100644 index 000000000..9d267436e --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.dsc @@ -0,0 +1,16 @@ +BaseName: cachedb_no_store +Version: 1.0 +Description: cachedb test the cachedb-no-store option +CreationDate: Wed 11 Oct 11:00:00 CEST 2023 +Maintainer: dr. W.C.A. Wijngaards +Category: +Component: +CmdDepends: +Depends: +Help: +Pre: cachedb_no_store.pre +Post: cachedb_no_store.post +Test: cachedb_no_store.test +AuxFiles: +Passed: +Failure: diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.post b/testdata/cachedb_no_store.tdir/cachedb_no_store.post new file mode 100644 index 000000000..27fe0961e --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.post @@ -0,0 +1,21 @@ +# #-- cachedb_no_store.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 +PRE="../.." +. ../common.sh +if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi + +echo "> cat logfiles" +cat fwd.log +if test -f fwd2.log; then cat fwd2.log; else echo "no fwd2.log"; fi +if test -f fwd3.log; then cat fwd3.log; else echo "no fwd3.log"; fi +if test -f fwd4.log; then cat fwd4.log; else echo "no fwd4.log"; fi +cat unbound.log +if test -f unbound2.log; then cat unbound2.log; else echo "no unbound2.log"; fi +kill_pid $FWD_PID +kill_pid `cat unbound.pid` +rm -f $CONTROL_PATH/controlpipe.$CONTROL_PID diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.pre b/testdata/cachedb_no_store.tdir/cachedb_no_store.pre new file mode 100644 index 000000000..e59d3b8da --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.pre @@ -0,0 +1,36 @@ +# #-- cachedb_no_store.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 + +PRE="../.." +. ../common.sh +if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; 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 cachedb_no_store.testns >fwd.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test + +# make config file +CONTROL_PATH=/tmp +CONTROL_PID=$$ +sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@TOPORT\@/'$FWD_PORT'/' -e 's?@CONTROL_PATH\@?'$CONTROL_PATH'?' -e 's/@CONTROL_PID@/'$CONTROL_PID'/' < cachedb_no_store.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 +echo "CONTROL_PATH=$CONTROL_PATH" >> .tpkg.var.test +echo "CONTROL_PID=$CONTROL_PID" >> .tpkg.var.test + +cat .tpkg.var.test +wait_ldns_testns_up fwd.log +wait_unbound_up unbound.log diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns b/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns new file mode 100644 index 000000000..b41abb0ff --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.servfail.testns @@ -0,0 +1,8 @@ +ENTRY_BEGIN +MATCH opcode +ADJUST copy_id copy_query +REPLY QR AA SERVFAIL +SECTION QUESTION +txt1.example.com. IN TXT +SECTION ANSWER +ENTRY_END diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.test b/testdata/cachedb_no_store.tdir/cachedb_no_store.test new file mode 100644 index 000000000..b1b543bdb --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.test @@ -0,0 +1,133 @@ +# #-- cachedb_no_store.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 + +PRE="../.." +. ../common.sh +if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi + +# do the test +get_ldns_testns + +# query for a text record that is stored by unbound's cache and cachedb +# in the testframe cache. +echo "> dig txt1.example.com." +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# stop the forwarder with servfail, to check the answer came from the cache +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns with servfails" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd2.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd2.log + +echo "> dig txt1.example.com. from unbound cache" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# clear the cache of unbound, but not cachedb testframe cache +echo "> unbound-control flush" +$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "> dig txt1.example.com. from cachedb" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# start the forwarder again. +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.testns >fwd3.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd3.log + +# stop unbound to flush the cachedb cache +echo "> stop unbound" +kill_pid `cat unbound.pid` + +echo "" +echo "> config unbound with cachedb-no-store: yes" +echo "cachedb: cachedb-no-store: yes" >> ub.conf + +# start unbound again. +echo "> start unbound" +$PRE/unbound -d -c ub.conf >unbound2.log 2>&1 & +UNBOUND_PID=$! +echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test +wait_unbound_up unbound2.log + +echo "" +echo "> dig txt1.example.com." +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# stop the forwarder with servfail, to check the answer came from the cache +echo "> stop ldns-testns" +kill_pid $FWD_PID +echo "> start ldns-testns with servfails" +$LDNS_TESTNS -p $FWD_PORT cachedb_no_store.servfail.testns >fwd4.log 2>&1 & +FWD_PID=$! +echo "FWD_PID=$FWD_PID" >> .tpkg.var.test +wait_ldns_testns_up fwd4.log + +echo "> dig txt1.example.com. from unbound cache" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "example text message" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +# clear the cache of unbound, but not cachedb testframe cache +echo "> unbound-control flush" +$PRE/unbound-control -c ub.conf flush_type txt1.example.com. TXT +if test $? -ne 0; then + echo "wrong exit value." + exit 1 +else + echo "exit value: OK" +fi + +echo "> dig txt1.example.com. from cachedb, but that has no message stored" +dig @localhost -p $UNBOUND_PORT txt1.example.com. TXT | tee outfile +if grep "SERVFAIL" outfile; then + echo "OK" +else + echo "Not OK" + exit 1 +fi + +exit 0 diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.testns b/testdata/cachedb_no_store.tdir/cachedb_no_store.testns new file mode 100644 index 000000000..282b224f8 --- /dev/null +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.testns @@ -0,0 +1,9 @@ +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR AA NOERROR +SECTION QUESTION +txt1.example.com. IN TXT +SECTION ANSWER +txt1.example.com. IN TXT "example text message" +ENTRY_END From f2528dc3ac5b8b09384eb02f9ef17c34096a8d9a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 11 Oct 2023 13:29:56 +0200 Subject: [PATCH 43/64] - Fix that cachedb does not warn when serve-expired is disabled about use of serve-expired-reply-ttl and serve-expired-client-timeout. --- cachedb/cachedb.c | 6 +++--- doc/Changelog | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cachedb/cachedb.c b/cachedb/cachedb.c index 30645268c..573017eef 100644 --- a/cachedb/cachedb.c +++ b/cachedb/cachedb.c @@ -265,11 +265,11 @@ cachedb_init(struct module_env* env, int id) return 0; } cachedb_env->enabled = 1; - if(env->cfg->serve_expired_reply_ttl) + if(env->cfg->serve_expired && env->cfg->serve_expired_reply_ttl) log_warn( "cachedb: serve-expired-reply-ttl is set but not working for data " - "originating from the external cache; 0 TLL is used for those."); - if(env->cfg->serve_expired_client_timeout) + "originating from the external cache; 0 TTL is used for those."); + if(env->cfg->serve_expired && env->cfg->serve_expired_client_timeout) log_warn( "cachedb: serve-expired-client-timeout is set but not working for " "data originating from the external cache; expired data are used " diff --git a/doc/Changelog b/doc/Changelog index d1e3b6846..60e61d7b9 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,8 @@ 11 October 2023: Wouter - Fix #949: "could not create control compt". + - Fix that cachedb does not warn when serve-expired is disabled about + use of serve-expired-reply-ttl and serve-expired-client-timeout. 10 October 2023: George - Fix infinite loop when reading multiple lines of input on a broken From 67153f897e40f47bf2fb67fa8f6febb4b843768d Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Wed, 11 Oct 2023 13:47:28 +0200 Subject: [PATCH 44/64] - Fix for #949: Fix pythonmod/ubmodule-tst.py for Python 3.x. --- doc/Changelog | 1 + pythonmod/ubmodule-tst.py | 44 +++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 60e61d7b9..07bf0c703 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -6,6 +6,7 @@ - Fix #949: "could not create control compt". - Fix that cachedb does not warn when serve-expired is disabled about use of serve-expired-reply-ttl and serve-expired-client-timeout. + - Fix for #949: Fix pythonmod/ubmodule-tst.py for Python 3.x. 10 October 2023: George - Fix infinite loop when reading multiple lines of input on a broken diff --git a/pythonmod/ubmodule-tst.py b/pythonmod/ubmodule-tst.py index 0b9b5a9d2..70729071c 100644 --- a/pythonmod/ubmodule-tst.py +++ b/pythonmod/ubmodule-tst.py @@ -33,7 +33,12 @@ POSSIBILITY OF SUCH DAMAGE. ''' def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + scripts=[] + s = cfg.python_script + while s != None: + scripts.append(s.str) + s = s.next + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, scripts)) return True def deinit(id): @@ -55,12 +60,15 @@ def setTTL(qstate, ttl): def dataHex(data, prefix=""): res = "" - for i in range(0, (len(data)+15)/16): + for i in range(0, int((len(data)+15)/16)): res += "%s0x%02X | " % (prefix, i*16) - d = map(lambda x:ord(x), data[i*16:i*16+17]) + if type(data[0]) == type(1): + d = map(lambda x:int(x), data[i*16:i*16+17]) + else: + d = map(lambda x:ord(x), data[i*16:i*16+17]) for ch in d: res += "%02X " % ch - for i in range(0,17-len(d)): + for i in range(0,17-len(data[i*16:i*16+17])): res += " " res += "| " for ch in d: @@ -72,31 +80,31 @@ def dataHex(data, prefix=""): return res def printReturnMsg(qstate): - print "Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl) - print " qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str + print("Return MSG rep :: flags: %04X, QDcount: %d, Security:%d, TTL=%d" % (qstate.return_msg.rep.flags, qstate.return_msg.rep.qdcount,qstate.return_msg.rep.security, qstate.return_msg.rep.ttl)) + print(" qinfo :: qname:",qstate.return_msg.qinfo.qname_list, qstate.return_msg.qinfo.qname_str, "type:",qstate.return_msg.qinfo.qtype_str, "class:",qstate.return_msg.qinfo.qclass_str) if (qstate.return_msg.rep): - print "RRSets:",qstate.return_msg.rep.rrset_count + print("RRSets:",qstate.return_msg.rep.rrset_count) prevkey = None for i in range(0,qstate.return_msg.rep.rrset_count): r = qstate.return_msg.rep.rrsets[i] rk = r.rk - print i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags, - print "type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class) + print(i,":",rk.dname_list, rk.dname_str, "flags: %04X" % rk.flags) + print("type:",rk.type_str,"(%d)" % ntohs(rk.type), "class:",rk.rrset_class_str,"(%d)" % ntohs(rk.rrset_class)) d = r.entry.data - print " RRDatas:",d.count+d.rrsig_count + print(" RRDatas:",d.count+d.rrsig_count) for j in range(0,d.count+d.rrsig_count): - print " ",j,":","TTL=",d.rr_ttl[j],"RR data:" - print dataHex(d.rr_data[j]," ") + print(" ",j,":","TTL=",d.rr_ttl[j],"RR data:") + print(dataHex(d.rr_data[j]," ")) def operate(id, event, qstate, qdata): log_info("pythonmod: operate called, id: %d, event:%s" % (id, strmodulevent(event))) - #print "pythonmod: per query data", qdata + #print("pythonmod: per query data", qdata) - print "Query:", ''.join(map(lambda x:chr(max(32,ord(x))),qstate.qinfo.qname)), qstate.qinfo.qname_list, - print "Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype, - print "Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass - print + print("Query:", qstate.qinfo.qname, qstate.qinfo.qname_list, qstate.qinfo.qname_str) + print("Type:",qstate.qinfo.qtype_str,"(%d)" % qstate.qinfo.qtype) + print("Class:",qstate.qinfo.qclass_str,"(%d)" % qstate.qinfo.qclass) + print("") # TEST: # > dig @127.0.0.1 www.seznam.cz A @@ -118,7 +126,7 @@ def operate(id, event, qstate, qdata): invalidateQueryInCache(qstate, qstate.return_msg.qinfo) if (qstate.return_msg.rep.authoritative): - print "X"*300 + print("X"*300) setTTL(qstate, 10) #do cache nastavime TTL na 10 if not storeQueryInCache(qstate, qstate.return_msg.qinfo, qstate.return_msg.rep, 0): From 908e1cb11a8e0362eb32d2de4aebdc42fa46593a Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 12 Oct 2023 14:05:31 +0200 Subject: [PATCH 45/64] Changelog note for #944. - Merge #944: Disable EDNS DO. Disable the EDNS DO flag in upstream requests. This can be helpful for devices that cannot handle DNSSEC information. But it should not be enabled otherwise, because that would stop DNSSEC validation. The DNSSEC validation would not work for Unbound itself, and also not for downstream users. Default is no. The option is disable-edns-do: no --- doc/Changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 07bf0c703..14fe19acd 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,12 @@ +12 October 2023: Wouter + - Merge #944: Disable EDNS DO. + Disable the EDNS DO flag in upstream requests. This can be helpful + for devices that cannot handle DNSSEC information. But it should not + be enabled otherwise, because that would stop DNSSEC validation. The + DNSSEC validation would not work for Unbound itself, and also not + for downstream users. Default is no. The option + is disable-edns-do: no + 11 October 2023: George - Fix #850: [FR] Ability to use specific database in Redis, with new redis-logical-db configuration option. From 4a211a9117bbc9f32d9a5395ef0389878532b4aa Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 13 Oct 2023 11:37:18 +0200 Subject: [PATCH 46/64] - cachedb-no-store, example conf and man page documentation. --- doc/example.conf.in | 2 ++ doc/unbound.conf.5.in | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/doc/example.conf.in b/doc/example.conf.in index a0035b740..6bf1c668e 100644 --- a/doc/example.conf.in +++ b/doc/example.conf.in @@ -1226,6 +1226,8 @@ remote-control: # backend: "testframe" # # secret seed string to calculate hashed keys # secret-seed: "default" +# # if the backend should be read from, but not written to. +# cachedb-no-store: no # # # For "redis" backend: # # (to enable, use --with-libhiredis to configure before compiling) diff --git a/doc/unbound.conf.5.in b/doc/unbound.conf.5.in index 13aa9ec7c..76cfa2383 100644 --- a/doc/unbound.conf.5.in +++ b/doc/unbound.conf.5.in @@ -2681,6 +2681,11 @@ operationally. If the backend database is shared by multiple Unbound instances, all instances must use the same secret seed. This option defaults to "default". +.TP +.B cachedb-no-store: \fI\fR +If the backend should be read from, but not written to. This makes this +instance not store dns messages in the backend. But if data is available it +is retrieved. The default is no. .P The following .B cachedb From dbd2a43ab15192f6aa7b14ddf80e8b4f978dd88e Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Fri, 13 Oct 2023 13:46:52 +0200 Subject: [PATCH 47/64] Apply suggestions from code review Co-authored-by: Yorgos Thessalonikefs --- testdata/cachedb_no_store.tdir/cachedb_no_store.post | 1 - testdata/cachedb_no_store.tdir/cachedb_no_store.test | 1 - 2 files changed, 2 deletions(-) diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.post b/testdata/cachedb_no_store.tdir/cachedb_no_store.post index 27fe0961e..901f01a87 100644 --- a/testdata/cachedb_no_store.tdir/cachedb_no_store.post +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.post @@ -7,7 +7,6 @@ # do your teardown here PRE="../.." . ../common.sh -if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi echo "> cat logfiles" cat fwd.log diff --git a/testdata/cachedb_no_store.tdir/cachedb_no_store.test b/testdata/cachedb_no_store.tdir/cachedb_no_store.test index b1b543bdb..352026844 100644 --- a/testdata/cachedb_no_store.tdir/cachedb_no_store.test +++ b/testdata/cachedb_no_store.tdir/cachedb_no_store.test @@ -6,7 +6,6 @@ PRE="../.." . ../common.sh -if grep "define USE_CACHEDB 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi # do the test get_ldns_testns From 07149f576a223431b6d831798cfbe40910931afc Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Fri, 13 Oct 2023 14:58:16 +0200 Subject: [PATCH 48/64] - Better fix for infinite loop when reading multiple lines of input on a broken remote control socket, by treating a zero byte line the same as transmission end. Addesses #947 and #948. --- daemon/remote.c | 20 ++++++++++---------- doc/Changelog | 5 +++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 0460d5308..235f96c7f 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -590,13 +590,13 @@ ssl_read_line(RES* res, char* buf, size_t max) while(1) { ssize_t rr = recv(res->fd, buf+len, 1, 0); if(rr <= 0) { - if(rr == 0 && len != 0) { + if(rr == 0) { buf[len] = 0; return 1; } if(errno == EINTR || errno == EAGAIN) continue; - log_err("could not recv: %s", + if(rr < 0) log_err("could not recv: %s", sock_strerror(errno)); return 0; } @@ -1223,8 +1223,8 @@ do_zones_add(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_zone_add(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; @@ -1272,8 +1272,8 @@ do_zones_remove(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_zone_remove(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; @@ -1336,8 +1336,8 @@ do_datas_add(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0, line = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ line++; if(perform_data_add(ssl, zones, buf, line)) num++; @@ -1376,8 +1376,8 @@ do_datas_remove(RES* ssl, struct local_zones* zones) char buf[2048]; int num = 0; while(ssl_read_line(ssl, buf, sizeof(buf))) { - if(buf[0] == 0x04 && buf[1] == 0) - break; /* end of transmission */ + if(buf[0] == 0 || (buf[0] == 0x04 && buf[1] == 0)) + break; /* zero byte line or end of transmission */ if(!perform_data_remove(ssl, zones, buf)) { if(!ssl_printf(ssl, "error for input line: %s\n", buf)) return; diff --git a/doc/Changelog b/doc/Changelog index 14fe19acd..eb2dae2ad 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +13 October 2023: George + - Better fix for infinite loop when reading multiple lines of input on + a broken remote control socket, by treating a zero byte line the + same as transmission end. Addesses #947 and #948. + 12 October 2023: Wouter - Merge #944: Disable EDNS DO. Disable the EDNS DO flag in upstream requests. This can be helpful From 63a5280f8f56b71ac73b3df50bf2e393626c0b20 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 16 Oct 2023 15:47:18 +0200 Subject: [PATCH 49/64] - Expose the script filename in the Python module environment 'mod_env' instead of the config_file structure which includes the linked list of scripts in a multi Python module setup; fixes #79. --- doc/Changelog | 5 +++++ pythonmod/doc/examples/example0-1.py | 4 ++-- pythonmod/doc/examples/example0.rst | 4 ++-- pythonmod/doc/modules/config.rst | 3 ++- pythonmod/doc/modules/env.rst | 7 +++++-- pythonmod/examples/edns.py | 2 +- pythonmod/examples/inplace_callbacks.py | 2 +- pythonmod/examples/log.py | 2 +- pythonmod/interface.i | 8 ++++++++ pythonmod/pythonmod.c | 10 +++++++++- pythonmod/ubmodule-msg.py | 2 +- pythonmod/ubmodule-tst.py | 7 +------ 12 files changed, 38 insertions(+), 18 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index eb2dae2ad..893dfc2d5 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,8 @@ +16 October 2023: George + - Expose the script filename in the Python module environment 'mod_env' + instead of the config_file structure which includes the linked list + of scripts in a multi Python module setup; fixes #79. + 13 October 2023: George - Better fix for infinite loop when reading multiple lines of input on a broken remote control socket, by treating a zero byte line the diff --git a/pythonmod/doc/examples/example0-1.py b/pythonmod/doc/examples/example0-1.py index 7904f73a5..506235eb4 100644 --- a/pythonmod/doc/examples/example0-1.py +++ b/pythonmod/doc/examples/example0-1.py @@ -1,9 +1,9 @@ def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def init_standard(id, env): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, env.cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/doc/examples/example0.rst b/pythonmod/doc/examples/example0.rst index 693972a14..cee551de0 100644 --- a/pythonmod/doc/examples/example0.rst +++ b/pythonmod/doc/examples/example0.rst @@ -50,7 +50,7 @@ Script file must contain four compulsory functions: :: def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True @@ -69,7 +69,7 @@ Script file must contain four compulsory functions: :: def init_standard(id, env): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, env.cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, env.cfg.port, mod_env['script'])) return True diff --git a/pythonmod/doc/modules/config.rst b/pythonmod/doc/modules/config.rst index 89afbef8a..1298725f2 100644 --- a/pythonmod/doc/modules/config.rst +++ b/pythonmod/doc/modules/config.rst @@ -339,4 +339,5 @@ config_file .. attribute:: python_script - Python script file. + Linked list of Python script files. + Deprecated; `mod_env['script']` should be used instead. diff --git a/pythonmod/doc/modules/env.rst b/pythonmod/doc/modules/env.rst index eae4c73c7..be5c3b1db 100644 --- a/pythonmod/doc/modules/env.rst +++ b/pythonmod/doc/modules/env.rst @@ -6,8 +6,11 @@ Global variables .. envvar:: mod_env - Module environment, contains data pointer for module-specific data. - See :class:`pythonmod_env`. + Module environment, it is the 'data' pointer for module-specific data + in :class:`pythonmod_env`. + It is initialized as a dictionary with the 'script' key pointing to the + module's python script. + It can be further populated during runtime for module-specific data. Predefined constants diff --git a/pythonmod/examples/edns.py b/pythonmod/examples/edns.py index ddcccc51c..4e2eebd4f 100644 --- a/pythonmod/examples/edns.py +++ b/pythonmod/examples/edns.py @@ -80,7 +80,7 @@ def init_standard(id, env): ..note:: The previously accessible configuration options can now be found in env.cfg. """ - log_info("python: inited script {}".format(env.cfg.python_script)) + log_info("python: inited script {}".format(mod_env['script'])) # Register EDNS option 65001 as a known EDNS option. if not register_edns_option(env, 65001, bypass_cache_stage=True, diff --git a/pythonmod/examples/inplace_callbacks.py b/pythonmod/examples/inplace_callbacks.py index e1caaecc7..42806daa1 100644 --- a/pythonmod/examples/inplace_callbacks.py +++ b/pythonmod/examples/inplace_callbacks.py @@ -287,7 +287,7 @@ def init_standard(id, env): env.cfg. """ - log_info("python: inited script {}".format(env.cfg.python_script)) + log_info("python: inited script {}".format(mod_env['script'])) # Register the inplace_reply_callback function as an inplace callback # function when answering a resolved query. diff --git a/pythonmod/examples/log.py b/pythonmod/examples/log.py index c17106b0f..03f741962 100644 --- a/pythonmod/examples/log.py +++ b/pythonmod/examples/log.py @@ -87,7 +87,7 @@ def logDnsMsg(qstate): print "-"*100 def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/interface.i b/pythonmod/interface.i index a436389e1..d31c7ae55 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -952,6 +952,7 @@ struct config_str2list { /* ************************************************************************************ * Structure config_file * ************************************************************************************ */ +%ignore config_file::python_script; struct config_file { int verbosity; int stat_interval; @@ -1035,6 +1036,13 @@ struct config_file { struct config_strlist* python_script; }; +%extend config_file { + %pythoncode %{ + def _deprecated_python_script(self): return "cfg.python_script is deprecated, you can use `mod_env['script']` instead." + python_script = property(_deprecated_python_script) + %} +} + /* ************************************************************************************ * ASN: Adding structures related to forwards_lookup and dns_cache_find_delegation * ************************************************************************************ */ diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index 628308612..e655066cb 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -269,7 +269,7 @@ int pythonmod_init(struct module_env* env, int id) /* Initialize module */ FILE* script_py = NULL; - PyObject* py_init_arg = NULL, *res = NULL; + PyObject* py_init_arg = NULL, *res = NULL, *fname = NULL; PyGILState_STATE gil; int init_standard = 1, i = 0; #if PY_MAJOR_VERSION < 3 @@ -419,6 +419,14 @@ int pythonmod_init(struct module_env* env, int id) pe->dict = PyModule_GetDict(pe->module); Py_XINCREF(pe->dict); pe->data = PyDict_New(); + /* add the script filename to the global "mod_env" for trivial access */ + fname = PyString_FromString(pe->fname); + if(PyDict_SetItemString(pe->data, "script", fname) < 0) { + log_err("pythonmod: could not add item to dictionary"); + Py_XDECREF(fname); + goto python_init_fail; + } + Py_XDECREF(fname); Py_XINCREF(pe->data); /* reference will be stolen below */ if(PyModule_AddObject(pe->module, "mod_env", pe->data) < 0) { log_err("pythonmod: could not add mod_env object"); diff --git a/pythonmod/ubmodule-msg.py b/pythonmod/ubmodule-msg.py index 648368080..6a690e281 100644 --- a/pythonmod/ubmodule-msg.py +++ b/pythonmod/ubmodule-msg.py @@ -35,7 +35,7 @@ import os def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/pythonmod/ubmodule-tst.py b/pythonmod/ubmodule-tst.py index 70729071c..07543e399 100644 --- a/pythonmod/ubmodule-tst.py +++ b/pythonmod/ubmodule-tst.py @@ -33,12 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. ''' def init(id, cfg): - scripts=[] - s = cfg.python_script - while s != None: - scripts.append(s.str) - s = s.next - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, scripts)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): From 122dd6c11e946e7e60e4c4e9ca9cc73c4fe6b74d Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 16 Oct 2023 15:53:47 +0200 Subject: [PATCH 50/64] - Expose the configured listening and outgoing interfaces, if any, as a list of strings in the Python 'config_file' class instead of the current Swig object proxy; fixes #79. --- doc/Changelog | 3 +++ pythonmod/doc/modules/config.rst | 4 ++-- pythonmod/interface.i | 28 ++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/doc/Changelog b/doc/Changelog index 893dfc2d5..195e3b170 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -2,6 +2,9 @@ - Expose the script filename in the Python module environment 'mod_env' instead of the config_file structure which includes the linked list of scripts in a multi Python module setup; fixes #79. + - Expose the configured listening and outgoing interfaces, if any, as + a list of strings in the Python 'config_file' class instead of the + current Swig object proxy; fixes #79. 13 October 2023: George - Better fix for infinite loop when reading multiple lines of input on diff --git a/pythonmod/doc/modules/config.rst b/pythonmod/doc/modules/config.rst index 1298725f2..ac4db4c94 100644 --- a/pythonmod/doc/modules/config.rst +++ b/pythonmod/doc/modules/config.rst @@ -129,7 +129,7 @@ config_file .. attribute:: ifs - Interface description strings (IP addresses). + List of interface description strings (IP addresses). .. attribute:: num_out_ifs @@ -138,7 +138,7 @@ config_file .. attribute:: out_ifs - Outgoing interface description strings (IP addresses). + List of outgoing interface description strings (IP addresses). .. attribute:: root_hints diff --git a/pythonmod/interface.i b/pythonmod/interface.i index d31c7ae55..d9839fc38 100644 --- a/pythonmod/interface.i +++ b/pythonmod/interface.i @@ -86,6 +86,20 @@ } return list; } + + /* converts an array of strings (char**) to a List of strings */ + PyObject* CharArrayAsStringList(char** array, int len) { + PyObject* list; + int i; + + if(!array||len==0) return PyList_New(0); + + list = PyList_New(len); + for (i=0; i < len; i++) { + PyList_SET_ITEM(list, i, PyString_FromString(array[i])); + } + return list; + } %} /* ************************************************************************************ * @@ -952,6 +966,8 @@ struct config_str2list { /* ************************************************************************************ * Structure config_file * ************************************************************************************ */ +%ignore config_file::ifs; +%ignore config_file::out_ifs; %ignore config_file::python_script; struct config_file { int verbosity; @@ -1036,8 +1052,20 @@ struct config_file { struct config_strlist* python_script; }; +%inline %{ + PyObject* _get_ifs_tuple(struct config_file* cfg) { + return CharArrayAsStringList(cfg->ifs, cfg->num_ifs); + } + PyObject* _get_ifs_out_tuple(struct config_file* cfg) { + return CharArrayAsStringList(cfg->out_ifs, cfg->num_out_ifs); + } +%} + %extend config_file { %pythoncode %{ + ifs = property(_unboundmodule._get_ifs_tuple) + out_ifs = property(_unboundmodule._get_ifs_out_tuple) + def _deprecated_python_script(self): return "cfg.python_script is deprecated, you can use `mod_env['script']` instead." python_script = property(_deprecated_python_script) %} From e4510c76e54fc50cbcb4b33c2b39f0a51e17684a Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 16 Oct 2023 16:03:11 +0200 Subject: [PATCH 51/64] - For multi Python module setups, clean previously parsed module functions in __main__'s dictionary, if any, so that only current module functions are registered. --- doc/Changelog | 3 +++ pythonmod/pythonmod.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 195e3b170..ff12a6b46 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -5,6 +5,9 @@ - Expose the configured listening and outgoing interfaces, if any, as a list of strings in the Python 'config_file' class instead of the current Swig object proxy; fixes #79. + - For multi Python module setups, clean previously parsed module + functions in __main__'s dictionary, if any, so that only current + module functions are registered. 13 October 2023: George - Better fix for infinite loop when reading multiple lines of input on diff --git a/pythonmod/pythonmod.c b/pythonmod/pythonmod.c index e655066cb..c6294a1d5 100644 --- a/pythonmod/pythonmod.c +++ b/pythonmod/pythonmod.c @@ -112,6 +112,34 @@ struct pythonmod_qstate { PyObject* data; }; +/* The dict from __main__ could have remnants from a previous script + * invocation, in a multi python module setup. Usually this is fine since newer + * scripts will update their values. The obvious erroneous case is when mixing + * python scripts that make use of both 'init' and 'init_standard'. This + * results in 'init_standard' to persist on following scripts that don't use it + * (thus not replacing it). This is also problematic in case where a script + * does not define a required function but a previously loaded script did. The + * current solution is to make sure to clean offensive remnants that influence + * further parsing of the individual scripts. + */ +static void +clean_python_function_objects(PyObject* dict) { + const char* function_names[] = { + "init", + "init_standard", + "deinit", + "operate", + "inform_super" + }; + size_t i; + + for(i=0; imodule); pe->dict = PyModule_GetDict(pe->module); Py_XINCREF(pe->dict); + clean_python_function_objects(pe->dict); + pe->data = PyDict_New(); /* add the script filename to the global "mod_env" for trivial access */ fname = PyString_FromString(pe->fname); From 4b627bd29e5c28fef256c7e6182bced10adbcc68 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Mon, 16 Oct 2023 16:32:09 +0200 Subject: [PATCH 52/64] - Update pymod tests for the new Python script variable. --- testdata/pymod.tdir/pymod.py | 7 +------ testdata/pymod_thread.tdir/pymod_thread.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/testdata/pymod.tdir/pymod.py b/testdata/pymod.tdir/pymod.py index 1eb7af5b1..f1e988b39 100644 --- a/testdata/pymod.tdir/pymod.py +++ b/testdata/pymod.tdir/pymod.py @@ -37,12 +37,7 @@ import os def init(id, cfg): - scripts=[] - s = cfg.python_script - while s != None: - scripts.append(s.str) - s = s.next - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, scripts)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): diff --git a/testdata/pymod_thread.tdir/pymod_thread.py b/testdata/pymod_thread.tdir/pymod_thread.py index 30c258875..58304ab46 100644 --- a/testdata/pymod_thread.tdir/pymod_thread.py +++ b/testdata/pymod_thread.tdir/pymod_thread.py @@ -37,7 +37,7 @@ import os def init(id, cfg): - log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, cfg.python_script)) + log_info("pythonmod: init called, module id is %d port: %d script: %s" % (id, cfg.port, mod_env['script'])) return True def deinit(id): From 0f78bea4a3b6a65a628342b69a5a36ad62ee3d6e Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Tue, 17 Oct 2023 16:47:04 +0200 Subject: [PATCH 53/64] - Fix #954: Inconsistent RPZ handling for A record returned along with CNAME. --- doc/Changelog | 4 ++ iterator/iterator.c | 33 +++++++++ services/rpz.c | 4 ++ testdata/rpz_cached_cname.rpl | 122 ++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) create mode 100644 testdata/rpz_cached_cname.rpl diff --git a/doc/Changelog b/doc/Changelog index ff12a6b46..bdd8ea2f1 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +17 October 2023: Wouter + - Fix #954: Inconsistent RPZ handling for A record returned along with + CNAME. + 16 October 2023: George - Expose the script filename in the Python module environment 'mod_env' instead of the config_file structure which includes the linked list diff --git a/iterator/iterator.c b/iterator/iterator.c index 053c0faec..6ff811a27 100644 --- a/iterator/iterator.c +++ b/iterator/iterator.c @@ -1449,6 +1449,39 @@ processInitRequest(struct module_qstate* qstate, struct iter_qstate* iq, } iq->qchase.qname = sname; iq->qchase.qname_len = slen; + if(qstate->env->auth_zones) { + /* apply rpz qname triggers after cname */ + struct dns_msg* forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + while(forged_response && reply_find_rrset_section_an( + forged_response->rep, iq->qchase.qname, + iq->qchase.qname_len, LDNS_RR_TYPE_CNAME, + iq->qchase.qclass)) { + /* another cname to follow */ + if(!handle_cname_response(qstate, iq, forged_response, + &sname, &slen)) { + errinf(qstate, "malloc failure, CNAME info"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + iq->qchase.qname = sname; + iq->qchase.qname_len = slen; + forged_response = + rpz_callback_from_iterator_cname(qstate, iq); + } + if(forged_response != NULL) { + qstate->ext_state[id] = module_finished; + qstate->return_rcode = LDNS_RCODE_NOERROR; + qstate->return_msg = forged_response; + iq->response = forged_response; + next_state(iq, FINISHED_STATE); + if(!iter_prepend(iq, qstate->return_msg, qstate->region)) { + log_err("rpz: after cached cname, prepend rrsets: out of memory"); + return error_response(qstate, id, LDNS_RCODE_SERVFAIL); + } + qstate->return_msg->qinfo = qstate->qinfo; + return 0; + } + } /* This *is* a query restart, even if it is a cheap * one. */ iq->dp = NULL; diff --git a/services/rpz.c b/services/rpz.c index 7f7a2fa9b..18d76c07b 100644 --- a/services/rpz.c +++ b/services/rpz.c @@ -2448,6 +2448,10 @@ struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, rpz_action_to_string(localzone_type_to_rpz_action(lzt))); ret = NULL; } + if(r->log) + log_rpz_apply("qname", (z?z->name:NULL), NULL, + localzone_type_to_rpz_action(lzt), + &is->qchase, NULL, ms, r->log_name); lock_rw_unlock(&z->lock); lock_rw_unlock(&a->lock); return ret; diff --git a/testdata/rpz_cached_cname.rpl b/testdata/rpz_cached_cname.rpl new file mode 100644 index 000000000..198b94631 --- /dev/null +++ b/testdata/rpz_cached_cname.rpl @@ -0,0 +1,122 @@ +; config options +server: + module-config: "respip validator iterator" + target-fetch-policy: "0 0 0 0 0" + qname-minimisation: no + rrset-roundrobin: no + access-control: 192.0.0.0/8 allow + +rpz: + name: "rpz.example.com" + rpz-log: yes + rpz-log-name: "rpz.example.com" + zonefile: +TEMPFILE_NAME rpz.example.com +TEMPFILE_CONTENTS rpz.example.com +rpz.example.com. 3600 IN SOA ns.rpz.example.com. hostmaster.rpz.example.com. 1 3600 900 86400 3600 +rpz.example.com. 3600 IN NS ns.rpz.example.net. +a.foo.rpz.example.com. 120 IN A 10.99.99.99 +TEMPFILE_END + +stub-zone: + name: "." + stub-addr: 10.20.30.40 + +CONFIG_END + +SCENARIO_BEGIN Test RPZ with cached CNAME to A record + +RANGE_BEGIN 0 100 + ADDRESS 10.20.30.40 + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS ns. +SECTION ADDITIONAL +ns. IN NS 10.20.30.40 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 30 A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname qtype +ADJUST copy_id +REPLY QR NOERROR AA +SECTION QUESTION +a.foo. IN A +SECTION ANSWER +a.foo. A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.foo. IN A +ENTRY_END + +STEP 20 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +a.foo. IN A +SECTION ANSWER +a.foo. 120 A 10.99.99.99 +ENTRY_END + +STEP 30 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.foo. IN A +ENTRY_END + +STEP 40 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 120 A 10.99.99.99 +ENTRY_END + +STEP 50 TIME_PASSES ELAPSE 3 + +STEP 60 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.foo. IN A +ENTRY_END + +STEP 70 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA AA NOERROR +SECTION QUESTION +b.foo. IN A +SECTION ANSWER +b.foo. 30 CNAME a.foo. +a.foo. 120 A 10.99.99.99 +ENTRY_END + +SCENARIO_END From 23ae0a9838308706167484ef09a6ffe481953369 Mon Sep 17 00:00:00 2001 From: "Alexander V. Buev" Date: Tue, 17 Oct 2023 18:35:42 +0300 Subject: [PATCH 54/64] fix ipset wrong behavior Issue description: If local zone directive is assigned as following: "local-zone domain.com ipset" then any answers for query with names such as "anytext1domain.com" or "example2domain.com" will be added to ipset. This commit fixes this issue. --- ipset/ipset.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ipset/ipset.c b/ipset/ipset.c index c61ebc205..af55de8d6 100644 --- a/ipset/ipset.c +++ b/ipset/ipset.c @@ -158,10 +158,10 @@ ipset_check_zones_for_rrset(struct module_env *env, struct ipset_env *ie, qs = NULL; plen = strlen(p->str); - if (dlen >= plen) { + if (dlen == plen || (dlen > plen && dname[dlen - plen - 1] == '.' )) { ds = dname + (dlen - plen); } - if (qlen >= plen) { + if (qlen == plen || (qlen > plen && qname[qlen - plen - 1] == '.' )) { qs = qname + (qlen - plen); } if ((ds && strncasecmp(p->str, ds, plen) == 0) From 0c88f98a3b07c0a84e2f9eaa97f6867f06289de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber=20via=20Unbound-users?= Date: Mon, 9 Oct 2023 14:43:34 +0200 Subject: [PATCH 55/64] dns64: Refactor handle_event checks for readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change intended. Signed-off-by: Daniel Gröber --- dns64/dns64.c | 56 ++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/dns64/dns64.c b/dns64/dns64.c index 1e31f51e8..83d61b2bd 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -580,16 +580,16 @@ handle_event_pass(struct module_qstate* qstate, int id) /* Handle PTR queries for IPv6 addresses. */ return handle_ipv6_ptr(qstate, id); - if (qstate->env->cfg->dns64_synthall && - iq && iq->state == DNS64_NEW_QUERY - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) - return generate_type_A_query(qstate, id); - - if(dns64_always_synth_for_qname(qstate, id) && - iq && iq->state == DNS64_NEW_QUERY - && !(qstate->query_flags & BIT_CD) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA) { - verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); + int synth_all_cfg = qstate->env->cfg->dns64_synthall; + int synth_qname = + dns64_always_synth_for_qname(qstate, id) && + !(qstate->query_flags & BIT_CD); + + if (iq && iq->state == DNS64_NEW_QUERY && + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && + (synth_all_cfg || synth_qname)) { + if (synth_qname) + verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } @@ -627,24 +627,26 @@ handle_event_moddone(struct module_qstate* qstate, int id) * synthesize in (sec 5.1.2 of RFC6147). * - A successful AAAA query with an answer. */ - if((!iq || iq->state != DNS64_INTERNAL_QUERY) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA - && !(qstate->query_flags & BIT_CD) - && !(qstate->return_msg && - qstate->return_msg->rep && - reply_find_answer_rrset(&qstate->qinfo, - qstate->return_msg->rep))) - /* not internal, type AAAA, not CD, and no answer RRset, - * So, this is a AAAA noerror/nodata answer */ - return generate_type_A_query(qstate, id); - if((!iq || iq->state != DNS64_INTERNAL_QUERY) - && qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA - && !(qstate->query_flags & BIT_CD) - && dns64_always_synth_for_qname(qstate, id)) { - /* if it is not internal, AAAA, not CD and listed domain, - * generate from A record and ignore AAAA */ - verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); + /* When an AAAA query completes check if we want to perform DNS64 + * synthesis. We skip queries with DNSSEC enabled (!CD) and + * ones generated by us to retrive the A/PTR record to use for + * synth. */ + int want_synth = + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && + (!iq || iq->state != DNS64_INTERNAL_QUERY) && + !(qstate->query_flags & BIT_CD); + int has_data = /*< whether query returned non-empty rrset */ + qstate->return_msg && + qstate->return_msg->rep && + reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep); + int synth_qname; + + if(want_synth && + (!has_data || (synth_qname=dns64_always_synth_for_qname(qstate, id)))) + { + if (synth_qname) + verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } From 213bb7c6ed29a401c678403e2162263766fa8269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber=20via=20Unbound-users?= Date: Mon, 9 Oct 2023 14:43:35 +0200 Subject: [PATCH 56/64] dns64: Fix misleading indentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Gröber --- dns64/dns64.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dns64/dns64.c b/dns64/dns64.c index 83d61b2bd..b856a86c9 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -577,8 +577,8 @@ handle_event_pass(struct module_qstate* qstate, int id) && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR && qstate->qinfo.qname_len == 74 && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) - /* Handle PTR queries for IPv6 addresses. */ - return handle_ipv6_ptr(qstate, id); + /* Handle PTR queries for IPv6 addresses. */ + return handle_ipv6_ptr(qstate, id); int synth_all_cfg = qstate->env->cfg->dns64_synthall; int synth_qname = @@ -652,9 +652,12 @@ handle_event_moddone(struct module_qstate* qstate, int id) /* Store the response in cache. */ if ( (!iq || !iq->started_no_cache_store) && - qstate->return_msg && qstate->return_msg->rep && - !dns_cache_store(qstate->env, &qstate->qinfo, qstate->return_msg->rep, - 0, 0, 0, NULL, qstate->query_flags, qstate->qstarttime)) + qstate->return_msg && + qstate->return_msg->rep && + !dns_cache_store( + qstate->env, &qstate->qinfo, qstate->return_msg->rep, + 0, 0, 0, NULL, + qstate->query_flags, qstate->qstarttime)) log_err("out of memory"); /* do nothing */ From dd086e5bfd81080596112e02294d4d202ea9d69f Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Tue, 17 Oct 2023 15:00:07 +0200 Subject: [PATCH 57/64] Fixes for dns64 readability refactoring: - Move declarations to the top for C90 compliance. - Save cycles by not calling (yet) unneeded functions. - Possible use of uninitialised value. - Consistent formatting. --- dns64/dns64.c | 53 ++++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/dns64/dns64.c b/dns64/dns64.c index b856a86c9..178427479 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -573,28 +573,29 @@ static enum module_ext_state handle_event_pass(struct module_qstate* qstate, int id) { struct dns64_qstate* iq = (struct dns64_qstate*)qstate->minfo[id]; - if (iq && iq->state == DNS64_NEW_QUERY - && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR - && qstate->qinfo.qname_len == 74 - && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) + int synth_all_cfg = qstate->env->cfg->dns64_synthall; + int synth_qname = 0; + + if(iq && iq->state == DNS64_NEW_QUERY + && qstate->qinfo.qtype == LDNS_RR_TYPE_PTR + && qstate->qinfo.qname_len == 74 + && !strcmp((char*)&qstate->qinfo.qname[64], "\03ip6\04arpa")) { /* Handle PTR queries for IPv6 addresses. */ return handle_ipv6_ptr(qstate, id); + } - int synth_all_cfg = qstate->env->cfg->dns64_synthall; - int synth_qname = - dns64_always_synth_for_qname(qstate, id) && - !(qstate->query_flags & BIT_CD); - - if (iq && iq->state == DNS64_NEW_QUERY && - qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && - (synth_all_cfg || synth_qname)) { - if (synth_qname) + if(iq && iq->state == DNS64_NEW_QUERY && + qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && + (synth_all_cfg || + (synth_qname=(dns64_always_synth_for_qname(qstate, id) + && !(qstate->query_flags & BIT_CD))))) { + if(synth_qname) verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } /* We are finished when our sub-query is finished. */ - if (iq && iq->state == DNS64_SUBQUERY_FINISHED) + if(iq && iq->state == DNS64_SUBQUERY_FINISHED) return module_finished; /* Otherwise, pass request to next module. */ @@ -636,28 +637,28 @@ handle_event_moddone(struct module_qstate* qstate, int id) qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && (!iq || iq->state != DNS64_INTERNAL_QUERY) && !(qstate->query_flags & BIT_CD); - int has_data = /*< whether query returned non-empty rrset */ + int has_data = /* whether query returned non-empty rrset */ qstate->return_msg && qstate->return_msg->rep && reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep); - int synth_qname; + int synth_qname = 0; if(want_synth && - (!has_data || (synth_qname=dns64_always_synth_for_qname(qstate, id)))) - { - if (synth_qname) + (!has_data || + (synth_qname=dns64_always_synth_for_qname(qstate, id)))) { + if(synth_qname) verbose(VERB_ALGO, "dns64: ignore-aaaa and synthesize anyway"); return generate_type_A_query(qstate, id); } /* Store the response in cache. */ - if ( (!iq || !iq->started_no_cache_store) && - qstate->return_msg && - qstate->return_msg->rep && - !dns_cache_store( - qstate->env, &qstate->qinfo, qstate->return_msg->rep, - 0, 0, 0, NULL, - qstate->query_flags, qstate->qstarttime)) + if( (!iq || !iq->started_no_cache_store) && + qstate->return_msg && + qstate->return_msg->rep && + !dns_cache_store( + qstate->env, &qstate->qinfo, qstate->return_msg->rep, + 0, 0, 0, NULL, + qstate->query_flags, qstate->qstarttime)) log_err("out of memory"); /* do nothing */ From c1e5e6781ee60ca7136b122b44742b2d035da032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gr=C3=B6ber=20via=20Unbound-users?= Date: Mon, 9 Oct 2023 14:46:35 +0200 Subject: [PATCH 58/64] dns64: Fall back to plain AAAA query with synthall but no A records MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Networks which only have tunneled IPv6 access but still want to go IPv6-only internally can use unbound's DNS64 module together with the dns64-synthall or dns64-ignore-aaaa options to direct most traffic (any dualstack domain) to their NAT64. There is only one problem with this setup, currently domains with only AAAA records will fail to resolve. To allow for this use-case arrange for the A sub-query to make the AAAA super query advance along the module stack when no records are returned. Signed-off-by: Daniel Gröber --- dns64/dns64.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/dns64/dns64.c b/dns64/dns64.c index 178427479..10e7512a9 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -982,6 +982,17 @@ dns64_inform_super(struct module_qstate* qstate, int id, return; } + /* When no A record is found for synthesis fall back to AAAA again. */ + if (qstate->qinfo.qtype == LDNS_RR_TYPE_A && + qstate->return_rcode == LDNS_RCODE_NOERROR && + !( qstate->return_msg && + qstate->return_msg->rep && + reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep))) + { + super_dq->state = DNS64_INTERNAL_QUERY; + return; + } + /* Use return code from A query in response to client. */ if (super->return_rcode != LDNS_RCODE_NOERROR) super->return_rcode = qstate->return_rcode; From d5522c3480dc128dc8a5c84a4d7aea9c8d12e0f9 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Wed, 18 Oct 2023 11:59:41 +0200 Subject: [PATCH 59/64] Fixes for dns64 fallback to plain AAAA when no A records: - Cleanup if condition. - Rename variable for readability. --- dns64/dns64.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/dns64/dns64.c b/dns64/dns64.c index 10e7512a9..83fb02779 100644 --- a/dns64/dns64.c +++ b/dns64/dns64.c @@ -633,7 +633,7 @@ handle_event_moddone(struct module_qstate* qstate, int id) * synthesis. We skip queries with DNSSEC enabled (!CD) and * ones generated by us to retrive the A/PTR record to use for * synth. */ - int want_synth = + int could_synth = qstate->qinfo.qtype == LDNS_RR_TYPE_AAAA && (!iq || iq->state != DNS64_INTERNAL_QUERY) && !(qstate->query_flags & BIT_CD); @@ -643,7 +643,7 @@ handle_event_moddone(struct module_qstate* qstate, int id) reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep); int synth_qname = 0; - if(want_synth && + if(could_synth && (!has_data || (synth_qname=dns64_always_synth_for_qname(qstate, id)))) { if(synth_qname) @@ -975,20 +975,18 @@ dns64_inform_super(struct module_qstate* qstate, int id, } super_dq->state = DNS64_SUBQUERY_FINISHED; - /* If there is no successful answer, we're done. */ - if (qstate->return_rcode != LDNS_RCODE_NOERROR - || !qstate->return_msg - || !qstate->return_msg->rep) { + /* If there is no successful answer, we're done. + * Guarantee that we have at least a NOERROR reply further on. */ + if(qstate->return_rcode != LDNS_RCODE_NOERROR + || !qstate->return_msg + || !qstate->return_msg->rep) { return; } /* When no A record is found for synthesis fall back to AAAA again. */ - if (qstate->qinfo.qtype == LDNS_RR_TYPE_A && - qstate->return_rcode == LDNS_RCODE_NOERROR && - !( qstate->return_msg && - qstate->return_msg->rep && - reply_find_answer_rrset(&qstate->qinfo, qstate->return_msg->rep))) - { + if(qstate->qinfo.qtype == LDNS_RR_TYPE_A && + !reply_find_answer_rrset(&qstate->qinfo, + qstate->return_msg->rep)) { super_dq->state = DNS64_INTERNAL_QUERY; return; } From c5aa6a2286357d7106843123c7d4d9dcb9af03f7 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Wed, 18 Oct 2023 12:16:35 +0200 Subject: [PATCH 60/64] =?UTF-8?q?-=20Changelog=20entry=20for=20DNS64=20pat?= =?UTF-8?q?ches=20from=20Daniel=20Gr=C3=B6ber.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index bdd8ea2f1..3c9fd4b19 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +18 October 2023: George + - Mailing list patches from Daniel Gröber for DNS64 fallback to plain + AAAA when no A record exists for synthesis, and minor DNS64 code + refactoring for better readability. + - Fixes for the DNS64 patches. + 17 October 2023: Wouter - Fix #954: Inconsistent RPZ handling for A record returned along with CNAME. From 2f0b11673a1b19ba3b154dd4538b674cc0046d81 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Wed, 18 Oct 2023 12:59:54 +0200 Subject: [PATCH 61/64] - Update the dns64_lookup.rpl test for the DNS64 fallback patch. --- doc/Changelog | 1 + testdata/dns64_lookup.rpl | 104 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 3c9fd4b19..aaf71f239 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -3,6 +3,7 @@ AAAA when no A record exists for synthesis, and minor DNS64 code refactoring for better readability. - Fixes for the DNS64 patches. + - Update the dns64_lookup.rpl test for the DNS64 fallback patch. 17 October 2023: Wouter - Fix #954: Inconsistent RPZ handling for A record returned along with diff --git a/testdata/dns64_lookup.rpl b/testdata/dns64_lookup.rpl index 7986fc8fc..327f7dfed 100644 --- a/testdata/dns64_lookup.rpl +++ b/testdata/dns64_lookup.rpl @@ -4,6 +4,8 @@ server: qname-minimisation: "no" module-config: "dns64 validator iterator" dns64-prefix: 64:ff9b::0/96 + dns64-ignore-aaaa: ip6ignore.example.com + dns64-ignore-aaaa: ip6only.example.com minimal-responses: no stub-zone: @@ -15,6 +17,7 @@ SCENARIO_BEGIN Test dns64 lookup and synthesis. ; normal A lookup should still succeed ; AAAA is synthesized if not present. ; AAAA if present, is passed through unchanged. +; AAAA if present (but configured to be ignored) but no A, AAAA is passed through unchanged. ; K.ROOT-SERVERS.NET. RANGE_BEGIN 0 200 @@ -154,6 +157,61 @@ SECTION ADDITIONAL ns.example.com. IN A 1.2.3.4 ENTRY_END +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6ignore.example.com. IN AAAA +SECTION ANSWER +ip6ignore.example.com. IN AAAA 1:2:3::4 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6ignore.example.com. IN A +SECTION ANSWER +ip6ignore.example.com. IN A 5.6.7.8 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6only.example.com. IN AAAA +SECTION ANSWER +ip6only.example.com. IN AAAA 1:2:3::4 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +ip6only.example.com. IN A +SECTION ANSWER +; NO A present +SECTION AUTHORITY +example.com. IN SOA a. b. 1 2 3 4 5 +ENTRY_END + + ENTRY_BEGIN MATCH opcode qtype qname ADJUST copy_id @@ -257,4 +315,50 @@ SECTION AUTHORITY 7.6.5.in-addr.arpa. IN NS ns.example.com. ENTRY_END +; ignore AAAA and synthesize from A record 5.6.7.8 +STEP 80 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +ip6ignore.example.com. IN AAAA +ENTRY_END + +; recursion happens here. +STEP 90 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +ip6ignore.example.com. IN AAAA +SECTION ANSWER +ip6ignore.example.com. IN AAAA 64:ff9b::506:708 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + +; try to ignore AAAA but no A record to synthesize, fallback to AAAA +STEP 100 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +ip6only.example.com. IN AAAA +ENTRY_END + +; recursion happens here. +STEP 110 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD RA NOERROR +SECTION QUESTION +ip6only.example.com. IN AAAA +SECTION ANSWER +ip6only.example.com. IN AAAA 1:2:3::4 +SECTION AUTHORITY +example.com. IN NS ns.example.com. +SECTION ADDITIONAL +ns.example.com. IN A 1.2.3.4 +ENTRY_END + SCENARIO_END From 44c3d4d2dc0a3aafbf60edf9e59922b6105cfee9 Mon Sep 17 00:00:00 2001 From: George Thessalonikefs Date: Wed, 18 Oct 2023 15:11:38 +0200 Subject: [PATCH 62/64] - Changelog entry for: Merge #955 from buevsan: fix ipset wrong behavior. - Update testdata/ipset.tdir test for ipset fix. --- doc/Changelog | 2 ++ testdata/ipset.tdir/ipset.conf | 3 +++ testdata/ipset.tdir/ipset.test | 23 +++++++++++++++++++++++ testdata/ipset.tdir/ipset.testns | 10 ++++++++++ 4 files changed, 38 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index aaf71f239..b8b2ddd52 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -4,6 +4,8 @@ refactoring for better readability. - Fixes for the DNS64 patches. - Update the dns64_lookup.rpl test for the DNS64 fallback patch. + - Merge #955 from buevsan: fix ipset wrong behavior. + - Update testdata/ipset.tdir test for ipset fix. 17 October 2023: Wouter - Fix #954: Inconsistent RPZ handling for A record returned along with diff --git a/testdata/ipset.tdir/ipset.conf b/testdata/ipset.tdir/ipset.conf index 7cc34912d..3a5eb7b64 100644 --- a/testdata/ipset.tdir/ipset.conf +++ b/testdata/ipset.tdir/ipset.conf @@ -18,6 +18,9 @@ stub-zone: stub-zone: name: "example.com." stub-addr: "127.0.0.1@@TOPORT@" +stub-zone: + name: "lookslikeexample.net." + stub-addr: "127.0.0.1@@TOPORT@" ipset: name-v4: atotallymadeupnamefor4 name-v6: atotallymadeupnamefor6 diff --git a/testdata/ipset.tdir/ipset.test b/testdata/ipset.tdir/ipset.test index 4dab457ba..bfe17b6e6 100644 --- a/testdata/ipset.tdir/ipset.test +++ b/testdata/ipset.tdir/ipset.test @@ -146,6 +146,29 @@ else exit 1 fi +echo "> dig lookslikeexample.net. AAAA" +dig @127.0.0.1 -p $UNBOUND_PORT lookslikeexample.net. AAAA | tee outfile +echo "> check answer" +if grep "::4" 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 ::4 to atotallymadeupnamefor6 for lookslikeexample.net." unbound.log; then + echo "> cat logfiles" + cat fwd.log + cat unbound.log + echo "Not OK" + exit 1 +else + echo "ipset OK" +fi + echo "> cat logfiles" cat tap.log cat tap.errlog diff --git a/testdata/ipset.tdir/ipset.testns b/testdata/ipset.tdir/ipset.testns index 2b626e915..f67d77ed6 100644 --- a/testdata/ipset.tdir/ipset.testns +++ b/testdata/ipset.tdir/ipset.testns @@ -101,3 +101,13 @@ target.example.com. IN AAAA SECTION ANSWER target.example.com. IN AAAA ::3 ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +REPLY QR AA NOERROR +ADJUST copy_id +SECTION QUESTION +lookslikeexample.net. IN AAAA +SECTION ANSWER +lookslikeexample.net. IN AAAA ::4 +ENTRY_END From 35d0a8a843ce72821c5580608858f100f1365b52 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 19 Oct 2023 11:17:32 +0200 Subject: [PATCH 63/64] - Fix to print detailed errors when an SSL IO routine fails via SSL_get_error. --- daemon/remote.c | 17 +++--- dnstap/dtstream.c | 8 +-- dnstap/unbound-dnstap-socket.c | 12 ++-- doc/Changelog | 4 ++ testcode/dohclient.c | 6 +- testcode/streamtcp.c | 2 +- util/net_help.c | 104 +++++++++++++++++++++++++++++++++ util/net_help.h | 18 ++++++ util/netevent.c | 25 ++++---- 9 files changed, 165 insertions(+), 31 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 235f96c7f..3eb711ce6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -523,12 +523,13 @@ ssl_print_text(RES* res, const char* text) if(res->ssl) { ERR_clear_error(); if((r=SSL_write(res->ssl, text, (int)strlen(text))) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) { + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) { verbose(VERB_QUERY, "warning, in SSL_write, peer " "closed connection"); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); return 0; } } else { @@ -579,11 +580,12 @@ ssl_read_line(RES* res, char* buf, size_t max) if(res->ssl) { ERR_clear_error(); if((r=SSL_read(res->ssl, buf+len, 1)) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) { + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) { buf[len] = 0; return 1; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", r2); return 0; } } else { @@ -3222,9 +3224,10 @@ handle_req(struct daemon_remote* rc, struct rc_state* s, RES* res) if(res->ssl) { ERR_clear_error(); if((r=SSL_read(res->ssl, magic, (int)sizeof(magic)-1)) <= 0) { - if(SSL_get_error(res->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(res->ssl, r)) == SSL_ERROR_ZERO_RETURN) return; - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", r2); return; } } else { @@ -3291,7 +3294,7 @@ remote_handshake_later(struct daemon_remote* rc, struct rc_state* s, log_err("remote control connection closed prematurely"); log_addr(VERB_OPS, "failed connection from", &s->c->repinfo.remote_addr, s->c->repinfo.remote_addrlen); - log_crypto_err("remote control failed ssl"); + log_crypto_err_io("remote control failed ssl", r2); clean_point(rc, s); } return 0; diff --git a/dnstap/dtstream.c b/dnstap/dtstream.c index 9153f0404..69c951276 100644 --- a/dnstap/dtstream.c +++ b/dnstap/dtstream.c @@ -788,7 +788,7 @@ static int dtio_write_ssl(struct dt_io_thread* dtio, uint8_t* buf, } return -1; } - log_crypto_err("dnstap io, could not SSL_write"); + log_crypto_err_io("dnstap io, could not SSL_write", want); return -1; } return r; @@ -1029,7 +1029,7 @@ static int ssl_read_bytes(struct dt_io_thread* dtio, void* buf, size_t len) "other side"); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); verbose(VERB_DETAIL, "dnstap io: output closed by the " "other side"); return 0; @@ -1431,8 +1431,8 @@ static int dtio_ssl_handshake(struct dt_io_thread* dtio, } else { unsigned long err = ERR_get_error(); if(!squelch_err_ssl_handshake(err)) { - log_crypto_err_code("dnstap io, ssl handshake failed", - err); + log_crypto_err_io_code("dnstap io, ssl handshake failed", + want, err); verbose(VERB_OPS, "dnstap io, ssl handshake failed " "from %s", dtio->ip_str); } diff --git a/dnstap/unbound-dnstap-socket.c b/dnstap/unbound-dnstap-socket.c index d172a6744..04fda74b8 100644 --- a/dnstap/unbound-dnstap-socket.c +++ b/dnstap/unbound-dnstap-socket.c @@ -708,7 +708,7 @@ static ssize_t ssl_read_bytes(struct tap_data* data, void* buf, size_t len) (data->id?data->id:"")); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); if(verbosity) log_info("dnstap client stream closed from %s", (data->id?data->id:"")); return 0; @@ -760,10 +760,11 @@ static int reply_with_accept(struct tap_data* data) fd_set_block(data->fd); if(data->ssl) { if((r=SSL_write(data->ssl, acceptframe, len)) <= 0) { - if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN) log_err("SSL_write, peer closed connection"); else - log_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); fd_set_nonblock(data->fd); free(acceptframe); return 0; @@ -805,10 +806,11 @@ static int reply_with_finish(struct tap_data* data) if(data->ssl) { int r; if((r=SSL_write(data->ssl, finishframe, len)) <= 0) { - if(SSL_get_error(data->ssl, r) == SSL_ERROR_ZERO_RETURN) + int r2; + if((r2=SSL_get_error(data->ssl, r)) == SSL_ERROR_ZERO_RETURN) log_err("SSL_write, peer closed connection"); else - log_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", r2); fd_set_nonblock(data->fd); free(finishframe); return 0; diff --git a/doc/Changelog b/doc/Changelog index b8b2ddd52..33126fbc3 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,7 @@ +19 October 2023: Wouter + - Fix to print detailed errors when an SSL IO routine fails via + SSL_get_error. + 18 October 2023: George - Mailing list patches from Daniel Gröber for DNS64 fallback to plain AAAA when no A record exists for synthesis, and minor DNS64 code diff --git a/testcode/dohclient.c b/testcode/dohclient.c index de9f39d7d..2c12a5043 100644 --- a/testcode/dohclient.c +++ b/testcode/dohclient.c @@ -286,7 +286,7 @@ static ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), if(want == SSL_ERROR_ZERO_RETURN) { return NGHTTP2_ERR_EOF; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return NGHTTP2_ERR_EOF; } return r; @@ -317,7 +317,7 @@ static ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), if(want == SSL_ERROR_ZERO_RETURN) { return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; @@ -526,7 +526,7 @@ run(struct http2_session* h2_session, int port, int no_tls, int count, char** q) r = SSL_get_error(ssl, r); if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { - log_crypto_err("could not ssl_handshake"); + log_crypto_err_io("could not ssl_handshake", r); exit(1); } } diff --git a/testcode/streamtcp.c b/testcode/streamtcp.c index ef762c161..037bcfd8b 100644 --- a/testcode/streamtcp.c +++ b/testcode/streamtcp.c @@ -466,7 +466,7 @@ send_em(const char* svr, const char* pp2_client, int udp, int usessl, r = SSL_get_error(ssl, r); if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) { - log_crypto_err("could not ssl_handshake"); + log_crypto_err_io("could not ssl_handshake", r); exit(1); } } diff --git a/util/net_help.c b/util/net_help.c index dec46da20..16692b788 100644 --- a/util/net_help.c +++ b/util/net_help.c @@ -952,6 +952,110 @@ void log_crypto_err_code(const char* str, unsigned long err) #endif /* HAVE_SSL */ } +/** Print crypt erro with SSL_get_error want code and err_get_error code */ +static void log_crypto_err_io_code_arg(const char* str, int r, + unsigned long err, int err_present) +{ +#ifdef HAVE_SSL + int print_errno = 0, print_crypto_err = 0; + const char* inf = NULL; + + switch(r) { + case SSL_ERROR_NONE: + inf = "no error"; + break; + case SSL_ERROR_ZERO_RETURN: + inf = "channel closed"; + break; + case SSL_ERROR_WANT_READ: + inf = "want read"; + break; + case SSL_ERROR_WANT_WRITE: + inf = "want write"; + break; + case SSL_ERROR_WANT_CONNECT: + inf = "want connect"; + break; + case SSL_ERROR_WANT_ACCEPT: + inf = "want accept"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + inf = "want X509 lookup"; + break; + case SSL_ERROR_WANT_ASYNC: + inf = "want async"; + break; + case SSL_ERROR_WANT_ASYNC_JOB: + inf = "want async job"; + break; + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + inf = "want client hello cb"; + break; + case SSL_ERROR_SYSCALL: + print_errno = 1; + inf = "syscall"; + break; + case SSL_ERROR_SSL: + print_crypto_err = 1; + inf = "SSL, usually protocol, error"; + break; + default: + inf = "unknown SSL_get_error result code"; + print_errno = 1; + print_crypto_err = 1; + } + if(print_crypto_err) { + if(print_errno) { + char buf[1024]; + snprintf(buf, sizeof(buf), "%s with errno %s", + str, strerror(errno)); + if(err_present) + log_crypto_err_code(buf, err); + else log_crypto_err(buf); + } else { + if(err_present) + log_crypto_err_code(str, err); + else log_crypto_err(str); + } + } else { + if(print_errno) { + if(errno == 0) + log_err("str: syscall error with errno %s", + strerror(errno)); + else log_err("str: %s", strerror(errno)); + } else { + log_err("str: %s", inf); + } + } +#else + (void)str; + (void)r; + (void)err; + (void)err_present; +#endif /* HAVE_SSL */ +} + +void log_crypto_err_io(const char* str, int r) +{ +#ifdef HAVE_SSL + log_crypto_err_io_code_arg(str, r, 0, 0); +#else + (void)str; + (void)r; +#endif /* HAVE_SSL */ +} + +void log_crypto_err_io_code(const char* str, int r, unsigned long err) +{ +#ifdef HAVE_SSL + log_crypto_err_io_code_arg(str, r, err, 1); +#else + (void)str; + (void)r; + (void)err; +#endif /* HAVE_SSL */ +} + #ifdef HAVE_SSL /** log certificate details */ void diff --git a/util/net_help.h b/util/net_help.h index a9de910d5..edaea4235 100644 --- a/util/net_help.h +++ b/util/net_help.h @@ -429,6 +429,24 @@ void log_crypto_err(const char* str); */ void log_crypto_err_code(const char* str, unsigned long err); +/** + * Log an error from libcrypto that came from SSL_write and so on, with + * a value from SSL_get_error, calls log_err. If that fails it logs with + * log_crypto_err. + * @param str: what failed + * @param r: output of SSL_get_error on the I/O operation result. + */ +void log_crypto_err_io(const char* str, int r); + +/** + * Log an error from libcrypt that came from an I/O routine with the + * errcode from ERR_get_error. Calls log_err() and log_crypto_err_code. + * @param str: what failed + * @param r: output of SSL_get_error on the I/O operation result. + * @param err: error code from ERR_get_error + */ +void log_crypto_err_io_code(const char* str, int r, unsigned long err); + /** * Log certificate details verbosity, string, of X509 cert * @param level: verbosity level diff --git a/util/netevent.c b/util/netevent.c index 0a45f22ed..c26b13e4a 100644 --- a/util/netevent.c +++ b/util/netevent.c @@ -1666,7 +1666,8 @@ ssl_handshake(struct comm_point* c) } else { unsigned long err = ERR_get_error(); if(!squelch_err_ssl_handshake(err)) { - log_crypto_err_code("ssl handshake failed", err); + log_crypto_err_io_code("ssl handshake failed", + want, err); log_addr(VERB_OPS, "ssl handshake failed", &c->repinfo.remote_addr, c->repinfo.remote_addrlen); @@ -1816,7 +1817,8 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", + want); return 0; } c->tcp_byte_count += r; @@ -1883,7 +1885,8 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", + want); return 0; } c->tcp_byte_count += r; @@ -1943,7 +1946,7 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } c->tcp_byte_count += r; @@ -1993,7 +1996,7 @@ ssl_handle_read(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } sldns_buffer_skip(c->buffer, (ssize_t)r); @@ -2084,7 +2087,7 @@ ssl_handle_write(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } if(c->tcp_write_and_read) { @@ -2136,7 +2139,7 @@ ssl_handle_write(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } if(c->tcp_write_and_read) { @@ -2930,7 +2933,7 @@ ssl_http_read_more(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return 0; } verbose(VERB_ALGO, "ssl http read more skip to %d + %d", @@ -3381,7 +3384,7 @@ ssize_t http2_recv_cb(nghttp2_session* ATTR_UNUSED(session), uint8_t* buf, strerror(errno)); return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_read"); + log_crypto_err_io("could not SSL_read", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; @@ -3636,7 +3639,7 @@ ssl_http_write_more(struct comm_point* c) strerror(errno)); return 0; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return 0; } sldns_buffer_skip(c->buffer, (ssize_t)r); @@ -3709,7 +3712,7 @@ ssize_t http2_send_cb(nghttp2_session* ATTR_UNUSED(session), const uint8_t* buf, strerror(errno)); return NGHTTP2_ERR_CALLBACK_FAILURE; } - log_crypto_err("could not SSL_write"); + log_crypto_err_io("could not SSL_write", want); return NGHTTP2_ERR_CALLBACK_FAILURE; } return r; From 0ce68e97a74c9ba896e12f9bffa0444c59d43056 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Fri, 20 Oct 2023 17:01:13 +0200 Subject: [PATCH 64/64] Changelog entry for #951. - Merge #951: Cachedb no store. The cachedb-no-store: yes option is used to stop cachedb from writing messages to the backend storage. It reads messages when data is available from the backend. The default is no. --- doc/Changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/Changelog b/doc/Changelog index 33126fbc3..ea948c044 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,9 @@ +20 October 2023: Wouter + - Merge #951: Cachedb no store. The cachedb-no-store: yes option is + used to stop cachedb from writing messages to the backend storage. + It reads messages when data is available from the backend. The + default is no. + 19 October 2023: Wouter - Fix to print detailed errors when an SSL IO routine fails via SSL_get_error.