/*++ /* NAME /* postscreen_haproxy 3 /* SUMMARY /* haproxy protocol adapter /* SYNOPSIS /* #include /* /* 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 #include #include #include /* Utility library. */ #include #include #include #include #include #include #include /* Global library. */ #include #include /* Application-specific. */ #include #include /* * 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 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); }