diff options
Diffstat (limited to 'src/postscreen/postscreen_haproxy.c')
-rw-r--r-- | src/postscreen/postscreen_haproxy.c | 199 |
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); +} |