* -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;
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;
}
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;
}
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",
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;
}
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"),
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,
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);
}
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"
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
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)) {
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",
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;
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)) {
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)
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))
# 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
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
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
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
dir=$name.$$
result=result.$name
done=.done-$name
+skip=.skip-$name
success="no"
if test -x "`which bash`"; then
shell="bash"
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
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
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
rm -rf $dir
fi
else
- if test $success == "no"; then
+ if test $success = "no"; then
exit 1
fi
exit 0
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);
}
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);
} 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;
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);
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);
#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"
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");
/** 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
#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
/** 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) {
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) {
#ifndef USE_WINSOCK
perror("send() len failed");
#else
- printf("send len: %s\n",
+ printf("send len: %s\n",
wsa_strerror(WSAGetLastError()));
#endif
exit(1);
}
} 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);
}
#ifndef USE_WINSOCK
perror("read() len failed");
#else
- printf("read len: %s\n",
+ printf("read len: %s\n",
wsa_strerror(WSAGetLastError()));
#endif
exit(1);
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);
} 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);
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");
}
}
}
+ /* 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
#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 */
}
sock_close(fd);
sldns_buffer_free(buf);
+ sldns_buffer_free(proxy_buf);
printf("orderly exit\n");
}
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;
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;
(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();
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 );
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) );
}
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) );
#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)
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();
}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */