From 0e9b6f9f434b93ba2f11e3b38a3da379b202585e Mon Sep 17 00:00:00 2001 From: sthen Date: Thu, 20 Oct 2022 08:25:16 +0000 Subject: [PATCH] import unbound 1.17.0, ok florian --- usr.sbin/unbound/dnstap/dtstream.c | 4 +- .../unbound/dnstap/unbound-dnstap-socket.c | 4 +- usr.sbin/unbound/services/rpz.c | 28 +- usr.sbin/unbound/testcode/delayer.c | 2 +- usr.sbin/unbound/testcode/do-tests.sh | 2 + usr.sbin/unbound/testcode/fake_event.c | 21 +- usr.sbin/unbound/testcode/mini_tdir.sh | 38 ++- usr.sbin/unbound/testcode/perf.c | 2 +- usr.sbin/unbound/testcode/replay.c | 11 +- usr.sbin/unbound/testcode/streamtcp.c | 128 +++++++-- usr.sbin/unbound/testcode/unitecs.c | 6 +- usr.sbin/unbound/testcode/unittcpreuse.c | 253 ++++++++++++++++++ usr.sbin/unbound/util/proxy_protocol.c | 139 ++++++++++ usr.sbin/unbound/util/proxy_protocol.h | 131 +++++++++ 14 files changed, 696 insertions(+), 73 deletions(-) create mode 100644 usr.sbin/unbound/util/proxy_protocol.c create mode 100644 usr.sbin/unbound/util/proxy_protocol.h diff --git a/usr.sbin/unbound/dnstap/dtstream.c b/usr.sbin/unbound/dnstap/dtstream.c index a1dd9703ea9..9153f040472 100644 --- a/usr.sbin/unbound/dnstap/dtstream.c +++ b/usr.sbin/unbound/dnstap/dtstream.c @@ -954,7 +954,7 @@ static int dtio_write_more(struct dt_io_thread* dtio) * -1: continue, >0: number of bytes read into buffer */ static ssize_t receive_bytes(struct dt_io_thread* dtio, void* buf, size_t len) { ssize_t r; - r = recv(dtio->fd, (void*)buf, len, 0); + r = recv(dtio->fd, (void*)buf, len, MSG_DONTWAIT); if(r == -1) { char* to = dtio->socket_path; if(!to) to = dtio->ip_str; @@ -1960,7 +1960,7 @@ static int dtio_open_output_tcp(struct dt_io_thread* dtio) memset(&addr, 0, sizeof(addr)); addrlen = (socklen_t)sizeof(addr); - if(!extstrtoaddr(dtio->ip_str, &addr, &addrlen)) { + if(!extstrtoaddr(dtio->ip_str, &addr, &addrlen, UNBOUND_DNS_PORT)) { log_err("could not parse IP '%s'", dtio->ip_str); return 0; } diff --git a/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c b/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c index 63292fbca36..3bf889463ea 100644 --- a/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c +++ b/usr.sbin/unbound/dnstap/unbound-dnstap-socket.c @@ -272,7 +272,7 @@ static int make_tcp_accept(char* ip) memset(&addr, 0, sizeof(addr)); len = (socklen_t)sizeof(addr); - if(!extstrtoaddr(ip, &addr, &len)) { + if(!extstrtoaddr(ip, &addr, &len, UNBOUND_DNS_PORT)) { log_err("could not parse IP '%s'", ip); return -1; } @@ -617,7 +617,7 @@ static void log_data_frame(uint8_t* pkt, size_t len) static ssize_t receive_bytes(struct tap_data* data, int fd, void* buf, size_t len) { - ssize_t ret = recv(fd, buf, len, 0); + ssize_t ret = recv(fd, buf, len, MSG_DONTWAIT); if(ret == 0) { /* closed */ if(verbosity) log_info("dnstap client stream closed from %s", diff --git a/usr.sbin/unbound/services/rpz.c b/usr.sbin/unbound/services/rpz.c index 77b6266fecb..e876f3f9483 100644 --- a/usr.sbin/unbound/services/rpz.c +++ b/usr.sbin/unbound/services/rpz.c @@ -1392,11 +1392,13 @@ log_rpz_apply(char* trigger, uint8_t* dname, struct addr_tree_node* addrnode, dnamestr[0]=0; } if(repinfo) { - addr_to_str(&repinfo->addr, repinfo->addrlen, ip, sizeof(ip)); - port = ntohs(((struct sockaddr_in*)&repinfo->addr)->sin_port); + addr_to_str(&repinfo->client_addr, repinfo->client_addrlen, ip, sizeof(ip)); + port = ntohs(((struct sockaddr_in*)&repinfo->client_addr)->sin_port); } else if(ms && ms->mesh_info && ms->mesh_info->reply_list) { - addr_to_str(&ms->mesh_info->reply_list->query_reply.addr, ms->mesh_info->reply_list->query_reply.addrlen, ip, sizeof(ip)); - port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.addr)->sin_port); + addr_to_str(&ms->mesh_info->reply_list->query_reply.client_addr, + ms->mesh_info->reply_list->query_reply.client_addrlen, + ip, sizeof(ip)); + port = ntohs(((struct sockaddr_in*)&ms->mesh_info->reply_list->query_reply.client_addr)->sin_port); } else { ip[0]=0; port = 0; @@ -1468,7 +1470,9 @@ rpz_resolve_client_action_and_zone(struct auth_zones* az, struct query_info* qin } z = rpz_find_zone(r->local_zones, qinfo->qname, qinfo->qname_len, qinfo->qclass, 0, 0, 0); - node = rpz_ipbased_trigger_lookup(r->client_set, &repinfo->addr, repinfo->addrlen, "clientip"); + node = rpz_ipbased_trigger_lookup(r->client_set, + &repinfo->client_addr, repinfo->client_addrlen, + "clientip"); if((z || node) && r->action_override == RPZ_DISABLED_ACTION) { if(r->log) log_rpz_apply((node?"clientip":"qname"), @@ -2164,18 +2168,16 @@ rpz_callback_from_iterator_module(struct module_qstate* ms, struct iter_qstate* lock_rw_unlock(&az->rpz_lock); - if(raddr == NULL && z == NULL) { return NULL; } - else if(raddr != NULL) { + if(raddr == NULL && z == NULL) + return NULL; + + if(raddr != NULL) { if(z) { lock_rw_unlock(&z->lock); } return rpz_apply_nsip_trigger(ms, r, raddr, a); - } else if(z != NULL) { - if(raddr) { - lock_rw_unlock(&raddr->lock); - } - return rpz_apply_nsdname_trigger(ms, r, z, &match, a); - } else { return NULL; } + } + return rpz_apply_nsdname_trigger(ms, r, z, &match, a); } struct dns_msg* rpz_callback_from_iterator_cname(struct module_qstate* ms, diff --git a/usr.sbin/unbound/testcode/delayer.c b/usr.sbin/unbound/testcode/delayer.c index e915961f5ae..647a4e24c46 100644 --- a/usr.sbin/unbound/testcode/delayer.c +++ b/usr.sbin/unbound/testcode/delayer.c @@ -974,7 +974,7 @@ service(const char* bind_str, int bindport, const char* serv_str, dl_tv_add(&reuse, &delay); if(reuse.tv_sec == 0) reuse.tv_sec = 1; - if(!extstrtoaddr(serv_str, &srv_addr, &srv_len)) { + if(!extstrtoaddr(serv_str, &srv_addr, &srv_len, UNBOUND_DNS_PORT)) { printf("cannot parse forward address: %s\n", serv_str); exit(1); } diff --git a/usr.sbin/unbound/testcode/do-tests.sh b/usr.sbin/unbound/testcode/do-tests.sh index 2a1cfc4c96e..6599f9f6659 100755 --- a/usr.sbin/unbound/testcode/do-tests.sh +++ b/usr.sbin/unbound/testcode/do-tests.sh @@ -16,6 +16,7 @@ NEED_WHOAMI='07-confroot.tdir' NEED_IPV6='fwd_ancil.tdir fwd_tcp_tc6.tdir stub_udp6.tdir edns_cache.tdir' NEED_NOMINGW='tcp_sigpipe.tdir 07-confroot.tdir 08-host-lib.tdir fwd_ancil.tdir' NEED_DNSCRYPT_PROXY='dnscrypt_queries.tdir dnscrypt_queries_chacha.tdir' +NEED_UNSHARE='acl_interface.tdir proxy_protocol.tdir' # test if dig and ldns-testns are available. test_tool_avail "dig" @@ -50,6 +51,7 @@ for test in `ls -d *.tdir`; do skip_if_in_list $test "$NEED_NC" "nc" skip_if_in_list $test "$NEED_WHOAMI" "whoami" skip_if_in_list $test "$NEED_DNSCRYPT_PROXY" "dnscrypt-proxy" + skip_if_in_list $test "$NEED_UNSHARE" "unshare" if echo $NEED_IPV6 | grep $test >/dev/null; then if test "$HAVE_IPV6" = no; then diff --git a/usr.sbin/unbound/testcode/fake_event.c b/usr.sbin/unbound/testcode/fake_event.c index be06a4721c2..efb22a6fb63 100644 --- a/usr.sbin/unbound/testcode/fake_event.c +++ b/usr.sbin/unbound/testcode/fake_event.c @@ -384,8 +384,8 @@ answer_callback_from_entry(struct replay_runtime* runtime, fill_buffer_with_reply(c.buffer, entry, pend->pkt, pend->pkt_len, pend->tcp_pkt_counter); repinfo.c = &c; - repinfo.addrlen = pend->addrlen; - memcpy(&repinfo.addr, &pend->addr, pend->addrlen); + repinfo.remote_addrlen = pend->addrlen; + memcpy(&repinfo.remote_addr, &pend->addr, pend->addrlen); if(!pend->serviced) { if(entry && entry->reply_list->next && pend->tcp_pkt_counter < count_reply_packets(entry)) { @@ -415,7 +415,7 @@ answer_check_it(struct replay_runtime* runtime) tr = transport_udp; if((runtime->now->addrlen == 0 || sockaddr_cmp( &runtime->now->addr, runtime->now->addrlen, - &ans->repinfo.addr, ans->repinfo.addrlen) == 0) && + &ans->repinfo.remote_addr, ans->repinfo.remote_addrlen) == 0) && find_match(runtime->now->match, ans->pkt, ans->pkt_len, tr)) { log_info("testbound matched event entry from line %d", @@ -453,10 +453,12 @@ fake_front_query(struct replay_runtime* runtime, struct replay_moment *todo) repinfo.c = (struct comm_point*)calloc(1, sizeof(struct comm_point)); if(!repinfo.c) fatal_exit("out of memory in fake_front_query"); - repinfo.addrlen = (socklen_t)sizeof(struct sockaddr_in); + repinfo.remote_addrlen = (socklen_t)sizeof(struct sockaddr_in); if(todo->addrlen != 0) { - repinfo.addrlen = todo->addrlen; - memcpy(&repinfo.addr, &todo->addr, todo->addrlen); + repinfo.remote_addrlen = todo->addrlen; + memcpy(&repinfo.remote_addr, &todo->addr, todo->addrlen); + repinfo.client_addrlen = todo->addrlen; + memcpy(&repinfo.client_addr, &todo->addr, todo->addrlen); } repinfo.c->fd = -1; repinfo.c->ev = (struct internal_event*)runtime; @@ -510,8 +512,8 @@ fake_pending_callback(struct replay_runtime* runtime, p->pkt_len, p->tcp_pkt_counter); } repinfo.c = &c; - repinfo.addrlen = p->addrlen; - memcpy(&repinfo.addr, &p->addr, p->addrlen); + repinfo.remote_addrlen = p->addrlen; + memcpy(&repinfo.remote_addr, &p->addr, p->addrlen); if(!p->serviced) { if(todo->match && todo->match->reply_list->next && !error && p->tcp_pkt_counter < count_reply_packets(todo->match)) { @@ -1344,7 +1346,7 @@ struct listen_port* listening_ports_open(struct config_file* ATTR_UNUSED(cfg), char** ATTR_UNUSED(ifs), int ATTR_UNUSED(num_ifs), int* ATTR_UNUSED(reuseport)) { - return calloc(1, 1); + return calloc(1, sizeof(struct listen_port)); } void listening_ports_free(struct listen_port* list) @@ -1663,6 +1665,7 @@ int create_udp_sock(int ATTR_UNUSED(family), int ATTR_UNUSED(socktype), struct comm_point* comm_point_create_udp(struct comm_base *ATTR_UNUSED(base), int ATTR_UNUSED(fd), sldns_buffer* ATTR_UNUSED(buffer), + int ATTR_UNUSED(pp2_enabled), comm_point_callback_type* ATTR_UNUSED(callback), void* ATTR_UNUSED(callback_arg), struct unbound_socket* ATTR_UNUSED(socket)) diff --git a/usr.sbin/unbound/testcode/mini_tdir.sh b/usr.sbin/unbound/testcode/mini_tdir.sh index 6bbece8d936..624ecdf7fe5 100755 --- a/usr.sbin/unbound/testcode/mini_tdir.sh +++ b/usr.sbin/unbound/testcode/mini_tdir.sh @@ -8,6 +8,7 @@ fi # This will keep the temporary directory around and return 1 when the test failed. DEBUG=0 +test -n "$DEBUG_TDIR" && DEBUG=1 quiet=0 if test "$1" = "-q"; then @@ -17,9 +18,9 @@ fi if test "$1" = "clean"; then if test $quiet = 0; then - echo "rm -f result.* .done* .tdir.var.master .tdir.var.test" + echo "rm -f result.* .done* .skip* .tdir.var.master .tdir.var.test" fi - rm -f result.* .done* .tdir.var.master .tdir.var.test + rm -f result.* .done* .skip* .tdir.var.master .tdir.var.test exit 0 fi if test "$1" = "fake"; then @@ -54,12 +55,15 @@ if test "$1" = "-f" && test "$2" = "report"; then echo "** PASSED ** $timelen $name: $desc" pass=`expr $pass + 1` fi + elif test -f ".skip-$name"; then + echo ".. SKIPPED.. $timelen $name: $desc" + skip=`expr $skip + 1` else if test -f "result.$name"; then echo "!! FAILED !! $timelen $name: $desc" fail=`expr $fail + 1` else - echo ".> SKIPPED<< $timelen $name: $desc" + echo ".. SKIPPED.. $timelen $name: $desc" skip=`expr $skip + 1` fi fi @@ -81,11 +85,17 @@ if test "$1" = "report" || test "$2" = "report"; then if test $quiet = 0; then echo "** PASSED ** : $name" fi + elif test -f ".skip-$name"; then + if test $quiet = 0; then + echo ".. SKIPPED.. : $name" + fi else if test -f "result.$name"; then echo "!! FAILED !! : $name" else - echo ">> SKIPPED<< : $name" + if test $quiet = 0; then + echo ".. SKIPPED.. : $name" + fi fi fi done @@ -116,6 +126,7 @@ name=`basename $1 .tdir` dir=$name.$$ result=result.$name done=.done-$name +skip=.skip-$name success="no" if test -x "`which bash`"; then shell="bash" @@ -124,8 +135,8 @@ else fi # check already done -if test -f .done-$name; then - echo "minitdir .done-$name exists. skip test." +if test -f $done; then + echo "minitdir $done exists. skip test." exit 0 fi @@ -151,11 +162,16 @@ if test -f $name.pre; then fi echo "minitdir exe $name.pre" >> $result $shell $name.pre $args >> $result - if test $? -ne 0; then + exit_value=$? + if test $exit_value -eq 3; then + echo "$name: SKIPPED" >> $result + echo "$name: SKIPPED" > ../$skip + echo "$name: SKIPPED" + elif test $exit_value -ne 0; then echo "Warning: $name.pre did not exit successfully" fi fi -if test -f $name.test; then +if test -f $name.test -a ! -f ../$skip; then if test $quiet = 0; then echo "minitdir exe $name.test" fi @@ -167,14 +183,14 @@ if test -f $name.test; then success="no" else echo "$name: PASSED" >> $result - echo "$name: PASSED" > ../.done-$name + echo "$name: PASSED" > ../$done if test $quiet = 0; then echo "$name: PASSED" fi success="yes" fi fi -if test -f $name.post; then +if test -f $name.post -a ! -f ../$skip; then if test $quiet = 0; then echo "minitdir exe $name.post" fi @@ -198,7 +214,7 @@ if test $DEBUG -eq 0; then rm -rf $dir fi else - if test $success == "no"; then + if test $success = "no"; then exit 1 fi exit 0 diff --git a/usr.sbin/unbound/testcode/perf.c b/usr.sbin/unbound/testcode/perf.c index 55d6483c7cf..7fb524e22d9 100644 --- a/usr.sbin/unbound/testcode/perf.c +++ b/usr.sbin/unbound/testcode/perf.c @@ -618,7 +618,7 @@ int main(int argc, char* argv[]) printf("error: pass server IP address on commandline.\n"); usage(nm); } - if(!extstrtoaddr(argv[0], &info.dest, &info.destlen)) { + if(!extstrtoaddr(argv[0], &info.dest, &info.destlen, UNBOUND_DNS_PORT)) { printf("Could not parse ip: %s\n", argv[0]); exit(1); } diff --git a/usr.sbin/unbound/testcode/replay.c b/usr.sbin/unbound/testcode/replay.c index 2487c146f7a..43101d6acec 100644 --- a/usr.sbin/unbound/testcode/replay.c +++ b/usr.sbin/unbound/testcode/replay.c @@ -179,7 +179,8 @@ replay_range_read(char* remain, FILE* in, const char* name, while(isspace((unsigned char)*parse)) parse++; strip_end_white(parse); - if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen)) { + if(!extstrtoaddr(parse, &rng->addr, &rng->addrlen, + UNBOUND_DNS_PORT)) { log_err("Line %d: could not read ADDRESS: %s", pstate->lineno, parse); free(rng); @@ -287,7 +288,8 @@ replay_moment_read(char* remain, FILE* in, const char* name, } else if(parse_keyword(&remain, "QUERY")) { mom->evt_type = repevt_front_query; readentry = 1; - if(!extstrtoaddr("127.0.0.1", &mom->addr, &mom->addrlen)) + if(!extstrtoaddr("127.0.0.1", &mom->addr, &mom->addrlen, + UNBOUND_DNS_PORT)) fatal_exit("internal error"); } else if(parse_keyword(&remain, "CHECK_ANSWER")) { mom->evt_type = repevt_front_reply; @@ -354,7 +356,7 @@ replay_moment_read(char* remain, FILE* in, const char* name, m++; while(isspace((unsigned char)*m)) m++; - if(!extstrtoaddr(s, &mom->addr, &mom->addrlen)) + if(!extstrtoaddr(s, &mom->addr, &mom->addrlen, UNBOUND_DNS_PORT)) fatal_exit("bad infra_rtt address %s", s); strip_end_white(m); mom->variable = strdup(remain); @@ -372,7 +374,8 @@ replay_moment_read(char* remain, FILE* in, const char* name, while(isspace((unsigned char)*remain)) remain++; strip_end_white(remain); - if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen)) { + if(!extstrtoaddr(remain, &mom->addr, &mom->addrlen, + UNBOUND_DNS_PORT)) { log_err("line %d: could not parse ADDRESS: %s", pstate->lineno, remain); free(mom); diff --git a/usr.sbin/unbound/testcode/streamtcp.c b/usr.sbin/unbound/testcode/streamtcp.c index ecc83c1cff3..b2c0d5328bb 100644 --- a/usr.sbin/unbound/testcode/streamtcp.c +++ b/usr.sbin/unbound/testcode/streamtcp.c @@ -49,6 +49,7 @@ #include "util/locks.h" #include "util/log.h" #include "util/net_help.h" +#include "util/proxy_protocol.h" #include "util/data/msgencode.h" #include "util/data/msgparse.h" #include "util/data/msgreply.h" @@ -71,6 +72,7 @@ static void usage(char* argv[]) printf("usage: %s [options] name type class ...\n", argv[0]); printf(" sends the name-type-class queries over TCP.\n"); printf("-f server what ipaddr@portnr to send the queries to\n"); + printf("-p client what ipaddr@portnr to include in PROXYv2\n"); printf("-u use UDP. No retries are attempted.\n"); printf("-n do not wait for an answer.\n"); printf("-a print answers as they arrive.\n"); @@ -82,18 +84,17 @@ static void usage(char* argv[]) /** open TCP socket to svr */ static int -open_svr(const char* svr, int udp) +open_svr(const char* svr, int udp, struct sockaddr_storage* addr, + socklen_t* addrlen) { - struct sockaddr_storage addr; - socklen_t addrlen; int fd = -1; /* svr can be ip@port */ - memset(&addr, 0, sizeof(addr)); - if(!extstrtoaddr(svr, &addr, &addrlen)) { + memset(addr, 0, sizeof(*addr)); + if(!extstrtoaddr(svr, addr, addrlen, UNBOUND_DNS_PORT)) { printf("fatal: bad server specs '%s'\n", svr); exit(1); } - fd = socket(addr_is_ip6(&addr, addrlen)?PF_INET6:PF_INET, + fd = socket(addr_is_ip6(addr, *addrlen)?PF_INET6:PF_INET, udp?SOCK_DGRAM:SOCK_STREAM, 0); if(fd == -1) { #ifndef USE_WINSOCK @@ -103,7 +104,7 @@ open_svr(const char* svr, int udp) #endif exit(1); } - if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { + if(connect(fd, (struct sockaddr*)addr, *addrlen) < 0) { #ifndef USE_WINSOCK perror("connect() error"); #else @@ -116,11 +117,12 @@ open_svr(const char* svr, int udp) /** write a query over the TCP fd */ static void -write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, +write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, + sldns_buffer* proxy_buf, int pp2_parsed, const char* strname, const char* strtype, const char* strclass) { struct query_info qinfo; - uint16_t len; + size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf); /* qname */ qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); if(!qinfo.qname) { @@ -152,9 +154,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, attach_edns_record(buf, &edns); } + /* we need to send the PROXYv2 information in every UDP message */ + if(udp && pp2_parsed) { + /* append the proxy_buf with the buf's content + * and use that for sending */ + if(sldns_buffer_capacity(proxy_buf) < + sldns_buffer_limit(proxy_buf) + + sldns_buffer_limit(buf)) { + printf("buffer too small for packet + proxy"); + exit(1); + } + sldns_buffer_clear(proxy_buf); + sldns_buffer_skip(proxy_buf, proxy_buf_limit); + sldns_buffer_write(proxy_buf, sldns_buffer_begin(buf), + sldns_buffer_limit(buf)); + sldns_buffer_flip(proxy_buf); + buf = proxy_buf; + } + /* send it */ if(!udp) { - len = (uint16_t)sldns_buffer_limit(buf); + uint16_t len = (uint16_t)sldns_buffer_limit(buf); len = htons(len); if(ssl) { if(SSL_write(ssl, (void*)&len, (int)sizeof(len)) <= 0) { @@ -167,7 +187,7 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, #ifndef USE_WINSOCK perror("send() len failed"); #else - printf("send len: %s\n", + printf("send len: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); @@ -182,17 +202,20 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id, } } else { if(send(fd, (void*)sldns_buffer_begin(buf), - sldns_buffer_limit(buf), 0) < + sldns_buffer_limit(buf), 0) < (ssize_t)sldns_buffer_limit(buf)) { #ifndef USE_WINSOCK perror("send() data failed"); #else - printf("send data: %s\n", wsa_strerror(WSAGetLastError())); + printf("send data: %s\n", + wsa_strerror(WSAGetLastError())); #endif exit(1); } } + /* reset the proxy_buf for next packet */ + sldns_buffer_set_limit(proxy_buf, proxy_buf_limit); free(qinfo.qname); } @@ -224,7 +247,7 @@ recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf) #ifndef USE_WINSOCK perror("read() len failed"); #else - printf("read len: %s\n", + printf("read len: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); @@ -243,12 +266,12 @@ recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf) if(r != (int)len) fatal_exit("ssl_read %d of %d", r, len); } else { - if(recv(fd, (void*)sldns_buffer_begin(buf), len, 0) < + if(recv(fd, (void*)sldns_buffer_begin(buf), len, 0) < (ssize_t)len) { #ifndef USE_WINSOCK perror("read() data failed"); #else - printf("read data: %s\n", + printf("read data: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); @@ -257,12 +280,12 @@ recv_one(int fd, int udp, SSL* ssl, sldns_buffer* buf) } else { ssize_t l; sldns_buffer_clear(buf); - if((l=recv(fd, (void*)sldns_buffer_begin(buf), + if((l=recv(fd, (void*)sldns_buffer_begin(buf), sldns_buffer_capacity(buf), 0)) < 0) { #ifndef USE_WINSOCK perror("read() data failed"); #else - printf("read data: %s\n", + printf("read data: %s\n", wsa_strerror(WSAGetLastError())); #endif exit(1); @@ -324,17 +347,40 @@ static int get_random(void) return (int)arc4random(); } +/* parse the pp2_client and populate the proxy_buffer + * It doesn't populate the destination parts. */ +static int parse_pp2_client(const char* pp2_client, int udp, + sldns_buffer* proxy_buf) +{ + struct sockaddr_storage pp2_addr; + socklen_t pp2_addrlen = 0; + memset(&pp2_addr, 0, sizeof(pp2_addr)); + if(*pp2_client == 0) return 0; + if(!extstrtoaddr(pp2_client, &pp2_addr, &pp2_addrlen, UNBOUND_DNS_PORT)) { + printf("fatal: bad proxy client specs '%s'\n", pp2_client); + exit(1); + } + sldns_buffer_clear(proxy_buf); + pp2_write_to_buf(proxy_buf, &pp2_addr, !udp); + sldns_buffer_flip(proxy_buf); + return 1; +} + /** send the TCP queries and print answers */ static void -send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival, - int delay, int num, char** qs) +send_em(const char* svr, const char* pp2_client, int udp, int usessl, + int noanswer, int onarrival, int delay, int num, char** qs) { sldns_buffer* buf = sldns_buffer_new(65553); - int fd = open_svr(svr, udp); - int i, wait_results = 0; + sldns_buffer* proxy_buf = sldns_buffer_new(65553); + struct sockaddr_storage svr_addr; + socklen_t svr_addrlen; + int fd = open_svr(svr, udp, &svr_addr, &svr_addrlen); + int i, wait_results = 0, pp2_parsed; SSL_CTX* ctx = NULL; SSL* ssl = NULL; if(!buf) fatal_exit("out of memory"); + pp2_parsed = parse_pp2_client(pp2_client, udp, proxy_buf); if(usessl) { ctx = connect_sslctx_create(NULL, NULL, NULL, 0); if(!ctx) fatal_exit("cannot create ssl ctx"); @@ -361,6 +407,28 @@ send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival, } } } + /* Send the PROXYv2 information once per stream */ + if(!udp && pp2_parsed) { + if(ssl) { + if(SSL_write(ssl, (void*)sldns_buffer_begin(proxy_buf), + (int)sldns_buffer_limit(proxy_buf)) <= 0) { + log_crypto_err("cannot SSL_write"); + exit(1); + } + } else { + if(send(fd, (void*)sldns_buffer_begin(proxy_buf), + sldns_buffer_limit(proxy_buf), 0) < + (ssize_t)sldns_buffer_limit(proxy_buf)) { +#ifndef USE_WINSOCK + perror("send() data failed"); +#else + printf("send data: %s\n", + wsa_strerror(WSAGetLastError())); +#endif + exit(1); + } + } + } for(i=0; inode_count ); @@ -189,7 +189,7 @@ static void consistency_test(void) for (i = 0; i < 1000; i++) { l = randomkey(&k, 128); elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); - addrtree_insert(t, k, l, 64, elem, i + 10, i); + addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); free(k); unit_assert( !addrtree_inconsistent(t) ); } @@ -201,7 +201,7 @@ static void consistency_test(void) for (i = 0; i < 1000; i++) { l = randomkey(&k, 128); elem = (struct reply_info *) calloc(1, sizeof(struct reply_info)); - addrtree_insert(t, k, l, 64, elem, i + 10, i); + addrtree_insert(t, k, l, 64, elem, i + 10, i, 0); unit_assert( t->node_count <= 27); free(k); unit_assert( !addrtree_inconsistent(t) ); diff --git a/usr.sbin/unbound/testcode/unittcpreuse.c b/usr.sbin/unbound/testcode/unittcpreuse.c index 087c6c1b9d5..5f45a4b456f 100644 --- a/usr.sbin/unbound/testcode/unittcpreuse.c +++ b/usr.sbin/unbound/testcode/unittcpreuse.c @@ -44,6 +44,8 @@ #include "util/random.h" #include "services/outside_network.h" +#define MAX_TCP_WAITING_NODES 5 + /** add number of new IDs to the reuse tree, randomly chosen */ static void tcpid_addmore(struct reuse_tcp* reuse, struct outside_network* outnet, unsigned int addnum) @@ -228,9 +230,260 @@ static void tcp_reuse_tree_list_test(void) free(outnet.tcp_conns); } +static void check_waiting_tcp_list(struct outside_network* outnet, + struct waiting_tcp* first, struct waiting_tcp* last, size_t total) +{ + size_t i, j; + struct waiting_tcp* w = outnet->tcp_wait_first; + struct waiting_tcp* n = NULL; + if(first) unit_assert(outnet->tcp_wait_first == first); + if(last) unit_assert(outnet->tcp_wait_last == last && !last->next_waiting); + for(i=0; w; i++) { + unit_assert(ion_tcp_waiting_list); + n = w->next_waiting; + for(j=0; n; j++) { + unit_assert(jnext_waiting; + } + w = w->next_waiting; + } +} + +/** clear the tcp waiting list */ +static void waiting_tcp_list_clear(struct outside_network* outnet) +{ + struct waiting_tcp* w = outnet->tcp_wait_first, *n = NULL; + if(!w) return; + unit_assert(outnet->tcp_wait_first); + unit_assert(outnet->tcp_wait_last); + while(w) { + n = w->next_waiting; + w->on_tcp_waiting_list = 0; + w->next_waiting = (struct waiting_tcp*)1; /* In purpose faux value */ + w = n; + } + outnet->tcp_wait_first = NULL; + outnet->tcp_wait_last = NULL; +} + +/** check removal of the waiting_tcp element on the given position of total + * elements */ +static void check_waiting_tcp_removal(int is_pop, + struct outside_network* outnet, struct waiting_tcp* store, + size_t position, size_t total) +{ + size_t i; + struct waiting_tcp* w; + waiting_tcp_list_clear(outnet); + for(i=0; itcp_wait_first; + for(i=0; inext_waiting; + } + unit_assert(w); /* please clang-analyser */ + outnet_waiting_tcp_list_remove(outnet, w); + } + unit_assert(!(w->on_tcp_waiting_list || w->next_waiting)); + + if(position == 0 && total == 1) { + /* the list should be empty */ + check_waiting_tcp_list(outnet, NULL, NULL, total-1); + } else if(position == 0) { + /* first element should be gone */ + check_waiting_tcp_list(outnet, &store[1], &store[total-1], total-1); + } else if(position == total - 1) { + /* last element should be gone */ + check_waiting_tcp_list(outnet, &store[0], &store[total-2], total-1); + } else { + /* an element should be gone */ + check_waiting_tcp_list(outnet, &store[0], &store[total-1], total-1); + } +} + +static void waiting_tcp_list_test(void) +{ + size_t i = 0; + struct outside_network outnet; + struct waiting_tcp* w, *t = NULL; + struct waiting_tcp store[MAX_TCP_WAITING_NODES]; + memset(&outnet, 0, sizeof(outnet)); + memset(&store, 0, sizeof(store)); + + /* Check add first on empty list */ + unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add_first"); + t = &store[i]; + outnet_waiting_tcp_list_add_first(&outnet, t, 0); + check_waiting_tcp_list(&outnet, t, t, 1); + + /* Check add */ + unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add"); + for(i=1; iwrite_wait_first; + struct waiting_tcp* n = NULL; + if(first) unit_assert(reuse->write_wait_first == first && !first->write_wait_prev); + if(last) unit_assert(reuse->write_wait_last == last && !last->write_wait_next); + /* check one way */ + for(i=0; w; i++) { + unit_assert(iwrite_wait_queued); + n = w->write_wait_next; + for(j=0; n; j++) { + unit_assert(jwrite_wait_next; + } + w = w->write_wait_next; + } + /* check the other way */ + w = reuse->write_wait_last; + for(i=0; w; i++) { + unit_assert(iwrite_wait_queued); + n = w->write_wait_prev; + for(j=0; n; j++) { + unit_assert(jwrite_wait_prev; + } + w = w->write_wait_prev; + } +} + +/** clear the tcp waiting list */ +static void reuse_write_wait_clear(struct reuse_tcp* reuse) +{ + struct waiting_tcp* w = reuse->write_wait_first, *n = NULL; + if(!w) return; + unit_assert(reuse->write_wait_first); + unit_assert(reuse->write_wait_last); + while(w) { + n = w->write_wait_next; + w->write_wait_queued = 0; + w->write_wait_next = (struct waiting_tcp*)1; /* In purpose faux value */ + w->write_wait_prev = (struct waiting_tcp*)1; /* In purpose faux value */ + w = n; + } + reuse->write_wait_first = NULL; + reuse->write_wait_last = NULL; +} + +/** check removal of the reuse_write_wait element on the given position of total + * elements */ +static void check_reuse_write_wait_removal(int is_pop, + struct reuse_tcp* reuse, struct waiting_tcp* store, + size_t position, size_t total) +{ + size_t i; + struct waiting_tcp* w; + reuse_write_wait_clear(reuse); + for(i=0; iwrite_wait_first; + for(i=0; iwrite_wait_next; + reuse_write_wait_remove(reuse, w); + } + unit_assert(!(w->write_wait_queued || w->write_wait_next || w->write_wait_prev)); + + if(position == 0 && total == 1) { + /* the list should be empty */ + check_reuse_write_wait(reuse, NULL, NULL, total-1); + } else if(position == 0) { + /* first element should be gone */ + check_reuse_write_wait(reuse, &store[1], &store[total-1], total-1); + } else if(position == total - 1) { + /* last element should be gone */ + check_reuse_write_wait(reuse, &store[0], &store[total-2], total-1); + } else { + /* an element should be gone */ + check_reuse_write_wait(reuse, &store[0], &store[total-1], total-1); + } +} + +static void reuse_write_wait_test(void) +{ + size_t i; + struct reuse_tcp reuse; + struct waiting_tcp store[MAX_TCP_WAITING_NODES]; + struct waiting_tcp* w; + memset(&reuse, 0, sizeof(reuse)); + memset(&store, 0, sizeof(store)); + + /* Check adding */ + unit_show_func("services/outside_network.c", "reuse_write_wait_push_back"); + for(i=0; isin_family; + if(sldns_buffer_remaining(buf) < + PP2_HEADER_SIZE + (af==AF_INET?12:36)) { + return 0; + } + /* sig */ + sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN); + /* version and command */ + sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY); + if(af==AF_INET) { + /* family and protocol */ + sldns_buffer_write_u8(buf, + (PP2_AF_INET<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + /* length */ + sldns_buffer_write_u16(buf, 12); + /* src addr */ + sldns_buffer_write(buf, + &((struct sockaddr_in*)src)->sin_addr.s_addr, 4); + /* dst addr */ + sldns_buffer_write_u32(buf, 0); + /* src port */ + sldns_buffer_write(buf, + &((struct sockaddr_in*)src)->sin_port, 2); + /* dst port */ + sldns_buffer_write_u16(buf, 0); + } else { + /* family and protocol */ + sldns_buffer_write_u8(buf, + (PP2_AF_INET6<<4) | + (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); + /* length */ + sldns_buffer_write_u16(buf, 36); + /* src addr */ + sldns_buffer_write(buf, + &((struct sockaddr_in6*)src)->sin6_addr, 16); + /* dst addr */ + sldns_buffer_set_at(buf, + sldns_buffer_position(buf), 0, 16); + sldns_buffer_skip(buf, 16); + /* src port */ + sldns_buffer_write(buf, + &((struct sockaddr_in6*)src)->sin6_port, 2); + /* dst port */ + sldns_buffer_write_u16(buf, 0); + } + return 1; +} + +struct pp2_header* +pp2_read_header(struct sldns_buffer* buf) +{ + size_t size; + struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(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; + } + /* 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; + } + /* 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; + } + /* 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; + } + /* 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; + } + /* We have a correct header */ + return header; +} diff --git a/usr.sbin/unbound/util/proxy_protocol.h b/usr.sbin/unbound/util/proxy_protocol.h new file mode 100644 index 00000000000..13cab9d7438 --- /dev/null +++ b/usr.sbin/unbound/util/proxy_protocol.h @@ -0,0 +1,131 @@ +/* + * util/proxy_protocol.h - PROXY protocol + * + * Copyright (c) 2022, NLnet Labs. All rights reserved. + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the NLNET LABS nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * This file contains PROXY protocol structs and functions. + * Only v2 is supported. TLVs are not currently supported. + */ +#ifndef PROXY_PROTOCOL_H +#define PROXY_PROTOCOL_H + +#include "sldns/sbuffer.h" + +/** PROXYv2 minimum header size */ +#define PP2_HEADER_SIZE 16 + +/** PROXYv2 header signature */ +#define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" +#define PP2_SIG_LEN 12 + +/** PROXYv2 version */ +#define PP2_VERSION 0x2 + +/** + * PROXYv2 command. + */ +enum pp2_command { + PP2_CMD_LOCAL = 0x0, + PP2_CMD_PROXY = 0x1 +}; + +/** + * PROXYv2 address family. + */ +enum pp2_af { + PP2_AF_UNSPEC = 0x0, + PP2_AF_INET = 0x1, + PP2_AF_INET6 = 0x2, + PP2_AF_UNIX = 0x3 +}; + +/** + * PROXYv2 protocol. + */ +enum pp2_protocol { + PP2_PROT_UNSPEC = 0x0, + PP2_PROT_STREAM = 0x1, + PP2_PROT_DGRAM = 0x2 +}; + +/** + * PROXYv2 header. + */ +struct pp2_header { + uint8_t sig[PP2_SIG_LEN]; + uint8_t ver_cmd; + uint8_t fam_prot; + uint16_t len; + union { + struct { /* for TCP/UDP over IPv4, len = 12 */ + uint32_t src_addr; + uint32_t dst_addr; + uint16_t src_port; + uint16_t dst_port; + } addr4; + struct { /* for TCP/UDP over IPv6, len = 36 */ + uint8_t src_addr[16]; + uint8_t dst_addr[16]; + uint16_t src_port; + uint16_t dst_port; + } addr6; + struct { /* for AF_UNIX sockets, len = 216 */ + uint8_t src_addr[108]; + uint8_t dst_addr[108]; + } addru; + } addr; +}; + +/** + * Write a PROXYv2 header at the current position of the buffer. + * @param buf: the buffer to write to. + * @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); + +/** + * 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. + */ +struct pp2_header* pp2_read_header(struct sldns_buffer* buf); + +#endif /* PROXY_PROTOCOL_H */ -- 2.20.1