1*811a4a01Schristos /*
2*811a4a01Schristos * util/proxy_protocol.c - event notification
3*811a4a01Schristos *
4*811a4a01Schristos * Copyright (c) 2022, NLnet Labs. All rights reserved.
5*811a4a01Schristos *
6*811a4a01Schristos * This software is open source.
7*811a4a01Schristos *
8*811a4a01Schristos * Redistribution and use in source and binary forms, with or without
9*811a4a01Schristos * modification, are permitted provided that the following conditions
10*811a4a01Schristos * are met:
11*811a4a01Schristos *
12*811a4a01Schristos * Redistributions of source code must retain the above copyright notice,
13*811a4a01Schristos * this list of conditions and the following disclaimer.
14*811a4a01Schristos *
15*811a4a01Schristos * Redistributions in binary form must reproduce the above copyright notice,
16*811a4a01Schristos * this list of conditions and the following disclaimer in the documentation
17*811a4a01Schristos * and/or other materials provided with the distribution.
18*811a4a01Schristos *
19*811a4a01Schristos * Neither the name of the NLNET LABS nor the names of its contributors may
20*811a4a01Schristos * be used to endorse or promote products derived from this software without
21*811a4a01Schristos * specific prior written permission.
22*811a4a01Schristos *
23*811a4a01Schristos * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24*811a4a01Schristos * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25*811a4a01Schristos * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26*811a4a01Schristos * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27*811a4a01Schristos * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28*811a4a01Schristos * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29*811a4a01Schristos * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30*811a4a01Schristos * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31*811a4a01Schristos * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32*811a4a01Schristos * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33*811a4a01Schristos * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34*811a4a01Schristos */
35*811a4a01Schristos
36*811a4a01Schristos /**
37*811a4a01Schristos * \file
38*811a4a01Schristos *
39*811a4a01Schristos * This file contains PROXY protocol functions.
40*811a4a01Schristos */
41*811a4a01Schristos #include "util/proxy_protocol.h"
42*811a4a01Schristos
43*811a4a01Schristos /**
44*811a4a01Schristos * Internal struct initialized with function pointers for writing uint16 and
45*811a4a01Schristos * uint32.
46*811a4a01Schristos */
47*811a4a01Schristos struct proxy_protocol_data {
48*811a4a01Schristos void (*write_uint16)(void* buf, uint16_t data);
49*811a4a01Schristos void (*write_uint32)(void* buf, uint32_t data);
50*811a4a01Schristos };
51*811a4a01Schristos struct proxy_protocol_data pp_data;
52*811a4a01Schristos
53*811a4a01Schristos /**
54*811a4a01Schristos * Internal lookup table; could be further generic like sldns_lookup_table
55*811a4a01Schristos * for all the future generic stuff.
56*811a4a01Schristos */
57*811a4a01Schristos struct proxy_protocol_lookup_table {
58*811a4a01Schristos int id;
59*811a4a01Schristos const char *text;
60*811a4a01Schristos };
61*811a4a01Schristos
62*811a4a01Schristos /**
63*811a4a01Schristos * Internal parsing error text; could be exposed with pp_lookup_error.
64*811a4a01Schristos */
65*811a4a01Schristos static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
66*811a4a01Schristos { PP_PARSE_NOERROR, "no parse error" },
67*811a4a01Schristos { PP_PARSE_SIZE, "not enough space for header" },
68*811a4a01Schristos { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
69*811a4a01Schristos { PP_PARSE_UNKNOWN_CMD, "unknown command" },
70*811a4a01Schristos { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
71*811a4a01Schristos };
72*811a4a01Schristos
73*811a4a01Schristos void
pp_init(void (* write_uint16)(void * buf,uint16_t data),void (* write_uint32)(void * buf,uint32_t data))74*811a4a01Schristos pp_init(void (*write_uint16)(void* buf, uint16_t data),
75*811a4a01Schristos void (*write_uint32)(void* buf, uint32_t data)) {
76*811a4a01Schristos pp_data.write_uint16 = write_uint16;
77*811a4a01Schristos pp_data.write_uint32 = write_uint32;
78*811a4a01Schristos }
79*811a4a01Schristos
80*811a4a01Schristos const char*
pp_lookup_error(enum pp_parse_errors error)81*811a4a01Schristos pp_lookup_error(enum pp_parse_errors error) {
82*811a4a01Schristos return pp_parse_errors_data[error].text;
83*811a4a01Schristos }
84*811a4a01Schristos
85*811a4a01Schristos size_t
pp2_write_to_buf(uint8_t * buf,size_t buflen,struct sockaddr_storage * src,int stream)86*811a4a01Schristos pp2_write_to_buf(uint8_t* buf, size_t buflen,
87*811a4a01Schristos #ifdef INET6
88*811a4a01Schristos struct sockaddr_storage* src,
89*811a4a01Schristos #else
90*811a4a01Schristos struct sockaddr_in* src,
91*811a4a01Schristos #endif
92*811a4a01Schristos int stream)
93*811a4a01Schristos {
94*811a4a01Schristos int af;
95*811a4a01Schristos size_t expected_size;
96*811a4a01Schristos if(!src) return 0;
97*811a4a01Schristos af = (int)((struct sockaddr_in*)src)->sin_family;
98*811a4a01Schristos expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
99*811a4a01Schristos if(buflen < expected_size) {
100*811a4a01Schristos return 0;
101*811a4a01Schristos }
102*811a4a01Schristos /* sig */
103*811a4a01Schristos memcpy(buf, PP2_SIG, PP2_SIG_LEN);
104*811a4a01Schristos buf += PP2_SIG_LEN;
105*811a4a01Schristos /* version and command */
106*811a4a01Schristos *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
107*811a4a01Schristos buf++;
108*811a4a01Schristos switch(af) {
109*811a4a01Schristos case AF_INET:
110*811a4a01Schristos /* family and protocol */
111*811a4a01Schristos *buf = (PP2_AF_INET<<4) |
112*811a4a01Schristos (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
113*811a4a01Schristos buf++;
114*811a4a01Schristos /* length */
115*811a4a01Schristos (*pp_data.write_uint16)(buf, 12);
116*811a4a01Schristos buf += 2;
117*811a4a01Schristos /* src addr */
118*811a4a01Schristos memcpy(buf,
119*811a4a01Schristos &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
120*811a4a01Schristos buf += 4;
121*811a4a01Schristos /* dst addr */
122*811a4a01Schristos (*pp_data.write_uint32)(buf, 0);
123*811a4a01Schristos buf += 4;
124*811a4a01Schristos /* src port */
125*811a4a01Schristos memcpy(buf,
126*811a4a01Schristos &((struct sockaddr_in*)src)->sin_port, 2);
127*811a4a01Schristos buf += 2;
128*811a4a01Schristos /* dst addr */
129*811a4a01Schristos /* dst port */
130*811a4a01Schristos (*pp_data.write_uint16)(buf, 12);
131*811a4a01Schristos break;
132*811a4a01Schristos #ifdef INET6
133*811a4a01Schristos case AF_INET6:
134*811a4a01Schristos /* family and protocol */
135*811a4a01Schristos *buf = (PP2_AF_INET6<<4) |
136*811a4a01Schristos (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
137*811a4a01Schristos buf++;
138*811a4a01Schristos /* length */
139*811a4a01Schristos (*pp_data.write_uint16)(buf, 36);
140*811a4a01Schristos buf += 2;
141*811a4a01Schristos /* src addr */
142*811a4a01Schristos memcpy(buf,
143*811a4a01Schristos &((struct sockaddr_in6*)src)->sin6_addr, 16);
144*811a4a01Schristos buf += 16;
145*811a4a01Schristos /* dst addr */
146*811a4a01Schristos memset(buf, 0, 16);
147*811a4a01Schristos buf += 16;
148*811a4a01Schristos /* src port */
149*811a4a01Schristos memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
150*811a4a01Schristos buf += 2;
151*811a4a01Schristos /* dst port */
152*811a4a01Schristos (*pp_data.write_uint16)(buf, 0);
153*811a4a01Schristos break;
154*811a4a01Schristos #endif /* INET6 */
155*811a4a01Schristos case AF_UNIX:
156*811a4a01Schristos /* fallthrough */
157*811a4a01Schristos default:
158*811a4a01Schristos return 0;
159*811a4a01Schristos }
160*811a4a01Schristos return expected_size;
161*811a4a01Schristos }
162*811a4a01Schristos
163*811a4a01Schristos int
pp2_read_header(uint8_t * buf,size_t buflen)164*811a4a01Schristos pp2_read_header(uint8_t* buf, size_t buflen)
165*811a4a01Schristos {
166*811a4a01Schristos size_t size;
167*811a4a01Schristos struct pp2_header* header = (struct pp2_header*)buf;
168*811a4a01Schristos /* Try to fail all the unsupported cases first. */
169*811a4a01Schristos if(buflen < PP2_HEADER_SIZE) {
170*811a4a01Schristos return PP_PARSE_SIZE;
171*811a4a01Schristos }
172*811a4a01Schristos /* Check for PROXYv2 header */
173*811a4a01Schristos if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
174*811a4a01Schristos ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
175*811a4a01Schristos return PP_PARSE_WRONG_HEADERv2;
176*811a4a01Schristos }
177*811a4a01Schristos /* Check the length */
178*811a4a01Schristos size = PP2_HEADER_SIZE + ntohs(header->len);
179*811a4a01Schristos if(buflen < size) {
180*811a4a01Schristos return PP_PARSE_SIZE;
181*811a4a01Schristos }
182*811a4a01Schristos /* Check for supported commands */
183*811a4a01Schristos if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
184*811a4a01Schristos (header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
185*811a4a01Schristos return PP_PARSE_UNKNOWN_CMD;
186*811a4a01Schristos }
187*811a4a01Schristos /* Check for supported family and protocol */
188*811a4a01Schristos if(header->fam_prot != PP2_UNSPEC_UNSPEC &&
189*811a4a01Schristos header->fam_prot != PP2_INET_STREAM &&
190*811a4a01Schristos header->fam_prot != PP2_INET_DGRAM &&
191*811a4a01Schristos header->fam_prot != PP2_INET6_STREAM &&
192*811a4a01Schristos header->fam_prot != PP2_INET6_DGRAM &&
193*811a4a01Schristos header->fam_prot != PP2_UNIX_STREAM &&
194*811a4a01Schristos header->fam_prot != PP2_UNIX_DGRAM) {
195*811a4a01Schristos return PP_PARSE_UNKNOWN_FAM_PROT;
196*811a4a01Schristos }
197*811a4a01Schristos /* We have a correct header */
198*811a4a01Schristos return PP_PARSE_NOERROR;
199*811a4a01Schristos }
200