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