xref: /freebsd-src/contrib/unbound/util/proxy_protocol.c (revision 865f46b255599c4a645e84a4cbb5ea7abdc0e207)
1*865f46b2SCy Schubert /*
2*865f46b2SCy Schubert  * util/proxy_protocol.c - event notification
3*865f46b2SCy Schubert  *
4*865f46b2SCy Schubert  * Copyright (c) 2022, NLnet Labs. All rights reserved.
5*865f46b2SCy Schubert  *
6*865f46b2SCy Schubert  * This software is open source.
7*865f46b2SCy Schubert  *
8*865f46b2SCy Schubert  * Redistribution and use in source and binary forms, with or without
9*865f46b2SCy Schubert  * modification, are permitted provided that the following conditions
10*865f46b2SCy Schubert  * are met:
11*865f46b2SCy Schubert  *
12*865f46b2SCy Schubert  * Redistributions of source code must retain the above copyright notice,
13*865f46b2SCy Schubert  * this list of conditions and the following disclaimer.
14*865f46b2SCy Schubert  *
15*865f46b2SCy Schubert  * Redistributions in binary form must reproduce the above copyright notice,
16*865f46b2SCy Schubert  * this list of conditions and the following disclaimer in the documentation
17*865f46b2SCy Schubert  * and/or other materials provided with the distribution.
18*865f46b2SCy Schubert  *
19*865f46b2SCy Schubert  * Neither the name of the NLNET LABS nor the names of its contributors may
20*865f46b2SCy Schubert  * be used to endorse or promote products derived from this software without
21*865f46b2SCy Schubert  * specific prior written permission.
22*865f46b2SCy Schubert  *
23*865f46b2SCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*865f46b2SCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*865f46b2SCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*865f46b2SCy Schubert  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*865f46b2SCy Schubert  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*865f46b2SCy Schubert  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*865f46b2SCy Schubert  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*865f46b2SCy Schubert  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*865f46b2SCy Schubert  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*865f46b2SCy Schubert  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*865f46b2SCy Schubert  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*865f46b2SCy Schubert  */
35*865f46b2SCy Schubert 
36*865f46b2SCy Schubert /**
37*865f46b2SCy Schubert  * \file
38*865f46b2SCy Schubert  *
39*865f46b2SCy Schubert  * This file contains PROXY protocol functions.
40*865f46b2SCy Schubert  */
41*865f46b2SCy Schubert #include "config.h"
42*865f46b2SCy Schubert #include "util/log.h"
43*865f46b2SCy Schubert #include "util/proxy_protocol.h"
44*865f46b2SCy Schubert 
45*865f46b2SCy Schubert int
46*865f46b2SCy Schubert pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
47*865f46b2SCy Schubert 	int stream)
48*865f46b2SCy Schubert {
49*865f46b2SCy Schubert 	int af;
50*865f46b2SCy Schubert 	if(!src) return 0;
51*865f46b2SCy Schubert 	af = (int)((struct sockaddr_in*)src)->sin_family;
52*865f46b2SCy Schubert 	if(sldns_buffer_remaining(buf) <
53*865f46b2SCy Schubert 		PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
54*865f46b2SCy Schubert 		return 0;
55*865f46b2SCy Schubert 	}
56*865f46b2SCy Schubert 	/* sig */
57*865f46b2SCy Schubert 	sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
58*865f46b2SCy Schubert 	/* version and command */
59*865f46b2SCy Schubert 	sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
60*865f46b2SCy Schubert 	if(af==AF_INET) {
61*865f46b2SCy Schubert 		/* family and protocol */
62*865f46b2SCy Schubert 		sldns_buffer_write_u8(buf,
63*865f46b2SCy Schubert 			(PP2_AF_INET<<4) |
64*865f46b2SCy Schubert 			(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
65*865f46b2SCy Schubert 		/* length */
66*865f46b2SCy Schubert 		sldns_buffer_write_u16(buf, 12);
67*865f46b2SCy Schubert 		/* src addr */
68*865f46b2SCy Schubert 		sldns_buffer_write(buf,
69*865f46b2SCy Schubert 			&((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
70*865f46b2SCy Schubert 		/* dst addr */
71*865f46b2SCy Schubert 		sldns_buffer_write_u32(buf, 0);
72*865f46b2SCy Schubert 		/* src port */
73*865f46b2SCy Schubert 		sldns_buffer_write(buf,
74*865f46b2SCy Schubert 			&((struct sockaddr_in*)src)->sin_port, 2);
75*865f46b2SCy Schubert 		/* dst port */
76*865f46b2SCy Schubert 		sldns_buffer_write_u16(buf, 0);
77*865f46b2SCy Schubert 	} else {
78*865f46b2SCy Schubert 		/* family and protocol */
79*865f46b2SCy Schubert 		sldns_buffer_write_u8(buf,
80*865f46b2SCy Schubert 			(PP2_AF_INET6<<4) |
81*865f46b2SCy Schubert 			(stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
82*865f46b2SCy Schubert 		/* length */
83*865f46b2SCy Schubert 		sldns_buffer_write_u16(buf, 36);
84*865f46b2SCy Schubert 		/* src addr */
85*865f46b2SCy Schubert 		sldns_buffer_write(buf,
86*865f46b2SCy Schubert 			&((struct sockaddr_in6*)src)->sin6_addr, 16);
87*865f46b2SCy Schubert 		/* dst addr */
88*865f46b2SCy Schubert 		sldns_buffer_set_at(buf,
89*865f46b2SCy Schubert 			sldns_buffer_position(buf), 0, 16);
90*865f46b2SCy Schubert 		sldns_buffer_skip(buf, 16);
91*865f46b2SCy Schubert 		/* src port */
92*865f46b2SCy Schubert 		sldns_buffer_write(buf,
93*865f46b2SCy Schubert 			&((struct sockaddr_in6*)src)->sin6_port, 2);
94*865f46b2SCy Schubert 		/* dst port */
95*865f46b2SCy Schubert 		sldns_buffer_write_u16(buf, 0);
96*865f46b2SCy Schubert 	}
97*865f46b2SCy Schubert 	return 1;
98*865f46b2SCy Schubert }
99*865f46b2SCy Schubert 
100*865f46b2SCy Schubert struct pp2_header*
101*865f46b2SCy Schubert pp2_read_header(struct sldns_buffer* buf)
102*865f46b2SCy Schubert {
103*865f46b2SCy Schubert 	size_t size;
104*865f46b2SCy Schubert 	struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
105*865f46b2SCy Schubert 	/* Try to fail all the unsupported cases first. */
106*865f46b2SCy Schubert 	if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
107*865f46b2SCy Schubert 		log_err("proxy_protocol: not enough space for header");
108*865f46b2SCy Schubert 		return NULL;
109*865f46b2SCy Schubert 	}
110*865f46b2SCy Schubert 	/* Check for PROXYv2 header */
111*865f46b2SCy Schubert 	if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
112*865f46b2SCy Schubert 		((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
113*865f46b2SCy Schubert 		log_err("proxy_protocol: could not match PROXYv2 header");
114*865f46b2SCy Schubert 		return NULL;
115*865f46b2SCy Schubert 	}
116*865f46b2SCy Schubert 	/* Check the length */
117*865f46b2SCy Schubert 	size = PP2_HEADER_SIZE + ntohs(header->len);
118*865f46b2SCy Schubert 	if(sldns_buffer_remaining(buf) < size) {
119*865f46b2SCy Schubert 		log_err("proxy_protocol: not enough space for header");
120*865f46b2SCy Schubert 		return NULL;
121*865f46b2SCy Schubert 	}
122*865f46b2SCy Schubert 	/* Check for supported commands */
123*865f46b2SCy Schubert 	if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
124*865f46b2SCy Schubert 		(header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
125*865f46b2SCy Schubert 		log_err("proxy_protocol: unsupported command");
126*865f46b2SCy Schubert 		return NULL;
127*865f46b2SCy Schubert 	}
128*865f46b2SCy Schubert 	/* Check for supported family and protocol */
129*865f46b2SCy Schubert 	if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
130*865f46b2SCy Schubert 		header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
131*865f46b2SCy Schubert 		header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
132*865f46b2SCy Schubert 		header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
133*865f46b2SCy Schubert 		header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
134*865f46b2SCy Schubert 		log_err("proxy_protocol: unsupported family and protocol");
135*865f46b2SCy Schubert 		return NULL;
136*865f46b2SCy Schubert 	}
137*865f46b2SCy Schubert 	/* We have a correct header */
138*865f46b2SCy Schubert 	return header;
139*865f46b2SCy Schubert }
140