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