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.
}
/* Ignore the destination address; it should be us. */
break;
+ default:
+ log_err("proxy_protocol: unsupported family and "
+ "protocol");
+ return 0;
}
rep->is_proxied = 1;
done:
}
}
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_remaining(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) <
PP2_HEADER_SIZE + want_read_size) {
}
}
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_remaining(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) <
PP2_HEADER_SIZE + want_read_size) {
*
* 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, struct sockaddr_storage* src,
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);
+ *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
+ buf++;
if(af==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);
+ buf += 2;
+ /* dst addr */
/* dst port */
- sldns_buffer_write_u16(buf, 0);
+ (*pp_data.write_uint16)(buf, 12);
} else {
/* family and protocol */
- sldns_buffer_write_u8(buf,
- (PP2_AF_INET6<<4) |
- (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+ *buf = (PP2_AF_INET6<<4) |
+ (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+ buf++;
/* length */
- sldns_buffer_write_u16(buf, 36);
+ (*pp_data.write_uint16)(buf, 36);
+ buf += 2;
/* src addr */
- sldns_buffer_write(buf,
+ memcpy(buf,
&((struct sockaddr_in6*)src)->sin6_addr, 16);
+ buf += 16;
/* dst addr */
- sldns_buffer_set_at(buf,
- sldns_buffer_position(buf), 0, 16);
- sldns_buffer_skip(buf, 16);
+ memset(buf, 0, 16);
+ buf += 16;
/* src port */
- sldns_buffer_write(buf,
- &((struct sockaddr_in6*)src)->sin6_port, 2);
+ memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
+ buf += 2;
/* dst port */
- sldns_buffer_write_u16(buf, 0);
+ (*pp_data.write_uint16)(buf, 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;
+ header->fam_prot != 0x22 /* AF_INET6|DGRAM */ &&
+ header->fam_prot != 0x31 /* AF_UNIX|STREAM */ &&
+ header->fam_prot != 0x32 /* AF_UNIX|DGRAM */) {
+ return PP_PARSE_UNKNOWN_FAM_PROT;
}
/* We have a correct header */
- return header;
+ return PP_PARSE_NOERROR;
}
#ifndef PROXY_PROTOCOL_H
#define PROXY_PROTOCOL_H
-#include "sldns/sbuffer.h"
+#include "config.h"
/** PROXYv2 minimum header size */
#define PP2_HEADER_SIZE 16
} 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,
- int stream);
+size_t pp2_write_to_buf(uint8_t* buf, size_t buflen,
+ 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.
+ * @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 */