Merge branch 'master' into disable-edns-do

This commit is contained in:
W.C.A. Wijngaards 2023-10-04 13:34:47 +02:00
commit eff3e01ec3
15 changed files with 475 additions and 89 deletions

View file

@ -66,6 +66,7 @@
#include "util/data/msgencode.h" #include "util/data/msgencode.h"
#include "util/data/dname.h" #include "util/data/dname.h"
#include "util/fptr_wlist.h" #include "util/fptr_wlist.h"
#include "util/proxy_protocol.h"
#include "util/tube.h" #include "util/tube.h"
#include "util/edns.h" #include "util/edns.h"
#include "util/timeval_func.h" #include "util/timeval_func.h"
@ -2317,6 +2318,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
worker->env.cfg->stat_interval); worker->env.cfg->stat_interval);
worker_restart_timer(worker); worker_restart_timer(worker);
} }
pp_init(&sldns_write_uint16, &sldns_write_uint32);
return 1; return 1;
} }

View file

@ -1,3 +1,9 @@
3 October 2023: George
- Merge #881: Generalise the proxy protocol code.
2 October 2023: George
- Fix misplaced comment.
22 September 2023: Wouter 22 September 2023: Wouter
- Fix #942: 1.18.0 libunbound DNS regression when built without - Fix #942: 1.18.0 libunbound DNS regression when built without
OpenSSL. OpenSSL.

View file

@ -62,6 +62,7 @@
#include "util/random.h" #include "util/random.h"
#include "util/config_file.h" #include "util/config_file.h"
#include "util/netevent.h" #include "util/netevent.h"
#include "util/proxy_protocol.h"
#include "util/storage/lookup3.h" #include "util/storage/lookup3.h"
#include "util/storage/slabhash.h" #include "util/storage/slabhash.h"
#include "util/net_help.h" #include "util/net_help.h"
@ -265,6 +266,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
w->env->kill_sub = &mesh_state_delete; w->env->kill_sub = &mesh_state_delete;
w->env->detect_cycle = &mesh_detect_cycle; w->env->detect_cycle = &mesh_detect_cycle;
comm_base_timept(w->base, &w->env->now, &w->env->now_tv); comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
pp_init(&sldns_write_uint16, &sldns_write_uint32);
return w; return w;
} }

View file

@ -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.reuse != (struct reuse_tcp*)result);
log_assert(&key_p != ((struct reuse_tcp*)result)->pending); 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 /* It is possible that we search for something before the first element
* in the tree. Replace a null pointer with 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); result = rbtree_first(&outnet->tcp_reuse);
} }
/* not found, return null */
if(!result || result == RBTREE_NULL) if(!result || result == RBTREE_NULL)
return NULL; return NULL;

View file

@ -61,6 +61,17 @@ Specify the server to send the queries to. If not specified localhost (127.0.0.1
.B \-d \fIsecs .B \-d \fIsecs
Delay after the connection before sending query. This tests the timeout Delay after the connection before sending query. This tests the timeout
on the other side, eg. if shorter the connection is closed. 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.
.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" .SH "EXAMPLES"
.LP .LP
Some examples of use. Some examples of use.

View file

@ -79,6 +79,8 @@ static void usage(char* argv[])
printf("-d secs delay after connection before sending query\n"); printf("-d secs delay after connection before sending query\n");
printf("-s use ssl\n"); printf("-s use ssl\n");
printf("-h this help text\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); exit(1);
} }
@ -115,6 +117,29 @@ open_svr(const char* svr, int udp, struct sockaddr_storage* addr,
return fd; 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 */ /** write a query over the TCP fd */
static void 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,
@ -123,6 +148,8 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
{ {
struct query_info qinfo; struct query_info qinfo;
size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf); size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf);
int have_serial = 0, is_notify = 0;
uint32_t serial = 0;
/* qname */ /* qname */
qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len); qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
if(!qinfo.qname) { if(!qinfo.qname) {
@ -130,12 +157,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
exit(1); exit(1);
} }
/* qtype and qclass */ /* qtype */
qinfo.qtype = sldns_get_rr_type_by_name(strtype); if(strncasecmp(strtype, "IXFR=", 5) == 0) {
if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) { serial = (uint32_t)atoi(strtype+5);
printf("cannot parse query type: '%s'\n", strtype); have_serial = 1;
exit(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);
if(qinfo.qtype == 0 && strcmp(strtype, "TYPE0") != 0) {
printf("cannot parse query type: '%s'\n", strtype);
exit(1);
}
} }
/* qclass */
qinfo.qclass = sldns_get_rr_class_by_name(strclass); qinfo.qclass = sldns_get_rr_class_by_name(strclass);
if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) { if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) {
printf("cannot parse query class: '%s'\n", strclass); printf("cannot parse query class: '%s'\n", strclass);
@ -150,6 +192,21 @@ 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, 0, id);
sldns_buffer_write_u16_at(buf, 2, BIT_RD); 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. */
write_soa_serial_to_buf(buf, &qinfo, serial);
LDNS_NSCOUNT_SET(sldns_buffer_begin(buf), 1);
}
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) { if(1) {
/* add EDNS DO */ /* add EDNS DO */
struct edns_data edns; struct edns_data edns;
@ -361,6 +418,7 @@ static int parse_pp2_client(const char* pp2_client, int udp,
sldns_buffer* proxy_buf) sldns_buffer* proxy_buf)
{ {
struct sockaddr_storage pp2_addr; struct sockaddr_storage pp2_addr;
size_t bytes_written;
socklen_t pp2_addrlen = 0; socklen_t pp2_addrlen = 0;
memset(&pp2_addr, 0, sizeof(pp2_addr)); memset(&pp2_addr, 0, sizeof(pp2_addr));
if(*pp2_client == 0) return 0; if(*pp2_client == 0) return 0;
@ -369,7 +427,9 @@ static int parse_pp2_client(const char* pp2_client, int udp,
exit(1); exit(1);
} }
sldns_buffer_clear(proxy_buf); 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); sldns_buffer_flip(proxy_buf);
return 1; return 1;
} }
@ -541,6 +601,8 @@ int main(int argc, char** argv)
break; break;
case 'p': case 'p':
pp2_client = optarg; pp2_client = optarg;
pp_init(&sldns_write_uint16,
&sldns_write_uint32);
break; break;
case 'a': case 'a':
onarrival = 1; onarrival = 1;

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -892,15 +892,18 @@ static int udp_recv_needs_log(int err)
static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep, static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
int stream) { int stream) {
size_t size; size_t size;
struct pp2_header *header = pp2_read_header(buf); struct pp2_header *header;
if(header == NULL) return 0; 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); size = PP2_HEADER_SIZE + ntohs(header->len);
if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) { if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) {
/* A connection from the proxy itself. /* A connection from the proxy itself.
* No need to do anything with addresses. */ * No need to do anything with addresses. */
goto done; goto done;
} }
if(header->fam_prot == 0x00) { if(header->fam_prot == PP2_UNSPEC_UNSPEC) {
/* Unspecified family and protocol. This could be used for /* Unspecified family and protocol. This could be used for
* health checks by proxies. * health checks by proxies.
* No need to do anything with addresses. */ * No need to do anything with addresses. */
@ -908,8 +911,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
} }
/* Read the proxied address */ /* Read the proxied address */
switch(header->fam_prot) { switch(header->fam_prot) {
case 0x11: /* AF_INET|STREAM */ case PP2_INET_STREAM:
case 0x12: /* AF_INET|DGRAM */ case PP2_INET_DGRAM:
{ {
struct sockaddr_in* addr = struct sockaddr_in* addr =
(struct sockaddr_in*)&rep->client_addr; (struct sockaddr_in*)&rep->client_addr;
@ -920,8 +923,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
} }
/* Ignore the destination address; it should be us. */ /* Ignore the destination address; it should be us. */
break; break;
case 0x21: /* AF_INET6|STREAM */ case PP2_INET6_STREAM:
case 0x22: /* AF_INET6|DGRAM */ case PP2_INET6_DGRAM:
{ {
struct sockaddr_in6* addr = struct sockaddr_in6* addr =
(struct sockaddr_in6*)&rep->client_addr; (struct sockaddr_in6*)&rep->client_addr;
@ -934,6 +937,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
} }
/* Ignore the destination address; it should be us. */ /* Ignore the destination address; it should be us. */
break; break;
default:
log_err("proxy_protocol: unsupported family and "
"protocol 0x%x", (int)header->fam_prot);
return 0;
} }
rep->is_proxied = 1; rep->is_proxied = 1;
done: done:
@ -1813,19 +1820,25 @@ ssl_handle_read(struct comm_point* c)
return 0; return 0;
} }
c->tcp_byte_count += r; c->tcp_byte_count += r;
sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1; if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_init; c->pp2_header_state = pp2_header_init;
} }
} }
if(c->pp2_header_state == pp2_header_init) { if(c->pp2_header_state == pp2_header_init) {
header = pp2_read_header(c->buffer); int err;
if(!header) { err = pp2_read_header(
sldns_buffer_begin(c->buffer),
sldns_buffer_limit(c->buffer));
if(err) {
log_err("proxy_protocol: could not parse " log_err("proxy_protocol: could not parse "
"PROXYv2 header"); "PROXYv2 header (%s)",
pp_lookup_error(err));
return 0; return 0;
} }
header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
want_read_size = ntohs(header->len); want_read_size = ntohs(header->len);
if(sldns_buffer_remaining(c->buffer) < if(sldns_buffer_limit(c->buffer) <
PP2_HEADER_SIZE + want_read_size) { PP2_HEADER_SIZE + want_read_size) {
log_err_addr("proxy_protocol: not enough " log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "", "buffer size to read PROXYv2 header", "",
@ -1874,6 +1887,7 @@ ssl_handle_read(struct comm_point* c)
return 0; return 0;
} }
c->tcp_byte_count += r; c->tcp_byte_count += r;
sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1; if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_done; c->pp2_header_state = pp2_header_done;
} }
@ -1884,6 +1898,7 @@ ssl_handle_read(struct comm_point* c)
c->repinfo.remote_addrlen); c->repinfo.remote_addrlen);
return 0; return 0;
} }
sldns_buffer_flip(c->buffer);
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
log_err_addr("proxy_protocol: could not consume " log_err_addr("proxy_protocol: could not consume "
"PROXYv2 header", "", &c->repinfo.remote_addr, "PROXYv2 header", "", &c->repinfo.remote_addr,
@ -2205,19 +2220,25 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
goto recv_error_initial; goto recv_error_initial;
} }
c->tcp_byte_count += r; c->tcp_byte_count += r;
sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1; if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_init; c->pp2_header_state = pp2_header_init;
} }
} }
if(c->pp2_header_state == pp2_header_init) { if(c->pp2_header_state == pp2_header_init) {
header = pp2_read_header(c->buffer); int err;
if(!header) { err = pp2_read_header(
sldns_buffer_begin(c->buffer),
sldns_buffer_limit(c->buffer));
if(err) {
log_err("proxy_protocol: could not parse " log_err("proxy_protocol: could not parse "
"PROXYv2 header"); "PROXYv2 header (%s)",
pp_lookup_error(err));
return 0; return 0;
} }
header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
want_read_size = ntohs(header->len); want_read_size = ntohs(header->len);
if(sldns_buffer_remaining(c->buffer) < if(sldns_buffer_limit(c->buffer) <
PP2_HEADER_SIZE + want_read_size) { PP2_HEADER_SIZE + want_read_size) {
log_err_addr("proxy_protocol: not enough " log_err_addr("proxy_protocol: not enough "
"buffer size to read PROXYv2 header", "", "buffer size to read PROXYv2 header", "",
@ -2244,6 +2265,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
goto recv_error; goto recv_error;
} }
c->tcp_byte_count += r; c->tcp_byte_count += r;
sldns_buffer_skip(c->buffer, r);
if(c->tcp_byte_count != current_read_size) return 1; if(c->tcp_byte_count != current_read_size) return 1;
c->pp2_header_state = pp2_header_done; c->pp2_header_state = pp2_header_done;
} }
@ -2254,6 +2276,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
c->repinfo.remote_addrlen); c->repinfo.remote_addrlen);
return 0; return 0;
} }
sldns_buffer_flip(c->buffer);
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) { if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
log_err_addr("proxy_protocol: could not consume " log_err_addr("proxy_protocol: could not consume "
"PROXYv2 header", "", &c->repinfo.remote_addr, "PROXYv2 header", "", &c->repinfo.remote_addr,

View file

@ -38,102 +38,162 @@
* *
* This file contains PROXY protocol functions. * This file contains PROXY protocol functions.
*/ */
#include "config.h"
#include "util/log.h"
#include "util/proxy_protocol.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,
#ifdef INET6
struct sockaddr_storage* src,
#else
struct sockaddr_in* src,
#endif
int stream) int stream)
{ {
int af; int af;
size_t expected_size;
if(!src) return 0; if(!src) return 0;
af = (int)((struct sockaddr_in*)src)->sin_family; af = (int)((struct sockaddr_in*)src)->sin_family;
if(sldns_buffer_remaining(buf) < expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
PP2_HEADER_SIZE + (af==AF_INET?12:36)) { if(buflen < expected_size) {
return 0; return 0;
} }
/* sig */ /* sig */
sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN); memcpy(buf, PP2_SIG, PP2_SIG_LEN);
buf += PP2_SIG_LEN;
/* version and command */ /* version and command */
sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY); *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
if(af==AF_INET) { buf++;
switch(af) {
case AF_INET:
/* family and protocol */ /* family and protocol */
sldns_buffer_write_u8(buf, *buf = (PP2_AF_INET<<4) |
(PP2_AF_INET<<4) | (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM)); buf++;
/* length */ /* length */
sldns_buffer_write_u16(buf, 12); (*pp_data.write_uint16)(buf, 12);
buf += 2;
/* src addr */ /* src addr */
sldns_buffer_write(buf, memcpy(buf,
&((struct sockaddr_in*)src)->sin_addr.s_addr, 4); &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
buf += 4;
/* dst addr */ /* dst addr */
sldns_buffer_write_u32(buf, 0); (*pp_data.write_uint32)(buf, 0);
buf += 4;
/* src port */ /* src port */
sldns_buffer_write(buf, memcpy(buf,
&((struct sockaddr_in*)src)->sin_port, 2); &((struct sockaddr_in*)src)->sin_port, 2);
/* dst port */ buf += 2;
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 */ /* 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 */ /* dst port */
sldns_buffer_write_u16(buf, 0); (*pp_data.write_uint16)(buf, 12);
break;
#ifdef INET6
case AF_INET6:
/* family and protocol */
*buf = (PP2_AF_INET6<<4) |
(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
buf++;
/* length */
(*pp_data.write_uint16)(buf, 36);
buf += 2;
/* src addr */
memcpy(buf,
&((struct sockaddr_in6*)src)->sin6_addr, 16);
buf += 16;
/* dst addr */
memset(buf, 0, 16);
buf += 16;
/* src port */
memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
buf += 2;
/* dst port */
(*pp_data.write_uint16)(buf, 0);
break;
#endif /* INET6 */
case AF_UNIX:
/* fallthrough */
default:
return 0;
} }
return 1; return expected_size;
} }
struct pp2_header* int
pp2_read_header(struct sldns_buffer* buf) pp2_read_header(uint8_t* buf, size_t buflen)
{ {
size_t size; 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. */ /* Try to fail all the unsupported cases first. */
if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) { if(buflen < PP2_HEADER_SIZE) {
log_err("proxy_protocol: not enough space for header"); return PP_PARSE_SIZE;
return NULL;
} }
/* Check for PROXYv2 header */ /* Check for PROXYv2 header */
if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 || if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) { ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
log_err("proxy_protocol: could not match PROXYv2 header"); return PP_PARSE_WRONG_HEADERv2;
return NULL;
} }
/* Check the length */ /* Check the length */
size = PP2_HEADER_SIZE + ntohs(header->len); size = PP2_HEADER_SIZE + ntohs(header->len);
if(sldns_buffer_remaining(buf) < size) { if(buflen < size) {
log_err("proxy_protocol: not enough space for header"); return PP_PARSE_SIZE;
return NULL;
} }
/* Check for supported commands */ /* Check for supported commands */
if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL && if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
(header->ver_cmd & 0xF) != PP2_CMD_PROXY) { (header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
log_err("proxy_protocol: unsupported command"); return PP_PARSE_UNKNOWN_CMD;
return NULL;
} }
/* Check for supported family and protocol */ /* Check for supported family and protocol */
if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ && if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
header->fam_prot != 0x11 /* AF_INET|STREAM */ && header->fam_prot != PP2_INET_STREAM &&
header->fam_prot != 0x12 /* AF_INET|DGRAM */ && header->fam_prot != PP2_INET_DGRAM &&
header->fam_prot != 0x21 /* AF_INET6|STREAM */ && header->fam_prot != PP2_INET6_STREAM &&
header->fam_prot != 0x22 /* AF_INET6|DGRAM */) { header->fam_prot != PP2_INET6_DGRAM &&
log_err("proxy_protocol: unsupported family and protocol"); header->fam_prot != PP2_UNIX_STREAM &&
return NULL; header->fam_prot != PP2_UNIX_DGRAM) {
return PP_PARSE_UNKNOWN_FAM_PROT;
} }
/* We have a correct header */ /* We have a correct header */
return header; return PP_PARSE_NOERROR;
} }

View file

@ -42,7 +42,7 @@
#ifndef PROXY_PROTOCOL_H #ifndef PROXY_PROTOCOL_H
#define PROXY_PROTOCOL_H #define PROXY_PROTOCOL_H
#include "sldns/sbuffer.h" #include "config.h"
/** PROXYv2 minimum header size */ /** PROXYv2 minimum header size */
#define PP2_HEADER_SIZE 16 #define PP2_HEADER_SIZE 16
@ -51,11 +51,11 @@
#define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" #define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
#define PP2_SIG_LEN 12 #define PP2_SIG_LEN 12
/** PROXYv2 version */ /** PROXYv2 version (protocol value) */
#define PP2_VERSION 0x2 #define PP2_VERSION 0x2
/** /**
* PROXYv2 command. * PROXYv2 command (protocol value).
*/ */
enum pp2_command { enum pp2_command {
PP2_CMD_LOCAL = 0x0, PP2_CMD_LOCAL = 0x0,
@ -63,7 +63,7 @@ enum pp2_command {
}; };
/** /**
* PROXYv2 address family. * PROXYv2 address family (protocol value).
*/ */
enum pp2_af { enum pp2_af {
PP2_AF_UNSPEC = 0x0, PP2_AF_UNSPEC = 0x0,
@ -73,7 +73,7 @@ enum pp2_af {
}; };
/** /**
* PROXYv2 protocol. * PROXYv2 protocol (protocol value).
*/ */
enum pp2_protocol { enum pp2_protocol {
PP2_PROT_UNSPEC = 0x0, PP2_PROT_UNSPEC = 0x0,
@ -81,6 +81,19 @@ enum pp2_protocol {
PP2_PROT_DGRAM = 0x2 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. * PROXYv2 header.
*/ */
@ -109,23 +122,56 @@ struct pp2_header {
} addr; } 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. * 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 src: the source address.
* @param stream: if the protocol is stream or datagram. * @param stream: if the protocol is stream or datagram.
* @return 1 on success, 0 on failure. * @return 1 on success, 0 on failure.
*/ */
int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src, size_t 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 stream);
/** /**
* Read a PROXYv2 header from the current position of the buffer. * Read a PROXYv2 header from the current position of the buffer.
* It does initial validation and returns a pointer to the buffer position on * It does initial validation and returns a pointer to the buffer position on
* success. * success.
* @param buf: the buffer to read from. * @param buf: pointer to the buffer data to read from.
* @return the pointer to the buffer position on success, NULL on error. * @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 */ #endif /* PROXY_PROTOCOL_H */