File: | src/usr.sbin/smtpd/smtpd/../proxy.c |
Warning: | line 195, column 4 Value stored to 'buf' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <sys/un.h> |
18 | |
19 | #include <inttypes.h> |
20 | #include <stdlib.h> |
21 | #include <string.h> |
22 | |
23 | #include "smtpd.h" |
24 | #include "log.h" |
25 | |
26 | /* |
27 | * The PROXYv2 protocol is described here: |
28 | * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt |
29 | */ |
30 | |
31 | #define PROXY_CLOCAL0x0 0x0 |
32 | #define PROXY_CPROXY0x1 0x1 |
33 | #define PROXY_AF_UNSPEC0x0 0x0 |
34 | #define PROXY_AF_INET0x1 0x1 |
35 | #define PROXY_AF_INET60x2 0x2 |
36 | #define PROXY_AF_UNIX0x3 0x3 |
37 | #define PROXY_TF_UNSPEC0x0 0x0 |
38 | #define PROXY_TF_STREAM0x1 0x1 |
39 | #define PROXY_TF_DGRAM0x2 0x2 |
40 | |
41 | #define PROXY_SESSION_TIMEOUT300 300 |
42 | |
43 | static const uint8_t pv2_signature[] = { |
44 | 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, |
45 | 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A |
46 | }; |
47 | |
48 | struct proxy_hdr_v2 { |
49 | uint8_t sig[12]; |
50 | uint8_t ver_cmd; |
51 | uint8_t fam; |
52 | uint16_t len; |
53 | } __attribute__((packed)); |
54 | |
55 | |
56 | struct proxy_addr_ipv4 { |
57 | uint32_t src_addr; |
58 | uint32_t dst_addr; |
59 | uint16_t src_port; |
60 | uint16_t dst_port; |
61 | } __attribute__((packed)); |
62 | |
63 | struct proxy_addr_ipv6 { |
64 | uint8_t src_addr[16]; |
65 | uint8_t dst_addr[16]; |
66 | uint16_t src_port; |
67 | uint16_t dst_port; |
68 | } __attribute__((packed)); |
69 | |
70 | struct proxy_addr_unix { |
71 | uint8_t src_addr[108]; |
72 | uint8_t dst_addr[108]; |
73 | } __attribute__((packed)); |
74 | |
75 | union proxy_addr { |
76 | struct proxy_addr_ipv4 ipv4; |
77 | struct proxy_addr_ipv6 ipv6; |
78 | struct proxy_addr_unix un; |
79 | } __attribute__((packed)); |
80 | |
81 | struct proxy_session { |
82 | struct listener *l; |
83 | struct io *io; |
84 | |
85 | uint64_t id; |
86 | int fd; |
87 | uint16_t header_len; |
88 | uint16_t header_total; |
89 | uint16_t addr_len; |
90 | uint16_t addr_total; |
91 | |
92 | struct sockaddr_storage ss; |
93 | struct proxy_hdr_v2 hdr; |
94 | union proxy_addr addr; |
95 | |
96 | void (*cb_accepted)(struct listener *, int, |
97 | const struct sockaddr_storage *, struct io *); |
98 | void (*cb_dropped)(struct listener *, int, |
99 | const struct sockaddr_storage *); |
100 | }; |
101 | |
102 | static void proxy_io(struct io *, int, void *); |
103 | static void proxy_error(struct proxy_session *, const char *, const char *); |
104 | static int proxy_header_validate(struct proxy_session *); |
105 | static int proxy_translate_ss(struct proxy_session *); |
106 | |
107 | int |
108 | proxy_session(struct listener *listener, int sock, |
109 | const struct sockaddr_storage *ss, |
110 | void (*accepted)(struct listener *, int, |
111 | const struct sockaddr_storage *, struct io *), |
112 | void (*dropped)(struct listener *, int, |
113 | const struct sockaddr_storage *)); |
114 | |
115 | int |
116 | proxy_session(struct listener *listener, int sock, |
117 | const struct sockaddr_storage *ss, |
118 | void (*accepted)(struct listener *, int, |
119 | const struct sockaddr_storage *, struct io *), |
120 | void (*dropped)(struct listener *, int, |
121 | const struct sockaddr_storage *)) |
122 | { |
123 | struct proxy_session *s; |
124 | |
125 | if ((s = calloc(1, sizeof(*s))) == NULL((void *)0)) |
126 | return (-1); |
127 | |
128 | if ((s->io = io_new()) == NULL((void *)0)) { |
129 | free(s); |
130 | return (-1); |
131 | } |
132 | |
133 | s->id = generate_uid(); |
134 | s->l = listener; |
135 | s->fd = sock; |
136 | s->header_len = 0; |
137 | s->addr_len = 0; |
138 | s->ss = *ss; |
139 | s->cb_accepted = accepted; |
140 | s->cb_dropped = dropped; |
141 | |
142 | io_set_callback(s->io, proxy_io, s); |
143 | io_set_fd(s->io, sock); |
144 | io_set_timeout(s->io, PROXY_SESSION_TIMEOUT300 * 1000); |
145 | io_set_read(s->io); |
146 | |
147 | log_info("%016"PRIx64"llx"" smtp event=proxy address=%s", |
148 | s->id, ss_to_text(&s->ss)); |
149 | |
150 | return 0; |
151 | } |
152 | |
153 | static void |
154 | proxy_io(struct io *io, int evt, void *arg) |
155 | { |
156 | struct proxy_session *s = arg; |
157 | struct proxy_hdr_v2 *h = &s->hdr; |
158 | uint8_t *buf; |
159 | size_t len, off; |
160 | |
161 | switch (evt) { |
162 | |
163 | case IO_DATAIN: |
164 | buf = io_data(io); |
165 | len = io_datalen(io); |
166 | |
167 | if (s->header_len < sizeof(s->hdr)) { |
168 | /* header is incomplete */ |
169 | off = sizeof(s->hdr) - s->header_len; |
170 | off = (len < off ? len : off); |
171 | memcpy((uint8_t *) &s->hdr + s->header_len, buf, off); |
172 | |
173 | s->header_len += off; |
174 | buf += off; |
175 | len -= off; |
176 | io_drop(s->io, off); |
177 | |
178 | if (s->header_len < sizeof(s->hdr)) { |
179 | /* header is still not complete */ |
180 | return; |
181 | } |
182 | |
183 | if (proxy_header_validate(s) != 0) |
184 | return; |
185 | } |
186 | |
187 | if (s->addr_len < s->addr_total) { |
188 | /* address is incomplete */ |
189 | off = s->addr_total - s->addr_len; |
190 | off = (len < off ? len : off); |
191 | memcpy((uint8_t *) &s->addr + s->addr_len, buf, off); |
192 | |
193 | s->header_len += off; |
194 | s->addr_len += off; |
195 | buf += off; |
Value stored to 'buf' is never read | |
196 | len -= off; |
197 | io_drop(s->io, off); |
198 | |
199 | if (s->addr_len < s->addr_total) { |
200 | /* address is still not complete */ |
201 | return; |
202 | } |
203 | } |
204 | |
205 | if (s->header_len < s->header_total) { |
206 | /* additional parameters not complete */ |
207 | /* these are ignored for now, but we still need to drop |
208 | * the bytes from the buffer */ |
209 | off = s->header_total - s->header_len; |
210 | off = (len < off ? len : off); |
211 | |
212 | s->header_len += off; |
213 | io_drop(s->io, off); |
214 | |
215 | if (s->header_len < s->header_total) |
216 | /* not complete yet */ |
217 | return; |
218 | } |
219 | |
220 | switch(h->ver_cmd & 0xF) { |
221 | case PROXY_CLOCAL0x0: |
222 | /* local address, no need to modify ss */ |
223 | break; |
224 | |
225 | case PROXY_CPROXY0x1: |
226 | if (proxy_translate_ss(s) != 0) |
227 | return; |
228 | break; |
229 | |
230 | default: |
231 | proxy_error(s, "protocol error", "unknown command"); |
232 | return; |
233 | } |
234 | |
235 | s->cb_accepted(s->l, s->fd, &s->ss, s->io); |
236 | /* we passed off s->io, so it does not need to be freed here */ |
237 | free(s); |
238 | break; |
239 | |
240 | case IO_TIMEOUT: |
241 | proxy_error(s, "timeout", NULL((void *)0)); |
242 | break; |
243 | |
244 | case IO_DISCONNECTED: |
245 | proxy_error(s, "disconnected", NULL((void *)0)); |
246 | break; |
247 | |
248 | case IO_ERROR: |
249 | proxy_error(s, "IO error", io_error(io)); |
250 | break; |
251 | |
252 | default: |
253 | fatalx("proxy_io()"); |
254 | } |
255 | |
256 | } |
257 | |
258 | static void |
259 | proxy_error(struct proxy_session *s, const char *reason, const char *extra) |
260 | { |
261 | if (extra) |
262 | log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"", |
263 | s, ss_to_text(&s->ss), reason, extra); |
264 | else |
265 | log_info("proxy %p event=closed address=%s reason=\"%s\"", |
266 | s, ss_to_text(&s->ss), reason); |
267 | |
268 | s->cb_dropped(s->l, s->fd, &s->ss); |
269 | io_free(s->io); |
270 | free(s); |
271 | } |
272 | |
273 | static int |
274 | proxy_header_validate(struct proxy_session *s) |
275 | { |
276 | struct proxy_hdr_v2 *h = &s->hdr; |
277 | |
278 | if (memcmp(h->sig, pv2_signature, |
279 | sizeof(pv2_signature)) != 0) { |
280 | proxy_error(s, "protocol error", "invalid signature"); |
281 | return (-1); |
282 | } |
283 | |
284 | if ((h->ver_cmd >> 4) != 2) { |
285 | proxy_error(s, "protocol error", "invalid version"); |
286 | return (-1); |
287 | } |
288 | |
289 | switch (h->fam) { |
290 | case (PROXY_AF_UNSPEC0x0 << 4 | PROXY_TF_UNSPEC0x0): |
291 | s->addr_total = 0; |
292 | break; |
293 | |
294 | case (PROXY_AF_INET0x1 << 4 | PROXY_TF_STREAM0x1): |
295 | s->addr_total = sizeof(s->addr.ipv4); |
296 | break; |
297 | |
298 | case (PROXY_AF_INET60x2 << 4 | PROXY_TF_STREAM0x1): |
299 | s->addr_total = sizeof(s->addr.ipv6); |
300 | break; |
301 | |
302 | case (PROXY_AF_UNIX0x3 << 4 | PROXY_TF_STREAM0x1): |
303 | s->addr_total = sizeof(s->addr.un); |
304 | break; |
305 | |
306 | default: |
307 | proxy_error(s, "protocol error", "unsupported address family"); |
308 | return (-1); |
309 | } |
310 | |
311 | s->header_total = ntohs(h->len)(__uint16_t)(__builtin_constant_p(h->len) ? (__uint16_t)(( (__uint16_t)(h->len) & 0xffU) << 8 | ((__uint16_t )(h->len) & 0xff00U) >> 8) : __swap16md(h->len )); |
312 | if (s->header_total > UINT16_MAX0xffff - sizeof(struct proxy_hdr_v2)) { |
313 | proxy_error(s, "protocol error", "header too long"); |
314 | return (-1); |
315 | } |
316 | s->header_total += sizeof(struct proxy_hdr_v2); |
317 | |
318 | if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) { |
319 | proxy_error(s, "protocol error", "address info too short"); |
320 | return (-1); |
321 | } |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static int |
327 | proxy_translate_ss(struct proxy_session *s) |
328 | { |
329 | struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss; |
330 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss; |
331 | struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss; |
332 | size_t sun_len; |
333 | |
334 | switch (s->hdr.fam) { |
335 | case (PROXY_AF_UNSPEC0x0 << 4 | PROXY_TF_UNSPEC0x0): |
336 | /* unspec: only supported for local */ |
337 | proxy_error(s, "address translation", "UNSPEC family not " |
338 | "supported for PROXYing"); |
339 | return (-1); |
340 | |
341 | case (PROXY_AF_INET0x1 << 4 | PROXY_TF_STREAM0x1): |
342 | memset(&s->ss, 0, sizeof(s->ss)); |
343 | sin->sin_family = AF_INET2; |
344 | sin->sin_port = s->addr.ipv4.src_port; |
345 | sin->sin_addr.s_addr = s->addr.ipv4.src_addr; |
346 | break; |
347 | |
348 | case (PROXY_AF_INET60x2 << 4 | PROXY_TF_STREAM0x1): |
349 | memset(&s->ss, 0, sizeof(s->ss)); |
350 | sin6->sin6_family = AF_INET624; |
351 | sin6->sin6_port = s->addr.ipv6.src_port; |
352 | memcpy(sin6->sin6_addr.s6_addr__u6_addr.__u6_addr8, s->addr.ipv6.src_addr, |
353 | sizeof(s->addr.ipv6.src_addr)); |
354 | break; |
355 | |
356 | case (PROXY_AF_UNIX0x3 << 4 | PROXY_TF_STREAM0x1): |
357 | memset(&s->ss, 0, sizeof(s->ss)); |
358 | sun_len = strnlen(s->addr.un.src_addr, |
359 | sizeof(s->addr.un.src_addr)); |
360 | if (sun_len > sizeof(sun->sun_path)) { |
361 | proxy_error(s, "address translation", "Unix socket path" |
362 | " longer than supported"); |
363 | return (-1); |
364 | } |
365 | sun->sun_family = AF_UNIX1; |
366 | memcpy(sun->sun_path, s->addr.un.src_addr, sun_len); |
367 | break; |
368 | |
369 | default: |
370 | fatalx("proxy_translate_ss()"); |
371 | } |
372 | |
373 | return 0; |
374 | } |