summaryrefslogtreecommitdiffstats
path: root/src/postscreen/postscreen_haproxy.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/postscreen/postscreen_haproxy.c')
-rw-r--r--src/postscreen/postscreen_haproxy.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/src/postscreen/postscreen_haproxy.c b/src/postscreen/postscreen_haproxy.c
new file mode 100644
index 0000000..d835bf0
--- /dev/null
+++ b/src/postscreen/postscreen_haproxy.c
@@ -0,0 +1,199 @@
+/*++
+/* NAME
+/* postscreen_haproxy 3
+/* SUMMARY
+/* haproxy protocol adapter
+/* SYNOPSIS
+/* #include <postscreen_haproxy.h>
+/*
+/* void psc_endpt_haproxy_lookup(smtp_client_stream, lookup_done)
+/* VSTRING *smtp_client_stream;
+/* void (*lookup_done)(status, smtp_client_stream,
+/* smtp_client_addr, smtp_client_port,
+/* smtp_server_addr, smtp_server_port)
+/* int status;
+/* MAI_HOSTADDR_STR *smtp_client_addr;
+/* MAI_SERVPORT_STR *smtp_client_port;
+/* MAI_HOSTADDR_STR *smtp_server_addr;
+/* MAI_SERVPORT_STR *smtp_server_port;
+/* DESCRIPTION
+/* psc_endpt_haproxy_lookup() looks up connection endpoint
+/* information via the haproxy protocol. Arguments and results
+/* conform to the postscreen_endpt(3) API.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <events.h>
+#include <myaddrinfo.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include <haproxy_srvr.h>
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+#include <postscreen_haproxy.h>
+
+ /*
+ * Per-session state.
+ */
+typedef struct {
+ VSTREAM *stream;
+ PSC_ENDPT_LOOKUP_FN notify;
+ VSTRING *buffer;
+} PSC_HAPROXY_STATE;
+
+/* psc_endpt_haproxy_event - read or time event */
+
+static void psc_endpt_haproxy_event(int event, void *context)
+{
+ const char *myname = "psc_endpt_haproxy_event";
+ PSC_HAPROXY_STATE *state = (PSC_HAPROXY_STATE *) context;
+ int status = 0;
+ MAI_HOSTADDR_STR smtp_client_addr;
+ MAI_SERVPORT_STR smtp_client_port;
+ MAI_HOSTADDR_STR smtp_server_addr;
+ MAI_SERVPORT_STR smtp_server_port;
+ int last_char = 0;
+ const char *err;
+ VSTRING *escape_buf;
+ char read_buf[HAPROXY_MAX_LEN];
+ ssize_t read_len;
+ char *cp;
+
+ /*
+ * We must not read(2) past the <CR><LF> that terminates the haproxy
+ * line. For efficiency reasons we read the entire haproxy line in one
+ * read(2) call when we know that the line is unfragmented. In the rare
+ * case that the line is fragmented, we fall back and read(2) it one
+ * character at a time.
+ */
+ switch (event) {
+ case EVENT_TIME:
+ msg_warn("haproxy read: time limit exceeded");
+ status = -1;
+ break;
+ case EVENT_READ:
+ /* Determine the initial VSTREAM read(2) buffer size. */
+ if (VSTRING_LEN(state->buffer) == 0) {
+ if ((read_len = recv(vstream_fileno(state->stream),
+ read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0
+ && ((cp = memchr(read_buf, '\n', read_len)) != 0)) {
+ read_len = cp - read_buf + 1;
+ } else {
+ read_len = 1;
+ }
+ vstream_control(state->stream, CA_VSTREAM_CTL_BUFSIZE(read_len),
+ CA_VSTREAM_CTL_END);
+ }
+ /* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */
+ do {
+ if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) {
+ if (vstream_ferror(state->stream))
+ msg_warn("haproxy read: %m");
+ else
+ msg_warn("haproxy read: lost connection");
+ status = -1;
+ break;
+ }
+ if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) {
+ msg_warn("haproxy read: line too long");
+ status = -1;
+ break;
+ }
+ VSTRING_ADDCH(state->buffer, last_char);
+ } while (vstream_peek(state->stream) > 0);
+ break;
+ }
+
+ /*
+ * Parse the haproxy line. Note: the haproxy_srvr_parse() routine
+ * performs address protocol checks, address and port syntax checks, and
+ * converts IPv4-in-IPv6 address string syntax (::ffff:1.2.3.4) to IPv4
+ * syntax where permitted by the main.cf:inet_protocols setting.
+ */
+ if (status == 0 && last_char == '\n') {
+ VSTRING_TERMINATE(state->buffer);
+ if ((err = haproxy_srvr_parse(vstring_str(state->buffer),
+ &smtp_client_addr, &smtp_client_port,
+ &smtp_server_addr, &smtp_server_port)) != 0) {
+ escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
+ escape(escape_buf, vstring_str(state->buffer),
+ VSTRING_LEN(state->buffer));
+ msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
+ status = -1;
+ vstring_free(escape_buf);
+ }
+ }
+
+ /*
+ * Are we done yet?
+ */
+ if (status < 0 || last_char == '\n') {
+ PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream),
+ psc_endpt_haproxy_event, context);
+ vstream_control(state->stream,
+ CA_VSTREAM_CTL_BUFSIZE(VSTREAM_BUFSIZE),
+ CA_VSTREAM_CTL_END);
+ state->notify(status, state->stream,
+ &smtp_client_addr, &smtp_client_port,
+ &smtp_server_addr, &smtp_server_port);
+ /* Note: the stream may be closed at this point. */
+ vstring_free(state->buffer);
+ myfree((void *) state);
+ }
+}
+
+/* psc_endpt_haproxy_lookup - event-driven haproxy client */
+
+void psc_endpt_haproxy_lookup(VSTREAM *stream,
+ PSC_ENDPT_LOOKUP_FN notify)
+{
+ const char *myname = "psc_endpt_haproxy_lookup";
+ PSC_HAPROXY_STATE *state;
+
+ /*
+ * Prepare the per-session state. XXX To improve overload behavior,
+ * maintain a pool of these so that we can reduce memory allocator
+ * activity.
+ */
+ state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state));
+ state->stream = stream;
+ state->notify = notify;
+ state->buffer = vstring_alloc(100);
+
+ /*
+ * Read the haproxy line.
+ */
+ PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_endpt_haproxy_event,
+ (void *) state, var_psc_uproxy_tmout);
+}