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