xref: /openbsd-src/sbin/unwind/libunbound/util/proxy_protocol.c (revision 7037e34cdfd270b3989fb1829c7cd3439048bd3a)
15c45b740Sflorian /*
25c45b740Sflorian  * util/proxy_protocol.c - event notification
35c45b740Sflorian  *
45c45b740Sflorian  * Copyright (c) 2022, NLnet Labs. All rights reserved.
55c45b740Sflorian  *
65c45b740Sflorian  * This software is open source.
75c45b740Sflorian  *
85c45b740Sflorian  * Redistribution and use in source and binary forms, with or without
95c45b740Sflorian  * modification, are permitted provided that the following conditions
105c45b740Sflorian  * are met:
115c45b740Sflorian  *
125c45b740Sflorian  * Redistributions of source code must retain the above copyright notice,
135c45b740Sflorian  * this list of conditions and the following disclaimer.
145c45b740Sflorian  *
155c45b740Sflorian  * Redistributions in binary form must reproduce the above copyright notice,
165c45b740Sflorian  * this list of conditions and the following disclaimer in the documentation
175c45b740Sflorian  * and/or other materials provided with the distribution.
185c45b740Sflorian  *
195c45b740Sflorian  * Neither the name of the NLNET LABS nor the names of its contributors may
205c45b740Sflorian  * be used to endorse or promote products derived from this software without
215c45b740Sflorian  * specific prior written permission.
225c45b740Sflorian  *
235c45b740Sflorian  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
245c45b740Sflorian  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
255c45b740Sflorian  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
265c45b740Sflorian  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
275c45b740Sflorian  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
285c45b740Sflorian  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
295c45b740Sflorian  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
305c45b740Sflorian  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
315c45b740Sflorian  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
325c45b740Sflorian  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
335c45b740Sflorian  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
345c45b740Sflorian  */
355c45b740Sflorian 
365c45b740Sflorian /**
375c45b740Sflorian  * \file
385c45b740Sflorian  *
395c45b740Sflorian  * This file contains PROXY protocol functions.
405c45b740Sflorian  */
415c45b740Sflorian #include "util/proxy_protocol.h"
425c45b740Sflorian 
4354cc57acSflorian /**
4454cc57acSflorian  * Internal struct initialized with function pointers for writing uint16 and
4554cc57acSflorian  * uint32.
4654cc57acSflorian  */
4754cc57acSflorian struct proxy_protocol_data {
4854cc57acSflorian 	void (*write_uint16)(void* buf, uint16_t data);
4954cc57acSflorian 	void (*write_uint32)(void* buf, uint32_t data);
5054cc57acSflorian };
5154cc57acSflorian struct proxy_protocol_data pp_data;
5254cc57acSflorian 
5354cc57acSflorian /**
5454cc57acSflorian  * Internal lookup table; could be further generic like sldns_lookup_table
5554cc57acSflorian  * for all the future generic stuff.
5654cc57acSflorian  */
5754cc57acSflorian struct proxy_protocol_lookup_table {
5854cc57acSflorian 	int id;
5954cc57acSflorian 	const char *text;
6054cc57acSflorian };
6154cc57acSflorian 
6254cc57acSflorian /**
6354cc57acSflorian  * Internal parsing error text; could be exposed with pp_lookup_error.
6454cc57acSflorian  */
6554cc57acSflorian static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
6654cc57acSflorian 	{ PP_PARSE_NOERROR, "no parse error" },
6754cc57acSflorian 	{ PP_PARSE_SIZE, "not enough space for header" },
6854cc57acSflorian 	{ PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
6954cc57acSflorian 	{ PP_PARSE_UNKNOWN_CMD, "unknown command" },
7054cc57acSflorian 	{ PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
7154cc57acSflorian };
7254cc57acSflorian 
7354cc57acSflorian void
7454cc57acSflorian pp_init(void (*write_uint16)(void* buf, uint16_t data),
7554cc57acSflorian 	void (*write_uint32)(void* buf, uint32_t data)) {
7654cc57acSflorian 	pp_data.write_uint16 = write_uint16;
7754cc57acSflorian 	pp_data.write_uint32 = write_uint32;
7854cc57acSflorian }
7954cc57acSflorian 
8054cc57acSflorian const char*
8154cc57acSflorian pp_lookup_error(enum pp_parse_errors error) {
8254cc57acSflorian 	return pp_parse_errors_data[error].text;
8354cc57acSflorian }
8454cc57acSflorian 
8554cc57acSflorian size_t
8654cc57acSflorian pp2_write_to_buf(uint8_t* buf, size_t buflen,
8754cc57acSflorian #ifdef INET6
8854cc57acSflorian 	struct sockaddr_storage* src,
8954cc57acSflorian #else
9054cc57acSflorian 	struct sockaddr_in* src,
9154cc57acSflorian #endif
925c45b740Sflorian 	int stream)
935c45b740Sflorian {
945c45b740Sflorian 	int af;
9554cc57acSflorian 	size_t expected_size;
965c45b740Sflorian 	if(!src) return 0;
975c45b740Sflorian 	af = (int)((struct sockaddr_in*)src)->sin_family;
9854cc57acSflorian 	expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
9954cc57acSflorian 	if(buflen < expected_size) {
1005c45b740Sflorian 		return 0;
1015c45b740Sflorian 	}
1025c45b740Sflorian 	/* sig */
10354cc57acSflorian 	memcpy(buf, PP2_SIG, PP2_SIG_LEN);
10454cc57acSflorian 	buf += PP2_SIG_LEN;
1055c45b740Sflorian 	/* version and command */
10654cc57acSflorian 	*buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
10754cc57acSflorian 	buf++;
10854cc57acSflorian 	switch(af) {
10954cc57acSflorian 	case AF_INET:
1105c45b740Sflorian 		/* family and protocol */
11154cc57acSflorian 		*buf = (PP2_AF_INET<<4) |
11254cc57acSflorian 			(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
11354cc57acSflorian 		buf++;
1145c45b740Sflorian 		/* length */
11554cc57acSflorian 		(*pp_data.write_uint16)(buf, 12);
11654cc57acSflorian 		buf += 2;
1175c45b740Sflorian 		/* src addr */
11854cc57acSflorian 		memcpy(buf,
1195c45b740Sflorian 			&((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
12054cc57acSflorian 		buf += 4;
1215c45b740Sflorian 		/* dst addr */
12254cc57acSflorian 		(*pp_data.write_uint32)(buf, 0);
12354cc57acSflorian 		buf += 4;
1245c45b740Sflorian 		/* src port */
12554cc57acSflorian 		memcpy(buf,
1265c45b740Sflorian 			&((struct sockaddr_in*)src)->sin_port, 2);
12754cc57acSflorian 		buf += 2;
1285c45b740Sflorian 		/* dst addr */
1295c45b740Sflorian 		/* dst port */
13054cc57acSflorian 		(*pp_data.write_uint16)(buf, 12);
13154cc57acSflorian 		break;
13254cc57acSflorian #ifdef INET6
13354cc57acSflorian 	case AF_INET6:
13454cc57acSflorian 		/* family and protocol */
13554cc57acSflorian 		*buf = (PP2_AF_INET6<<4) |
13654cc57acSflorian 			(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
13754cc57acSflorian 		buf++;
13854cc57acSflorian 		/* length */
13954cc57acSflorian 		(*pp_data.write_uint16)(buf, 36);
14054cc57acSflorian 		buf += 2;
14154cc57acSflorian 		/* src addr */
14254cc57acSflorian 		memcpy(buf,
14354cc57acSflorian 			&((struct sockaddr_in6*)src)->sin6_addr, 16);
14454cc57acSflorian 		buf += 16;
14554cc57acSflorian 		/* dst addr */
14654cc57acSflorian 		memset(buf, 0, 16);
14754cc57acSflorian 		buf += 16;
14854cc57acSflorian 		/* src port */
14954cc57acSflorian 		memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
15054cc57acSflorian 		buf += 2;
15154cc57acSflorian 		/* dst port */
15254cc57acSflorian 		(*pp_data.write_uint16)(buf, 0);
15354cc57acSflorian 		break;
15454cc57acSflorian #endif /* INET6 */
15554cc57acSflorian 	case AF_UNIX:
156*7037e34cSflorian 		ATTR_FALLTHROUGH
15754cc57acSflorian 		/* fallthrough */
15854cc57acSflorian 	default:
15954cc57acSflorian 		return 0;
1605c45b740Sflorian 	}
16154cc57acSflorian 	return expected_size;
1625c45b740Sflorian }
1635c45b740Sflorian 
16454cc57acSflorian int
16554cc57acSflorian pp2_read_header(uint8_t* buf, size_t buflen)
1665c45b740Sflorian {
1675c45b740Sflorian 	size_t size;
16854cc57acSflorian 	struct pp2_header* header = (struct pp2_header*)buf;
1695c45b740Sflorian 	/* Try to fail all the unsupported cases first. */
17054cc57acSflorian 	if(buflen < PP2_HEADER_SIZE) {
17154cc57acSflorian 		return PP_PARSE_SIZE;
1725c45b740Sflorian 	}
1735c45b740Sflorian 	/* Check for PROXYv2 header */
1745c45b740Sflorian 	if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
1755c45b740Sflorian 		((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
17654cc57acSflorian 		return PP_PARSE_WRONG_HEADERv2;
1775c45b740Sflorian 	}
1785c45b740Sflorian 	/* Check the length */
1795c45b740Sflorian 	size = PP2_HEADER_SIZE + ntohs(header->len);
18054cc57acSflorian 	if(buflen < size) {
18154cc57acSflorian 		return PP_PARSE_SIZE;
1825c45b740Sflorian 	}
1835c45b740Sflorian 	/* Check for supported commands */
1845c45b740Sflorian 	if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
1855c45b740Sflorian 		(header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
18654cc57acSflorian 		return PP_PARSE_UNKNOWN_CMD;
1875c45b740Sflorian 	}
1885c45b740Sflorian 	/* Check for supported family and protocol */
18954cc57acSflorian 	if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
19054cc57acSflorian 		header->fam_prot != PP2_INET_STREAM &&
19154cc57acSflorian 		header->fam_prot != PP2_INET_DGRAM &&
19254cc57acSflorian 		header->fam_prot != PP2_INET6_STREAM &&
19354cc57acSflorian 		header->fam_prot != PP2_INET6_DGRAM &&
19454cc57acSflorian 		header->fam_prot != PP2_UNIX_STREAM &&
19554cc57acSflorian 		header->fam_prot != PP2_UNIX_DGRAM) {
19654cc57acSflorian 		return PP_PARSE_UNKNOWN_FAM_PROT;
1975c45b740Sflorian 	}
1985c45b740Sflorian 	/* We have a correct header */
19954cc57acSflorian 	return PP_PARSE_NOERROR;
2005c45b740Sflorian }
201