mirror of
https://github.com/NLnetLabs/unbound.git
synced 2026-02-03 12:19:27 -05:00
- Merge #881: Generalise the proxy protocol code.
This commit is contained in:
commit
13d4504dfc
8 changed files with 297 additions and 88 deletions
|
|
@ -66,6 +66,7 @@
|
|||
#include "util/data/msgencode.h"
|
||||
#include "util/data/dname.h"
|
||||
#include "util/fptr_wlist.h"
|
||||
#include "util/proxy_protocol.h"
|
||||
#include "util/tube.h"
|
||||
#include "util/edns.h"
|
||||
#include "util/timeval_func.h"
|
||||
|
|
@ -2317,6 +2318,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
|
|||
worker->env.cfg->stat_interval);
|
||||
worker_restart_timer(worker);
|
||||
}
|
||||
pp_init(&sldns_write_uint16, &sldns_write_uint32);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
3 October 2023: George
|
||||
- Merge #881: Generalise the proxy protocol code.
|
||||
|
||||
2 October 2023: George
|
||||
- Fix misplaced comment.
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@
|
|||
#include "util/random.h"
|
||||
#include "util/config_file.h"
|
||||
#include "util/netevent.h"
|
||||
#include "util/proxy_protocol.h"
|
||||
#include "util/storage/lookup3.h"
|
||||
#include "util/storage/slabhash.h"
|
||||
#include "util/net_help.h"
|
||||
|
|
@ -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->detect_cycle = &mesh_detect_cycle;
|
||||
comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
|
||||
pp_init(&sldns_write_uint16, &sldns_write_uint32);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,17 @@ Specify the server to send the queries to. If not specified localhost (127.0.0.1
|
|||
.B \-d \fIsecs
|
||||
Delay after the connection before sending query. This tests the timeout
|
||||
on the other side, eg. if shorter the connection is closed.
|
||||
.TP
|
||||
.B \-p \fIclient
|
||||
Use proxy protocol to send the query. Specify the ipaddr@portnr of the client
|
||||
to include in PROXYv2.
|
||||
.TP
|
||||
.B IXFR=serial
|
||||
Pass the type of the query as IXFR=N to send an IXFR query with serial N.
|
||||
.TP
|
||||
.B NOTIFY[=serial]
|
||||
Pass the type of the query as NOTIFY[=N] to send a notify packet. The serial N
|
||||
of the new zone can be included.
|
||||
.SH "EXAMPLES"
|
||||
.LP
|
||||
Some examples of use.
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ static void usage(char* argv[])
|
|||
printf("-d secs delay after connection before sending query\n");
|
||||
printf("-s use ssl\n");
|
||||
printf("-h this help text\n");
|
||||
printf("IXFR=N for the type, sends ixfr query with serial N.\n");
|
||||
printf("NOTIFY[=N] for the type, sends notify. Can set new zone serial N.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +117,29 @@ open_svr(const char* svr, int udp, struct sockaddr_storage* addr,
|
|||
return fd;
|
||||
}
|
||||
|
||||
/** Append a SOA record with serial number */
|
||||
static void
|
||||
write_soa_serial_to_buf(sldns_buffer* buf, struct query_info* qinfo,
|
||||
uint32_t serial)
|
||||
{
|
||||
sldns_buffer_set_position(buf, sldns_buffer_limit(buf));
|
||||
sldns_buffer_set_limit(buf, sldns_buffer_capacity(buf));
|
||||
/* Write compressed reference to the query */
|
||||
sldns_buffer_write_u16(buf, PTR_CREATE(LDNS_HEADER_SIZE));
|
||||
sldns_buffer_write_u16(buf, LDNS_RR_TYPE_SOA);
|
||||
sldns_buffer_write_u16(buf, qinfo->qclass);
|
||||
sldns_buffer_write_u32(buf, 3600); /* TTL */
|
||||
sldns_buffer_write_u16(buf, 1+1+4*5); /* rdatalen */
|
||||
sldns_buffer_write_u8(buf, 0); /* primary "." */
|
||||
sldns_buffer_write_u8(buf, 0); /* email "." */
|
||||
sldns_buffer_write_u32(buf, serial); /* serial */
|
||||
sldns_buffer_write_u32(buf, 0); /* refresh */
|
||||
sldns_buffer_write_u32(buf, 0); /* retry */
|
||||
sldns_buffer_write_u32(buf, 0); /* expire */
|
||||
sldns_buffer_write_u32(buf, 0); /* minimum */
|
||||
sldns_buffer_flip(buf);
|
||||
}
|
||||
|
||||
/** write a query over the TCP fd */
|
||||
static void
|
||||
write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
|
||||
|
|
@ -123,6 +148,8 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
|
|||
{
|
||||
struct query_info qinfo;
|
||||
size_t proxy_buf_limit = sldns_buffer_limit(proxy_buf);
|
||||
int have_serial = 0, is_notify = 0;
|
||||
uint32_t serial = 0;
|
||||
/* qname */
|
||||
qinfo.qname = sldns_str2wire_dname(strname, &qinfo.qname_len);
|
||||
if(!qinfo.qname) {
|
||||
|
|
@ -130,12 +157,27 @@ write_q(int fd, int udp, SSL* ssl, sldns_buffer* buf, uint16_t id,
|
|||
exit(1);
|
||||
}
|
||||
|
||||
/* qtype and qclass */
|
||||
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);
|
||||
/* qtype */
|
||||
if(strncasecmp(strtype, "IXFR=", 5) == 0) {
|
||||
serial = (uint32_t)atoi(strtype+5);
|
||||
have_serial = 1;
|
||||
qinfo.qtype = LDNS_RR_TYPE_IXFR;
|
||||
} else if(strcasecmp(strtype, "NOTIFY") == 0) {
|
||||
is_notify = 1;
|
||||
qinfo.qtype = LDNS_RR_TYPE_SOA;
|
||||
} else if(strncasecmp(strtype, "NOTIFY=", 7) == 0) {
|
||||
serial = (uint32_t)atoi(strtype+7);
|
||||
have_serial = 1;
|
||||
is_notify = 1;
|
||||
qinfo.qtype = LDNS_RR_TYPE_SOA;
|
||||
} else {
|
||||
qinfo.qtype = sldns_get_rr_type_by_name(strtype);
|
||||
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);
|
||||
if(qinfo.qclass == 0 && strcmp(strclass, "CLASS0") != 0) {
|
||||
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, 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) {
|
||||
/* add EDNS DO */
|
||||
struct edns_data edns;
|
||||
|
|
@ -361,6 +418,7 @@ static int parse_pp2_client(const char* pp2_client, int udp,
|
|||
sldns_buffer* proxy_buf)
|
||||
{
|
||||
struct sockaddr_storage pp2_addr;
|
||||
size_t bytes_written;
|
||||
socklen_t pp2_addrlen = 0;
|
||||
memset(&pp2_addr, 0, sizeof(pp2_addr));
|
||||
if(*pp2_client == 0) return 0;
|
||||
|
|
@ -369,7 +427,9 @@ static int parse_pp2_client(const char* pp2_client, int udp,
|
|||
exit(1);
|
||||
}
|
||||
sldns_buffer_clear(proxy_buf);
|
||||
pp2_write_to_buf(proxy_buf, &pp2_addr, !udp);
|
||||
bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf),
|
||||
sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp);
|
||||
sldns_buffer_set_position(proxy_buf, bytes_written);
|
||||
sldns_buffer_flip(proxy_buf);
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -541,6 +601,8 @@ int main(int argc, char** argv)
|
|||
break;
|
||||
case 'p':
|
||||
pp2_client = optarg;
|
||||
pp_init(&sldns_write_uint16,
|
||||
&sldns_write_uint32);
|
||||
break;
|
||||
case 'a':
|
||||
onarrival = 1;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
int stream) {
|
||||
size_t size;
|
||||
struct pp2_header *header = pp2_read_header(buf);
|
||||
if(header == NULL) return 0;
|
||||
struct pp2_header *header;
|
||||
int err = pp2_read_header(sldns_buffer_begin(buf),
|
||||
sldns_buffer_remaining(buf));
|
||||
if(err) return 0;
|
||||
header = (struct pp2_header*)sldns_buffer_begin(buf);
|
||||
size = PP2_HEADER_SIZE + ntohs(header->len);
|
||||
if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) {
|
||||
/* A connection from the proxy itself.
|
||||
* No need to do anything with addresses. */
|
||||
goto done;
|
||||
}
|
||||
if(header->fam_prot == 0x00) {
|
||||
if(header->fam_prot == PP2_UNSPEC_UNSPEC) {
|
||||
/* Unspecified family and protocol. This could be used for
|
||||
* health checks by proxies.
|
||||
* No need to do anything with addresses. */
|
||||
|
|
@ -908,8 +911,8 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
|
|||
}
|
||||
/* Read the proxied address */
|
||||
switch(header->fam_prot) {
|
||||
case 0x11: /* AF_INET|STREAM */
|
||||
case 0x12: /* AF_INET|DGRAM */
|
||||
case PP2_INET_STREAM:
|
||||
case PP2_INET_DGRAM:
|
||||
{
|
||||
struct sockaddr_in* addr =
|
||||
(struct sockaddr_in*)&rep->client_addr;
|
||||
|
|
@ -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. */
|
||||
break;
|
||||
case 0x21: /* AF_INET6|STREAM */
|
||||
case 0x22: /* AF_INET6|DGRAM */
|
||||
case PP2_INET6_STREAM:
|
||||
case PP2_INET6_DGRAM:
|
||||
{
|
||||
struct sockaddr_in6* addr =
|
||||
(struct sockaddr_in6*)&rep->client_addr;
|
||||
|
|
@ -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. */
|
||||
break;
|
||||
default:
|
||||
log_err("proxy_protocol: unsupported family and "
|
||||
"protocol 0x%x", (int)header->fam_prot);
|
||||
return 0;
|
||||
}
|
||||
rep->is_proxied = 1;
|
||||
done:
|
||||
|
|
@ -1813,19 +1820,25 @@ ssl_handle_read(struct comm_point* c)
|
|||
return 0;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
sldns_buffer_skip(c->buffer, r);
|
||||
if(c->tcp_byte_count != current_read_size) return 1;
|
||||
c->pp2_header_state = pp2_header_init;
|
||||
}
|
||||
}
|
||||
if(c->pp2_header_state == pp2_header_init) {
|
||||
header = pp2_read_header(c->buffer);
|
||||
if(!header) {
|
||||
int err;
|
||||
err = pp2_read_header(
|
||||
sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_limit(c->buffer));
|
||||
if(err) {
|
||||
log_err("proxy_protocol: could not parse "
|
||||
"PROXYv2 header");
|
||||
"PROXYv2 header (%s)",
|
||||
pp_lookup_error(err));
|
||||
return 0;
|
||||
}
|
||||
header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
|
||||
want_read_size = ntohs(header->len);
|
||||
if(sldns_buffer_remaining(c->buffer) <
|
||||
if(sldns_buffer_limit(c->buffer) <
|
||||
PP2_HEADER_SIZE + want_read_size) {
|
||||
log_err_addr("proxy_protocol: not enough "
|
||||
"buffer size to read PROXYv2 header", "",
|
||||
|
|
@ -1874,6 +1887,7 @@ ssl_handle_read(struct comm_point* c)
|
|||
return 0;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
sldns_buffer_skip(c->buffer, r);
|
||||
if(c->tcp_byte_count != current_read_size) return 1;
|
||||
c->pp2_header_state = pp2_header_done;
|
||||
}
|
||||
|
|
@ -1884,6 +1898,7 @@ ssl_handle_read(struct comm_point* c)
|
|||
c->repinfo.remote_addrlen);
|
||||
return 0;
|
||||
}
|
||||
sldns_buffer_flip(c->buffer);
|
||||
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
|
||||
log_err_addr("proxy_protocol: could not consume "
|
||||
"PROXYv2 header", "", &c->repinfo.remote_addr,
|
||||
|
|
@ -2205,19 +2220,25 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
|||
goto recv_error_initial;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
sldns_buffer_skip(c->buffer, r);
|
||||
if(c->tcp_byte_count != current_read_size) return 1;
|
||||
c->pp2_header_state = pp2_header_init;
|
||||
}
|
||||
}
|
||||
if(c->pp2_header_state == pp2_header_init) {
|
||||
header = pp2_read_header(c->buffer);
|
||||
if(!header) {
|
||||
int err;
|
||||
err = pp2_read_header(
|
||||
sldns_buffer_begin(c->buffer),
|
||||
sldns_buffer_limit(c->buffer));
|
||||
if(err) {
|
||||
log_err("proxy_protocol: could not parse "
|
||||
"PROXYv2 header");
|
||||
"PROXYv2 header (%s)",
|
||||
pp_lookup_error(err));
|
||||
return 0;
|
||||
}
|
||||
header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
|
||||
want_read_size = ntohs(header->len);
|
||||
if(sldns_buffer_remaining(c->buffer) <
|
||||
if(sldns_buffer_limit(c->buffer) <
|
||||
PP2_HEADER_SIZE + want_read_size) {
|
||||
log_err_addr("proxy_protocol: not enough "
|
||||
"buffer size to read PROXYv2 header", "",
|
||||
|
|
@ -2244,6 +2265,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
|||
goto recv_error;
|
||||
}
|
||||
c->tcp_byte_count += r;
|
||||
sldns_buffer_skip(c->buffer, r);
|
||||
if(c->tcp_byte_count != current_read_size) return 1;
|
||||
c->pp2_header_state = pp2_header_done;
|
||||
}
|
||||
|
|
@ -2254,6 +2276,7 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
|
|||
c->repinfo.remote_addrlen);
|
||||
return 0;
|
||||
}
|
||||
sldns_buffer_flip(c->buffer);
|
||||
if(!consume_pp2_header(c->buffer, &c->repinfo, 1)) {
|
||||
log_err_addr("proxy_protocol: could not consume "
|
||||
"PROXYv2 header", "", &c->repinfo.remote_addr,
|
||||
|
|
|
|||
|
|
@ -38,102 +38,162 @@
|
|||
*
|
||||
* This file contains PROXY protocol functions.
|
||||
*/
|
||||
#include "config.h"
|
||||
#include "util/log.h"
|
||||
#include "util/proxy_protocol.h"
|
||||
|
||||
int
|
||||
pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
|
||||
/**
|
||||
* Internal struct initialized with function pointers for writing uint16 and
|
||||
* uint32.
|
||||
*/
|
||||
struct proxy_protocol_data {
|
||||
void (*write_uint16)(void* buf, uint16_t data);
|
||||
void (*write_uint32)(void* buf, uint32_t data);
|
||||
};
|
||||
struct proxy_protocol_data pp_data;
|
||||
|
||||
/**
|
||||
* Internal lookup table; could be further generic like sldns_lookup_table
|
||||
* for all the future generic stuff.
|
||||
*/
|
||||
struct proxy_protocol_lookup_table {
|
||||
int id;
|
||||
const char *text;
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal parsing error text; could be exposed with pp_lookup_error.
|
||||
*/
|
||||
static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
|
||||
{ PP_PARSE_NOERROR, "no parse error" },
|
||||
{ PP_PARSE_SIZE, "not enough space for header" },
|
||||
{ PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
|
||||
{ PP_PARSE_UNKNOWN_CMD, "unknown command" },
|
||||
{ PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
|
||||
};
|
||||
|
||||
void
|
||||
pp_init(void (*write_uint16)(void* buf, uint16_t data),
|
||||
void (*write_uint32)(void* buf, uint32_t data)) {
|
||||
pp_data.write_uint16 = write_uint16;
|
||||
pp_data.write_uint32 = write_uint32;
|
||||
}
|
||||
|
||||
const char*
|
||||
pp_lookup_error(enum pp_parse_errors error) {
|
||||
return pp_parse_errors_data[error].text;
|
||||
}
|
||||
|
||||
size_t
|
||||
pp2_write_to_buf(uint8_t* buf, size_t buflen,
|
||||
#ifdef INET6
|
||||
struct sockaddr_storage* src,
|
||||
#else
|
||||
struct sockaddr_in* src,
|
||||
#endif
|
||||
int stream)
|
||||
{
|
||||
int af;
|
||||
size_t expected_size;
|
||||
if(!src) return 0;
|
||||
af = (int)((struct sockaddr_in*)src)->sin_family;
|
||||
if(sldns_buffer_remaining(buf) <
|
||||
PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
|
||||
expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
|
||||
if(buflen < expected_size) {
|
||||
return 0;
|
||||
}
|
||||
/* sig */
|
||||
sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
|
||||
memcpy(buf, PP2_SIG, PP2_SIG_LEN);
|
||||
buf += PP2_SIG_LEN;
|
||||
/* version and command */
|
||||
sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
|
||||
if(af==AF_INET) {
|
||||
*buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
|
||||
buf++;
|
||||
switch(af) {
|
||||
case AF_INET:
|
||||
/* family and protocol */
|
||||
sldns_buffer_write_u8(buf,
|
||||
(PP2_AF_INET<<4) |
|
||||
(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
|
||||
*buf = (PP2_AF_INET<<4) |
|
||||
(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
|
||||
buf++;
|
||||
/* length */
|
||||
sldns_buffer_write_u16(buf, 12);
|
||||
(*pp_data.write_uint16)(buf, 12);
|
||||
buf += 2;
|
||||
/* src addr */
|
||||
sldns_buffer_write(buf,
|
||||
memcpy(buf,
|
||||
&((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
|
||||
buf += 4;
|
||||
/* dst addr */
|
||||
sldns_buffer_write_u32(buf, 0);
|
||||
(*pp_data.write_uint32)(buf, 0);
|
||||
buf += 4;
|
||||
/* src port */
|
||||
sldns_buffer_write(buf,
|
||||
memcpy(buf,
|
||||
&((struct sockaddr_in*)src)->sin_port, 2);
|
||||
/* 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);
|
||||
buf += 2;
|
||||
/* 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);
|
||||
(*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*
|
||||
pp2_read_header(struct sldns_buffer* buf)
|
||||
int
|
||||
pp2_read_header(uint8_t* buf, size_t buflen)
|
||||
{
|
||||
size_t size;
|
||||
struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
|
||||
struct pp2_header* header = (struct pp2_header*)buf;
|
||||
/* Try to fail all the unsupported cases first. */
|
||||
if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
|
||||
log_err("proxy_protocol: not enough space for header");
|
||||
return NULL;
|
||||
if(buflen < PP2_HEADER_SIZE) {
|
||||
return PP_PARSE_SIZE;
|
||||
}
|
||||
/* Check for PROXYv2 header */
|
||||
if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
|
||||
((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
|
||||
log_err("proxy_protocol: could not match PROXYv2 header");
|
||||
return NULL;
|
||||
return PP_PARSE_WRONG_HEADERv2;
|
||||
}
|
||||
/* Check the length */
|
||||
size = PP2_HEADER_SIZE + ntohs(header->len);
|
||||
if(sldns_buffer_remaining(buf) < size) {
|
||||
log_err("proxy_protocol: not enough space for header");
|
||||
return NULL;
|
||||
if(buflen < size) {
|
||||
return PP_PARSE_SIZE;
|
||||
}
|
||||
/* Check for supported commands */
|
||||
if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
|
||||
(header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
|
||||
log_err("proxy_protocol: unsupported command");
|
||||
return NULL;
|
||||
return PP_PARSE_UNKNOWN_CMD;
|
||||
}
|
||||
/* Check for supported family and protocol */
|
||||
if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
|
||||
header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
|
||||
header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
|
||||
header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
|
||||
header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
|
||||
log_err("proxy_protocol: unsupported family and protocol");
|
||||
return NULL;
|
||||
if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
|
||||
header->fam_prot != PP2_INET_STREAM &&
|
||||
header->fam_prot != PP2_INET_DGRAM &&
|
||||
header->fam_prot != PP2_INET6_STREAM &&
|
||||
header->fam_prot != PP2_INET6_DGRAM &&
|
||||
header->fam_prot != PP2_UNIX_STREAM &&
|
||||
header->fam_prot != PP2_UNIX_DGRAM) {
|
||||
return PP_PARSE_UNKNOWN_FAM_PROT;
|
||||
}
|
||||
/* We have a correct header */
|
||||
return header;
|
||||
return PP_PARSE_NOERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@
|
|||
#ifndef PROXY_PROTOCOL_H
|
||||
#define PROXY_PROTOCOL_H
|
||||
|
||||
#include "sldns/sbuffer.h"
|
||||
#include "config.h"
|
||||
|
||||
/** PROXYv2 minimum header size */
|
||||
#define PP2_HEADER_SIZE 16
|
||||
|
|
@ -51,11 +51,11 @@
|
|||
#define PP2_SIG "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
|
||||
#define PP2_SIG_LEN 12
|
||||
|
||||
/** PROXYv2 version */
|
||||
/** PROXYv2 version (protocol value) */
|
||||
#define PP2_VERSION 0x2
|
||||
|
||||
/**
|
||||
* PROXYv2 command.
|
||||
* PROXYv2 command (protocol value).
|
||||
*/
|
||||
enum pp2_command {
|
||||
PP2_CMD_LOCAL = 0x0,
|
||||
|
|
@ -63,7 +63,7 @@ enum pp2_command {
|
|||
};
|
||||
|
||||
/**
|
||||
* PROXYv2 address family.
|
||||
* PROXYv2 address family (protocol value).
|
||||
*/
|
||||
enum pp2_af {
|
||||
PP2_AF_UNSPEC = 0x0,
|
||||
|
|
@ -73,7 +73,7 @@ enum pp2_af {
|
|||
};
|
||||
|
||||
/**
|
||||
* PROXYv2 protocol.
|
||||
* PROXYv2 protocol (protocol value).
|
||||
*/
|
||||
enum pp2_protocol {
|
||||
PP2_PROT_UNSPEC = 0x0,
|
||||
|
|
@ -81,6 +81,19 @@ enum pp2_protocol {
|
|||
PP2_PROT_DGRAM = 0x2
|
||||
};
|
||||
|
||||
/**
|
||||
* Expected combinations of address family and protocol values used in checks.
|
||||
*/
|
||||
enum pp2_af_protocol_combination {
|
||||
PP2_UNSPEC_UNSPEC = (PP2_AF_UNSPEC<<4)|PP2_PROT_UNSPEC,
|
||||
PP2_INET_STREAM = (PP2_AF_INET<<4)|PP2_PROT_STREAM,
|
||||
PP2_INET_DGRAM = (PP2_AF_INET<<4)|PP2_PROT_DGRAM,
|
||||
PP2_INET6_STREAM = (PP2_AF_INET6<<4)|PP2_PROT_STREAM,
|
||||
PP2_INET6_DGRAM = (PP2_AF_INET6<<4)|PP2_PROT_DGRAM,
|
||||
PP2_UNIX_STREAM = (PP2_AF_UNIX<<4)|PP2_PROT_STREAM,
|
||||
PP2_UNIX_DGRAM = (PP2_AF_UNIX<<4)|PP2_PROT_DGRAM
|
||||
};
|
||||
|
||||
/**
|
||||
* PROXYv2 header.
|
||||
*/
|
||||
|
|
@ -109,23 +122,56 @@ struct pp2_header {
|
|||
} addr;
|
||||
};
|
||||
|
||||
/**
|
||||
* PROXY parse errors.
|
||||
*/
|
||||
enum pp_parse_errors {
|
||||
PP_PARSE_NOERROR = 0,
|
||||
PP_PARSE_SIZE,
|
||||
PP_PARSE_WRONG_HEADERv2,
|
||||
PP_PARSE_UNKNOWN_CMD,
|
||||
PP_PARSE_UNKNOWN_FAM_PROT,
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the internal proxy structure.
|
||||
* @param write_uint16: pointer to a function that can write uint16.
|
||||
* @param write_uint32: pointer to a function that can write uint32.
|
||||
*/
|
||||
void pp_init(void (*write_uint16)(void* buf, uint16_t data),
|
||||
void (*write_uint32)(void* buf, uint32_t data));
|
||||
|
||||
/**
|
||||
* Lookup the parsing error description.
|
||||
* @param error: parsing error from pp2_read_header.
|
||||
* @return the description.
|
||||
*/
|
||||
const char* pp_lookup_error(enum pp_parse_errors error);
|
||||
|
||||
/**
|
||||
* Write a PROXYv2 header at the current position of the buffer.
|
||||
* @param buf: the buffer to write to.
|
||||
* @param buf: pointer to the buffer to write data to.
|
||||
* @param buflen: available size on the buffer.
|
||||
* @param src: the source address.
|
||||
* @param stream: if the protocol is stream or datagram.
|
||||
* @return 1 on success, 0 on failure.
|
||||
*/
|
||||
int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
|
||||
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);
|
||||
|
||||
/**
|
||||
* Read a PROXYv2 header from the current position of the buffer.
|
||||
* It does initial validation and returns a pointer to the buffer position on
|
||||
* success.
|
||||
* @param buf: the buffer to read from.
|
||||
* @return the pointer to the buffer position on success, NULL on error.
|
||||
* @param buf: pointer to the buffer data to read from.
|
||||
* @param buflen: available size on the buffer.
|
||||
* @return parsing error, 0 on success.
|
||||
*/
|
||||
struct pp2_header* pp2_read_header(struct sldns_buffer* buf);
|
||||
int pp2_read_header(uint8_t* buf, size_t buflen);
|
||||
|
||||
#endif /* PROXY_PROTOCOL_H */
|
||||
|
|
|
|||
Loading…
Reference in a new issue