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_CLOCAL 0x0
32 #define PROXY_CPROXY 0x1
33 #define PROXY_AF_UNSPEC 0x0
34 #define PROXY_AF_INET 0x1
35 #define PROXY_AF_INET6 0x2
36 #define PROXY_AF_UNIX 0x3
37 #define PROXY_TF_UNSPEC 0x0
38 #define PROXY_TF_STREAM 0x1
39 #define PROXY_TF_DGRAM 0x2
40
41 #define PROXY_SESSION_TIMEOUT 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
proxy_session(struct listener * listener,int sock,const struct sockaddr_storage * ss,void (* accepted)(struct listener *,int,const struct sockaddr_storage *,struct io *),void (* dropped)(struct listener *,int,const struct sockaddr_storage *))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)
126 return (-1);
127
128 if ((s->io = io_new()) == NULL) {
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_TIMEOUT * 1000);
145 io_set_read(s->io);
146
147 log_info("%016"PRIx64" smtp event=proxy address=%s",
148 s->id, ss_to_text(&s->ss));
149
150 return 0;
151 }
152
153 static void
proxy_io(struct io * io,int evt,void * arg)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;
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_CLOCAL:
222 /* local address, no need to modify ss */
223 break;
224
225 case PROXY_CPROXY:
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);
242 break;
243
244 case IO_DISCONNECTED:
245 proxy_error(s, "disconnected", NULL);
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
proxy_error(struct proxy_session * s,const char * reason,const char * extra)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
proxy_header_validate(struct proxy_session * s)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_UNSPEC << 4 | PROXY_TF_UNSPEC):
291 s->addr_total = 0;
292 break;
293
294 case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
295 s->addr_total = sizeof(s->addr.ipv4);
296 break;
297
298 case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
299 s->addr_total = sizeof(s->addr.ipv6);
300 break;
301
302 case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
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);
312 if (s->header_total > UINT16_MAX - 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
proxy_translate_ss(struct proxy_session * s)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_UNSPEC << 4 | PROXY_TF_UNSPEC):
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_INET << 4 | PROXY_TF_STREAM):
342 memset(&s->ss, 0, sizeof(s->ss));
343 sin->sin_family = AF_INET;
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_INET6 << 4 | PROXY_TF_STREAM):
349 memset(&s->ss, 0, sizeof(s->ss));
350 sin6->sin6_family = AF_INET6;
351 sin6->sin6_port = s->addr.ipv6.src_port;
352 memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr,
353 sizeof(s->addr.ipv6.src_addr));
354 break;
355
356 case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
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_UNIX;
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 }
375