aboutsummaryrefslogtreecommitdiffstats
path: root/smtpd/proxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtpd/proxy.c')
-rw-r--r--smtpd/proxy.c387
1 files changed, 0 insertions, 387 deletions
diff --git a/smtpd/proxy.c b/smtpd/proxy.c
deleted file mode 100644
index fdaf4f27..00000000
--- a/smtpd/proxy.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/*
- * Copyright (c) 2017 Antoine Kaufmann <toni@famkaufmann.info>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "includes.h"
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/tree.h>
-#include <sys/un.h>
-
-#include <err.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "smtpd.h"
-
-
-/*
- * The PROXYv2 protocol is described here:
- * http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
- */
-
-#define PROXY_CLOCAL 0x0
-#define PROXY_CPROXY 0x1
-#define PROXY_AF_UNSPEC 0x0
-#define PROXY_AF_INET 0x1
-#define PROXY_AF_INET6 0x2
-#define PROXY_AF_UNIX 0x3
-#define PROXY_TF_UNSPEC 0x0
-#define PROXY_TF_STREAM 0x1
-#define PROXY_TF_DGRAM 0x2
-
-#define PROXY_SESSION_TIMEOUT 300
-
-static const uint8_t pv2_signature[] = {
- 0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D,
- 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
-};
-
-struct proxy_hdr_v2 {
- uint8_t sig[12];
- uint8_t ver_cmd;
- uint8_t fam;
- uint16_t len;
-} __attribute__((packed));
-
-
-struct proxy_addr_ipv4 {
- uint32_t src_addr;
- uint32_t dst_addr;
- uint16_t src_port;
- uint16_t dst_port;
-} __attribute__((packed));
-
-struct proxy_addr_ipv6 {
- uint8_t src_addr[16];
- uint8_t dst_addr[16];
- uint16_t src_port;
- uint16_t dst_port;
-} __attribute__((packed));
-
-struct proxy_addr_unix {
- uint8_t src_addr[108];
- uint8_t dst_addr[108];
-} __attribute__((packed));
-
-union proxy_addr {
- struct proxy_addr_ipv4 ipv4;
- struct proxy_addr_ipv6 ipv6;
- struct proxy_addr_unix un;
-} __attribute__((packed));
-
-struct proxy_session {
- struct listener *l;
- struct io *io;
-
- uint64_t id;
- int fd;
- uint16_t header_len;
- uint16_t header_total;
- uint16_t addr_len;
- uint16_t addr_total;
-
- struct sockaddr_storage ss;
- struct proxy_hdr_v2 hdr;
- union proxy_addr addr;
-
- void (*cb_accepted)(struct listener *, int,
- const struct sockaddr_storage *, struct io *);
- void (*cb_dropped)(struct listener *, int,
- const struct sockaddr_storage *);
-};
-
-static void proxy_io(struct io *, int, void *);
-static void proxy_error(struct proxy_session *, const char *, const char *);
-static int proxy_header_validate(struct proxy_session *);
-static int proxy_translate_ss(struct proxy_session *);
-
-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 *));
-
-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 *))
-{
- struct proxy_session *s;
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- return (-1);
-
- if ((s->io = io_new()) == NULL) {
- free(s);
- return (-1);
- }
-
- s->id = generate_uid();
- s->l = listener;
- s->fd = sock;
- s->header_len = 0;
- s->addr_len = 0;
- s->ss = *ss;
- s->cb_accepted = accepted;
- s->cb_dropped = dropped;
-
- io_set_callback(s->io, proxy_io, s);
- io_set_fd(s->io, sock);
- io_set_timeout(s->io, PROXY_SESSION_TIMEOUT * 1000);
- io_set_read(s->io);
-
- log_info("%016"PRIx64" smtp event=proxy address=%s",
- s->id, ss_to_text(&s->ss));
-
- return 0;
-}
-
-static void
-proxy_io(struct io *io, int evt, void *arg)
-{
- struct proxy_session *s = arg;
- struct proxy_hdr_v2 *h = &s->hdr;
- uint8_t *buf;
- size_t len, off;
-
- switch (evt) {
-
- case IO_DATAIN:
- buf = io_data(io);
- len = io_datalen(io);
-
- if (s->header_len < sizeof(s->hdr)) {
- /* header is incomplete */
- off = sizeof(s->hdr) - s->header_len;
- off = (len < off ? len : off);
- memcpy((uint8_t *) &s->hdr + s->header_len, buf, off);
-
- s->header_len += off;
- buf += off;
- len -= off;
- io_drop(s->io, off);
-
- if (s->header_len < sizeof(s->hdr)) {
- /* header is still not complete */
- return;
- }
-
- if (proxy_header_validate(s) != 0)
- return;
- }
-
- if (s->addr_len < s->addr_total) {
- /* address is incomplete */
- off = s->addr_total - s->addr_len;
- off = (len < off ? len : off);
- memcpy((uint8_t *) &s->addr + s->addr_len, buf, off);
-
- s->header_len += off;
- s->addr_len += off;
- buf += off;
- len -= off;
- io_drop(s->io, off);
-
- if (s->addr_len < s->addr_total) {
- /* address is still not complete */
- return;
- }
- }
-
- if (s->header_len < s->header_total) {
- /* additional parameters not complete */
- /* these are ignored for now, but we still need to drop
- * the bytes from the buffer */
- off = s->header_total - s->header_len;
- off = (len < off ? len : off);
-
- s->header_len += off;
- io_drop(s->io, off);
-
- if (s->header_len < s->header_total)
- /* not complete yet */
- return;
- }
-
- switch(h->ver_cmd & 0xF) {
- case PROXY_CLOCAL:
- /* local address, no need to modify ss */
- break;
-
- case PROXY_CPROXY:
- if (proxy_translate_ss(s) != 0)
- return;
- break;
-
- default:
- proxy_error(s, "protocol error", "unknown command");
- return;
- }
-
- s->cb_accepted(s->l, s->fd, &s->ss, s->io);
- /* we passed off s->io, so it does not need to be freed here */
- free(s);
- break;
-
- case IO_TIMEOUT:
- proxy_error(s, "timeout", NULL);
- break;
-
- case IO_DISCONNECTED:
- proxy_error(s, "disconnected", NULL);
- break;
-
- case IO_ERROR:
- proxy_error(s, "IO error", io_error(io));
- break;
-
- default:
- fatalx("proxy_io()");
- }
-
-}
-
-static void
-proxy_error(struct proxy_session *s, const char *reason, const char *extra)
-{
- if (extra)
- log_info("proxy %p event=closed address=%s reason=\"%s (%s)\"",
- s, ss_to_text(&s->ss), reason, extra);
- else
- log_info("proxy %p event=closed address=%s reason=\"%s\"",
- s, ss_to_text(&s->ss), reason);
-
- s->cb_dropped(s->l, s->fd, &s->ss);
- io_free(s->io);
- free(s);
-}
-
-static int
-proxy_header_validate(struct proxy_session *s)
-{
- struct proxy_hdr_v2 *h = &s->hdr;
-
- if (memcmp(h->sig, pv2_signature,
- sizeof(pv2_signature)) != 0) {
- proxy_error(s, "protocol error", "invalid signature");
- return (-1);
- }
-
- if ((h->ver_cmd >> 4) != 2) {
- proxy_error(s, "protocol error", "invalid version");
- return (-1);
- }
-
- switch (h->fam) {
- case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
- s->addr_total = 0;
- break;
-
- case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.ipv4);
- break;
-
- case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.ipv6);
- break;
-
- case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
- s->addr_total = sizeof(s->addr.un);
- break;
-
- default:
- proxy_error(s, "protocol error", "unsupported address family");
- return (-1);
- }
-
- s->header_total = ntohs(h->len);
- if (s->header_total > UINT16_MAX - sizeof(struct proxy_hdr_v2)) {
- proxy_error(s, "protocol error", "header too long");
- return (-1);
- }
- s->header_total += sizeof(struct proxy_hdr_v2);
-
- if (s->header_total < sizeof(struct proxy_hdr_v2) + s->addr_total) {
- proxy_error(s, "protocol error", "address info too short");
- return (-1);
- }
-
- return 0;
-}
-
-static int
-proxy_translate_ss(struct proxy_session *s)
-{
- struct sockaddr_in *sin = (struct sockaddr_in *) &s->ss;
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &s->ss;
- struct sockaddr_un *sun = (struct sockaddr_un *) &s->ss;
- size_t sun_len;
-
- switch (s->hdr.fam) {
- case (PROXY_AF_UNSPEC << 4 | PROXY_TF_UNSPEC):
- /* unspec: only supported for local */
- proxy_error(s, "address translation", "UNSPEC family not "
- "supported for PROXYing");
- return (-1);
-
- case (PROXY_AF_INET << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sin->sin_family = AF_INET;
- sin->sin_port = s->addr.ipv4.src_port;
- sin->sin_addr.s_addr = s->addr.ipv4.src_addr;
- break;
-
- case (PROXY_AF_INET6 << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = s->addr.ipv6.src_port;
- memcpy(sin6->sin6_addr.s6_addr, s->addr.ipv6.src_addr,
- sizeof(s->addr.ipv6.src_addr));
- break;
-
- case (PROXY_AF_UNIX << 4 | PROXY_TF_STREAM):
- memset(&s->ss, 0, sizeof(s->ss));
- sun_len = strnlen(s->addr.un.src_addr,
- sizeof(s->addr.un.src_addr));
- if (sun_len > sizeof(sun->sun_path)) {
- proxy_error(s, "address translation", "Unix socket path"
- " longer than supported");
- return (-1);
- }
- sun->sun_family = AF_UNIX;
- memcpy(sun->sun_path, s->addr.un.src_addr, sun_len);
- break;
-
- default:
- fatalx("proxy_translate_ss()");
- }
-
- return 0;
-}