1/*++ 2/* NAME 3/* postscreen_haproxy 3 4/* SUMMARY 5/* haproxy protocol adapter 6/* SYNOPSIS 7/* #include <postscreen_haproxy.h> 8/* 9/* void psc_endpt_haproxy_lookup(smtp_client_stream, lookup_done) 10/* VSTRING *smtp_client_stream; 11/* void (*lookup_done)(status, smtp_client_stream, 12/* smtp_client_addr, smtp_client_port, 13/* smtp_server_addr, smtp_server_port) 14/* int status; 15/* MAI_HOSTADDR_STR *smtp_client_addr; 16/* MAI_SERVPORT_STR *smtp_client_port; 17/* MAI_HOSTADDR_STR *smtp_server_addr; 18/* MAI_SERVPORT_STR *smtp_server_port; 19/* DESCRIPTION 20/* psc_endpt_haproxy_lookup() looks up connection endpoint 21/* information via the haproxy protocol. Arguments and results 22/* conform to the postscreen_endpt(3) API. 23/* LICENSE 24/* .ad 25/* .fi 26/* The Secure Mailer license must be distributed with this software. 27/* AUTHOR(S) 28/* Wietse Venema 29/* IBM T.J. Watson Research 30/* P.O. Box 704 31/* Yorktown Heights, NY 10598, USA 32/*--*/ 33 34/* System library. */ 35 36#include <sys_defs.h> 37#include <stdio.h> 38#include <stdarg.h> 39#include <stdlib.h> 40 41/* Utility library. */ 42 43#include <msg.h> 44#include <mymalloc.h> 45#include <events.h> 46#include <myaddrinfo.h> 47#include <vstream.h> 48#include <vstring.h> 49#include <stringops.h> 50 51/* Global library. */ 52 53#include <haproxy_srvr.h> 54#include <mail_params.h> 55 56/* Application-specific. */ 57 58#include <postscreen.h> 59#include <postscreen_haproxy.h> 60 61 /* 62 * Per-session state. 63 */ 64typedef struct { 65 VSTREAM *stream; 66 PSC_ENDPT_LOOKUP_FN notify; 67 VSTRING *buffer; 68} PSC_HAPROXY_STATE; 69 70/* psc_endpt_haproxy_event - read or time event */ 71 72static void psc_endpt_haproxy_event(int event, char *context) 73{ 74 const char *myname = "psc_endpt_haproxy_event"; 75 PSC_HAPROXY_STATE *state = (PSC_HAPROXY_STATE *) context; 76 int status = 0; 77 MAI_HOSTADDR_STR smtp_client_addr; 78 MAI_SERVPORT_STR smtp_client_port; 79 MAI_HOSTADDR_STR smtp_server_addr; 80 MAI_SERVPORT_STR smtp_server_port; 81 int last_char = 0; 82 const char *err; 83 VSTRING *escape_buf; 84 char read_buf[HAPROXY_MAX_LEN]; 85 ssize_t read_len; 86 char *cp; 87 88 /* 89 * We must not read(2) past the <CR><LF> that terminates the haproxy 90 * line. For efficiency reasons we read the entire haproxy line in one 91 * read(2) call when we know that the line is unfragmented. In the rare 92 * case that the line is fragmented, we fall back and read(2) it one 93 * character at a time. 94 */ 95 switch (event) { 96 case EVENT_TIME: 97 msg_warn("haproxy read: time limit exceeded"); 98 status = -1; 99 break; 100 case EVENT_READ: 101 /* Determine the initial VSTREAM read(2) buffer size. */ 102 if (VSTRING_LEN(state->buffer) == 0) { 103 if ((read_len = recv(vstream_fileno(state->stream), 104 read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0 105 && ((cp = memchr(read_buf, '\n', read_len)) != 0)) { 106 read_len = cp - read_buf + 1; 107 } else { 108 read_len = 1; 109 } 110 vstream_control(state->stream, VSTREAM_CTL_BUFSIZE, read_len, 111 VSTREAM_CTL_END); 112 } 113 /* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */ 114 do { 115 if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) { 116 if (vstream_ferror(state->stream)) 117 msg_warn("haproxy read: %m"); 118 else 119 msg_warn("haproxy read: lost connection"); 120 status = -1; 121 break; 122 } 123 if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) { 124 msg_warn("haproxy read: line too long"); 125 status = -1; 126 break; 127 } 128 VSTRING_ADDCH(state->buffer, last_char); 129 } while (vstream_peek(state->stream) > 0); 130 break; 131 } 132 133 /* 134 * Parse the haproxy line. Note: the haproxy_srvr_parse() routine 135 * performs address protocol checks, address and port syntax checks, and 136 * converts IPv4-in-IPv6 address string syntax (:ffff::1.2.3.4) to IPv4 137 * syntax where permitted by the main.cf:inet_protocols setting. 138 */ 139 if (status == 0 && last_char == '\n') { 140 VSTRING_TERMINATE(state->buffer); 141 if ((err = haproxy_srvr_parse(vstring_str(state->buffer), 142 &smtp_client_addr, &smtp_client_port, 143 &smtp_server_addr, &smtp_server_port)) != 0) { 144 escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); 145 escape(escape_buf, vstring_str(state->buffer), 146 VSTRING_LEN(state->buffer)); 147 msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); 148 status = -1; 149 vstring_free(escape_buf); 150 } 151 } 152 153 /* 154 * Are we done yet? 155 */ 156 if (status < 0 || last_char == '\n') { 157 PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream), 158 psc_endpt_haproxy_event, context); 159 vstream_control(state->stream, 160 VSTREAM_CTL_BUFSIZE, (ssize_t) VSTREAM_BUFSIZE, 161 VSTREAM_CTL_END); 162 state->notify(status, state->stream, 163 &smtp_client_addr, &smtp_client_port, 164 &smtp_server_addr, &smtp_server_port); 165 /* Note: the stream may be closed at this point. */ 166 vstring_free(state->buffer); 167 myfree((char *) state); 168 } 169} 170 171/* psc_endpt_haproxy_lookup - event-driven haproxy client */ 172 173void psc_endpt_haproxy_lookup(VSTREAM *stream, 174 PSC_ENDPT_LOOKUP_FN notify) 175{ 176 const char *myname = "psc_endpt_haproxy_lookup"; 177 PSC_HAPROXY_STATE *state; 178 179 /* 180 * Prepare the per-session state. XXX To improve overload behavior, 181 * maintain a pool of these so that we can reduce memory allocator 182 * activity. 183 */ 184 state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state)); 185 state->stream = stream; 186 state->notify = notify; 187 state->buffer = vstring_alloc(100); 188 189 /* 190 * Read the haproxy line. 191 */ 192 PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_endpt_haproxy_event, 193 (char *) state, var_psc_uproxy_tmout); 194} 195