import unbound 1.17.0, ok florian
authorsthen <sthen@openbsd.org>
Thu, 20 Oct 2022 08:25:16 +0000 (08:25 +0000)
committersthen <sthen@openbsd.org>
Thu, 20 Oct 2022 08:25:16 +0000 (08:25 +0000)
14 files changed:
usr.sbin/unbound/dnstap/dtstream.c
usr.sbin/unbound/dnstap/unbound-dnstap-socket.c
usr.sbin/unbound/services/rpz.c
usr.sbin/unbound/testcode/delayer.c
usr.sbin/unbound/testcode/do-tests.sh
usr.sbin/unbound/testcode/fake_event.c
usr.sbin/unbound/testcode/mini_tdir.sh
usr.sbin/unbound/testcode/perf.c
usr.sbin/unbound/testcode/replay.c
usr.sbin/unbound/testcode/streamtcp.c
usr.sbin/unbound/testcode/unitecs.c
usr.sbin/unbound/testcode/unittcpreuse.c
usr.sbin/unbound/util/proxy_protocol.c [new file with mode: 0644]
usr.sbin/unbound/util/proxy_protocol.h [new file with mode: 0644]

index a1dd970..9153f04 100644 (file)
@@ -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;
        }
index 63292fb..3bf8894 100644 (file)
@@ -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",
index 77b6266..e876f3f 100644 (file)
@@ -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,
index e915961..647a4e2 100644 (file)
@@ -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);
        }
index 2a1cfc4..6599f9f 100755 (executable)
@@ -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
index be06a47..efb22a6 100644 (file)
@@ -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))
index 6bbece8..624ecdf 100755 (executable)
@@ -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
index 55d6483..7fb524e 100644 (file)
@@ -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);
        }
index 2487c14..43101d6 100644 (file)
@@ -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);
index ecc83c1..b2c0d53 100644 (file)
@@ -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; i<num; i+=3) {
                if (delay != 0) {
 #ifdef HAVE_SLEEP
@@ -370,8 +438,9 @@ send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival,
 #endif
                }
                printf("\nNext query is %s %s %s\n", qs[i], qs[i+1], qs[i+2]);
-               write_q(fd, udp, ssl, buf, (uint16_t)get_random(), qs[i],
-                       qs[i+1], qs[i+2]);
+               write_q(fd, udp, ssl, buf, (uint16_t)get_random(), proxy_buf,
+                       pp2_parsed,
+                       qs[i], qs[i+1], qs[i+2]);
                /* print at least one result */
                if(onarrival) {
                        wait_results += 1; /* one more answer to fetch */
@@ -390,6 +459,7 @@ send_em(const char* svr, int udp, int usessl, int noanswer, int onarrival,
        }
        sock_close(fd);
        sldns_buffer_free(buf);
+       sldns_buffer_free(proxy_buf);
        printf("orderly exit\n");
 }
 
@@ -418,10 +488,11 @@ extern int optind;
 extern char* optarg;
 
 /** main program for streamtcp */
-int main(int argc, char** argv) 
+int main(int argc, char** argv)
 {
        int c;
        const char* svr = "127.0.0.1";
+       const char* pp2_client = "";
        int udp = 0;
        int noanswer = 0;
        int onarrival = 0;
@@ -451,11 +522,14 @@ int main(int argc, char** argv)
        if(argc == 1) {
                usage(argv);
        }
-       while( (c=getopt(argc, argv, "af:hnsud:")) != -1) {
+       while( (c=getopt(argc, argv, "af:p:hnsud:")) != -1) {
                switch(c) {
                        case 'f':
                                svr = optarg;
                                break;
+                       case 'p':
+                               pp2_client = optarg;
+                               break;
                        case 'a':
                                onarrival = 1;
                                break;
@@ -508,7 +582,7 @@ int main(int argc, char** argv)
                (void)OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL);
 #endif
        }
-       send_em(svr, udp, usessl, noanswer, onarrival, delay, argc, argv);
+       send_em(svr, pp2_client, udp, usessl, noanswer, onarrival, delay, argc, argv);
        checklock_stop();
 #ifdef USE_WINSOCK
        WSACleanup();
index b240bfc..68d6907 100644 (file)
@@ -173,7 +173,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, timenow + 10, timenow);
+               addrtree_insert(t, k, l, 64, elem, timenow + 10, timenow, 0);
                /* This should always hold because no items ever expire. They
                 * could be overwritten, though. */
                unit_assert( count <= t->node_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) );
index 087c6c1..5f45a4b 100644 (file)
@@ -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(i<total); /* otherwise we are looping */
+               unit_assert(w->on_tcp_waiting_list);
+               n = w->next_waiting;
+               for(j=0; n; j++) {
+                       unit_assert(j<total-i-1); /* otherwise we are looping */
+                       unit_assert(n != w);
+                       n = n->next_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; i<total; i++) {
+               outnet_waiting_tcp_list_add(outnet, &store[i], 0);
+       }
+       check_waiting_tcp_list(outnet, &store[0], &store[total-1], total);
+
+       if(is_pop) {
+               w = outnet_waiting_tcp_list_pop(outnet);
+               unit_assert(w); /* please clang-analyser */
+       } else {
+               w = outnet->tcp_wait_first;
+               for(i=0; i<position; i++) {
+                       unit_assert(w); /* please clang-analyser */
+                       w = w->next_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; i<MAX_TCP_WAITING_NODES-1; i++) {
+               w = &store[i];
+               outnet_waiting_tcp_list_add(&outnet, w, 0);
+       }
+       check_waiting_tcp_list(&outnet, t, w, MAX_TCP_WAITING_NODES-1);
+
+       /* Check add first on populated list */
+       unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_add_first");
+       w = &store[i];
+       t = outnet.tcp_wait_last;
+       outnet_waiting_tcp_list_add_first(&outnet, w, 0);
+       check_waiting_tcp_list(&outnet, w, t, MAX_TCP_WAITING_NODES);
+
+       /* Check removal */
+       unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_remove");
+       check_waiting_tcp_removal(0, &outnet, store, 2, 5);
+       check_waiting_tcp_removal(0, &outnet, store, 1, 3);
+       check_waiting_tcp_removal(0, &outnet, store, 0, 2);
+       check_waiting_tcp_removal(0, &outnet, store, 1, 2);
+       check_waiting_tcp_removal(0, &outnet, store, 0, 1);
+
+       /* Check pop */
+       unit_show_func("services/outside_network.c", "outnet_waiting_tcp_list_pop");
+       check_waiting_tcp_removal(1, &outnet, store, 0, 3);
+       check_waiting_tcp_removal(1, &outnet, store, 0, 2);
+       check_waiting_tcp_removal(1, &outnet, store, 0, 1);
+}
+
+static void check_reuse_write_wait(struct reuse_tcp* reuse,
+       struct waiting_tcp* first, struct waiting_tcp* last, size_t total)
+{
+       size_t i, j;
+       struct waiting_tcp* w = reuse->write_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(i<total); /* otherwise we are looping */
+               unit_assert(w->write_wait_queued);
+               n = w->write_wait_next;
+               for(j=0; n; j++) {
+                       unit_assert(j<total-i-1); /* otherwise we are looping */
+                       unit_assert(n != w);
+                       n = n->write_wait_next;
+               }
+               w = w->write_wait_next;
+       }
+       /* check the other way */
+       w = reuse->write_wait_last;
+       for(i=0; w; i++) {
+               unit_assert(i<total); /* otherwise we are looping */
+               unit_assert(w->write_wait_queued);
+               n = w->write_wait_prev;
+               for(j=0; n; j++) {
+                       unit_assert(j<total-i-1); /* otherwise we are looping */
+                       unit_assert(n != w);
+                       n = n->write_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; i<total; i++) {
+               reuse_write_wait_push_back(reuse, &store[i]);
+       }
+       check_reuse_write_wait(reuse, &store[0], &store[total-1], total);
+
+       if(is_pop) {
+               w = reuse_write_wait_pop(reuse);
+       } else {
+               w = reuse->write_wait_first;
+               for(i=0; i<position; i++) w = w->write_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; i<MAX_TCP_WAITING_NODES; i++) {
+               w = &store[i];
+               reuse_write_wait_push_back(&reuse, w);
+       }
+       check_reuse_write_wait(&reuse, &store[0], w, MAX_TCP_WAITING_NODES);
+
+       /* Check removal */
+       unit_show_func("services/outside_network.c", "reuse_write_wait_remove");
+       check_reuse_write_wait_removal(0, &reuse, store, 2, 5);
+       check_reuse_write_wait_removal(0, &reuse, store, 1, 3);
+       check_reuse_write_wait_removal(0, &reuse, store, 0, 2);
+       check_reuse_write_wait_removal(0, &reuse, store, 1, 2);
+       check_reuse_write_wait_removal(0, &reuse, store, 0, 1);
+
+       /* Check pop */
+       unit_show_func("services/outside_network.c", "reuse_write_wait_pop");
+       check_reuse_write_wait_removal(1, &reuse, store, 0, 3);
+       check_reuse_write_wait_removal(1, &reuse, store, 0, 2);
+       check_reuse_write_wait_removal(1, &reuse, store, 0, 1);
+}
+
 void tcpreuse_test(void)
 {
     unit_show_feature("tcp_reuse");
     tcpid_test();
     tcp_reuse_tree_list_test();
+    waiting_tcp_list_test();
+    reuse_write_wait_test();
 }
diff --git a/usr.sbin/unbound/util/proxy_protocol.c b/usr.sbin/unbound/util/proxy_protocol.c
new file mode 100644 (file)
index 0000000..757c514
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * util/proxy_protocol.c - event notification
+ *
+ * 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 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,
+       int stream)
+{
+       int af;
+       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)) {
+               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 (file)
index 0000000..13cab9d
--- /dev/null
@@ -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 */