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/types.h> 18 #include <sys/socket.h> 19 #include <sys/tree.h> 20 #include <sys/un.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <event.h> 25 #include <imsg.h> 26 #include <inttypes.h> 27 #include <limits.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 #include "log.h" 34 #include "smtpd.h" 35 36 37 /* 38 * The PROXYv2 protocol is described here: 39 * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt 40 */ 41 42 #define PROXY_CLOCAL 0x0 43 #define PROXY_CPROXY 0x1 44 #define PROXY_AF_UNSPEC 0x0 45 #define PROXY_AF_INET 0x1 46 #define PROXY_AF_INET6 0x2 47 #define PROXY_AF_UNIX 0x3 48 #define PROXY_TF_UNSPEC 0x0 49 #define PROXY_TF_STREAM 0x1 50 #define PROXY_TF_DGRAM 0x2 51 52 #define PROXY_SESSION_TIMEOUT 300 53 54 static const uint8_t pv2_signature[] = { 55 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 56 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A 57 }; 58 59 struct proxy_hdr_v2 { 60 uint8_t sig[12]; 61 uint8_t ver_cmd; 62 uint8_t fam; 63 uint16_t len; 64 } __attribute__((packed)); 65 66 67 struct proxy_addr_ipv4 { 68 uint32_t src_addr; 69 uint32_t dst_addr; 70 uint16_t src_port; 71 uint16_t dst_port; 72 } __attribute__((packed)); 73 74 struct proxy_addr_ipv6 { 75 uint8_t src_addr[16]; 76 uint8_t dst_addr[16]; 77 uint16_t src_port; 78 uint16_t dst_port; 79 } __attribute__((packed)); 80 81 struct proxy_addr_unix { 82 uint8_t src_addr[108]; 83 uint8_t dst_addr[108]; 84 } __attribute__((packed)); 85 86 union proxy_addr { 87 struct proxy_addr_ipv4 ipv4; 88 struct proxy_addr_ipv6 ipv6; 89 struct proxy_addr_unix un; 90 } __attribute__((packed)); 91 92 struct proxy_session { 93 struct listener *l; 94 struct io *io; 95 96 uint64_t id; 97 int fd; 98 uint16_t header_len; 99 uint16_t header_total; 100 uint16_t addr_len; 101 uint16_t addr_total; 102 103 struct sockaddr_storage ss; 104 struct proxy_hdr_v2 hdr; 105 union proxy_addr addr; 106 107 void (*cb_accepted)(struct listener *, int, 108 const struct sockaddr_storage *, struct io *); 109 void (*cb_dropped)(struct listener *, int, 110 const struct sockaddr_storage *); 111 }; 112 113 static void proxy_io(struct io *, int, void *); 114 static void proxy_error(struct proxy_session *, const char *, const char *); 115 static int proxy_header_validate(struct proxy_session *); 116 static int proxy_translate_ss(struct proxy_session *); 117 118 int 119 proxy_session(struct listener *listener, int sock, 120 const struct sockaddr_storage *ss, 121 void (*accepted)(struct listener *, int, 122 const struct sockaddr_storage *, struct io *), 123 void (*dropped)(struct listener *, int, 124 const struct sockaddr_storage *)); 125 126 int 127 proxy_session(struct listener *listener, int sock, 128 const struct sockaddr_storage *ss, 129 void (*accepted)(struct listener *, int, 130 const struct sockaddr_storage *, struct io *), 131 void (*dropped)(struct listener *, int, 132 const struct sockaddr_storage *)) 133 { 134 struct proxy_session *s; 135 136 if ((s = calloc(1, sizeof(*s))) == NULL) 137 return (-1); 138 139 if ((s->io = io_new()) == NULL) { 140 free(s); 141 return (-1); 142 } 143 144 s->id = generate_uid(); 145 s->l = listener; 146 s->fd = sock; 147 s->header_len = 0; 148 s->addr_len = 0; 149 s->ss = *ss; 150 s->cb_accepted = accepted; 151 s->cb_dropped = dropped; 152 153 io_set_callback(s->io, proxy_io, s); 154 io_set_fd(s->io, sock); 155 io_set_timeout(s->io, PROXY_SESSION_TIMEOUT * 1000); 156 io_set_read(s->io); 157 158 log_info("%016"PRIx64" smtp event=proxy address=%s", 159 s->id, ss_to_text(&s->ss)); 160 161 return 0; 162 } 163 164 static void 165 proxy_io(struct io *io, int evt, void *arg) 166 { 167 struct proxy_session *s = arg; 168 struct proxy_hdr_v2 *h = &s->hdr; 169 uint8_t *buf; 170 size_t len, off; 171 172 switch (evt) { 173 174 case IO_DATAIN: 175 buf = io_data(io); 176 len = io_datalen(io); 177 178 if (s->header_len < sizeof(s->hdr)) { 179 /* header is incomplete */ 180 off = sizeof(s->hdr) - s->header_len; 181 off = (len < off ? len : off); 182 memcpy((uint8_t *) &s->hdr + s->header_len, buf, off); 183 184 s->header_len += off; 185 buf += off; 186 len -= off; 187 io_drop(s->io, off); 188 189 if (s->header_len < sizeof(s->hdr)) { 190 /* header is still not complete */ 191 return; 192 } 193 194 if (proxy_header_validate(s) != 0) 195 return; 196 } 197 198 if (s->addr_len < s->addr_total) { 199 /* address is incomplete */ 200 off = s->addr_total - s->addr_len; 201 off = (len < off ? len : off); 202 memcpy((uint8_t *) &s->addr + s->addr_len, buf, off); 203 204 s->header_len += off; 205 s->addr_len += off; 206 buf += off; 207 len -= off; 208 io_drop(s->io, off); 209 210 if (s->addr_len < s->addr_total) { 211 /* address is still not complete */ 212 return; 213 } 214 } 215 216 if (s->header_len < s->header_total) { 217 /* additional parameters not complete */ 218 /* these are ignored for now, but we still need to drop 219 * the bytes from the buffer */ 220 off = s->header_total - s->header_len; 221 off = (len < off ? len : off); 222 223 s->header_len += off; 224 io_drop(s->io, off); 225 226 if (s->header_len < s->header_total) 227 /* not complete yet */ 228 return; 229 } 230 231 switch(h->ver_cmd & 0xF) { 232 case PROXY_CLOCAL: 233 /* local address, no need to modify ss */ 234 break; 235 236 case PROXY_CPROXY: 237 if (proxy_translate_ss(s) != 0) 238 return; 239 break; 240 241 default: 242 proxy_error(s, "protocol error", "unknown command"); 243 return; 244 } 245 246 s->cb_accepted(s->l, s->fd, &s->ss, s->io); 247 /* we passed off s->io, so it does not need to be freed here */ 248 free(s); 249 break; 250 251 case IO_TIMEOUT: 252 proxy_error(s, "timeout", NULL); 253 break; 254 255 case IO_DISCONNECTED: 256 proxy_error(s, "disconnected", NULL); 257 break; 258 259 case IO_ERROR: 260 proxy_error(s, "IO error", io_error(io)); 261 break; 262 263 default: 264 fatalx("proxy_io()"); 265 } 266 267 } 268 269 static void 270 proxy_error(struct proxy_session *s, const char *reason, const char *extra) 271 { 272 if (extra) 273 log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"", 274 s, ss_to_text(&s->ss), reason, extra); 275 else 276 log_info("proxy %p event=closed address=%s reason=\"%s\"", 277 s, ss_to_text(&s->ss), reason); 278 279 s->cb_dropped(s->l, s->fd, &s->ss); 280 io_free(s->io); 281 free(s); 282 } 283 284 static int 285 proxy_header_validate(struct proxy_session *s) 286 { 287 struct proxy_hdr_v2 *h = &s->hdr; 288 289 if (memcmp(h->sig, pv2_signature, 290 sizeof(pv2_signature)) != 0) { 291 proxy_error(s, "protocol error", "invalid signature"); 292 return (-1); 293 } 294 295 if ((h->ver_cmd >> 4) != 2) { 296 proxy_error(s, "protocol error", "invalid version"); 297 return (-1); 298 } 299 300 switch (h->fam) { 301 case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC): 302 s->addr_total = 0; 303 break; 304 305 case (PROXY_AF_INET << 4 | PROXY_TF_STREAM): 306 s->addr_total = sizeof(s->addr.ipv4); 307 break; 308 309 case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM): 310 s->addr_total = sizeof(s->addr.ipv6); 311 break; 312 313 case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM): 314 s->addr_total = sizeof(s->addr.un); 315 break; 316 317 default: 318 proxy_error(s, "protocol error", "unsupported address family"); 319 return (-1); 320 } 321 322 s->header_total = ntohs(h->len); 323 if (s->header_total > UINT16_MAX - sizeof(struct proxy_hdr_v2)) { 324 proxy_error(s, "protocol error", "header too long"); 325 return (-1); 326 } 327 s->header_total += sizeof(struct proxy_hdr_v2); 328 329 if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) { 330 proxy_error(s, "protocol error", "address info too short"); 331 return (-1); 332 } 333 334 return 0; 335 } 336 337 static int 338 proxy_translate_ss(struct proxy_session *s) 339 { 340 struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss; 341 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss; 342 struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss; 343 size_t sun_len; 344 345 switch (s->hdr.fam) { 346 case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC): 347 /* unspec: only supported for local */ 348 proxy_error(s, "address translation", "UNSPEC family not " 349 "supported for PROXYing"); 350 return (-1); 351 352 case (PROXY_AF_INET << 4 | PROXY_TF_STREAM): 353 memset(&s->ss, 0, sizeof(s->ss)); 354 sin->sin_family = AF_INET; 355 sin->sin_port = s->addr.ipv4.src_port; 356 sin->sin_addr.s_addr = s->addr.ipv4.src_addr; 357 break; 358 359 case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM): 360 memset(&s->ss, 0, sizeof(s->ss)); 361 sin6->sin6_family = AF_INET6; 362 sin6->sin6_port = s->addr.ipv6.src_port; 363 memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr, 364 sizeof(s->addr.ipv6.src_addr)); 365 break; 366 367 case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM): 368 memset(&s->ss, 0, sizeof(s->ss)); 369 sun_len = strnlen(s->addr.un.src_addr, 370 sizeof(s->addr.un.src_addr)); 371 if (sun_len > sizeof(sun->sun_path)) { 372 proxy_error(s, "address translation", "Unix socket path" 373 " longer than supported"); 374 return (-1); 375 } 376 sun->sun_family = AF_UNIX; 377 memcpy(sun->sun_path, s->addr.un.src_addr, sun_len); 378 break; 379 380 default: 381 fatalx("proxy_translate_ss()"); 382 } 383 384 return 0; 385 } 386