1*e8314800Stron /* $NetBSD: postscreen_send.c,v 1.1.1.1 2011/03/02 19:32:26 tron Exp $ */ 2*e8314800Stron 3*e8314800Stron /*++ 4*e8314800Stron /* NAME 5*e8314800Stron /* postscreen_send 3 6*e8314800Stron /* SUMMARY 7*e8314800Stron /* postscreen low-level output 8*e8314800Stron /* SYNOPSIS 9*e8314800Stron /* #include <postscreen.h> 10*e8314800Stron /* 11*e8314800Stron /* int psc_send_reply(state, text) 12*e8314800Stron /* PSC_STATE *state; 13*e8314800Stron /* const char *text; 14*e8314800Stron /* 15*e8314800Stron /* int PSC_SEND_REPLY(state, text) 16*e8314800Stron /* PSC_STATE *state; 17*e8314800Stron /* const char *text; 18*e8314800Stron /* 19*e8314800Stron /* void psc_send_socket(state) 20*e8314800Stron /* PSC_STATE *state; 21*e8314800Stron /* DESCRIPTION 22*e8314800Stron /* psc_send_reply() sends the specified text to the specified 23*e8314800Stron /* remote SMTP client. In case of an immediate error, it logs 24*e8314800Stron /* a warning (except EPIPE) with the client address and port, 25*e8314800Stron /* and returns a non-zero result (all errors including EPIPE). 26*e8314800Stron /* 27*e8314800Stron /* psc_send_reply() does a best effort to send the reply, but 28*e8314800Stron /* it won't block when the output is throttled by a hostile 29*e8314800Stron /* peer. 30*e8314800Stron /* 31*e8314800Stron /* PSC_SEND_REPLY() is a legacy wrapper for psc_send_reply(). 32*e8314800Stron /* It will eventually be replaced by its expansion. 33*e8314800Stron /* 34*e8314800Stron /* psc_send_socket() sends the specified socket to the real 35*e8314800Stron /* Postfix SMTP server. The socket is delivered in the background. 36*e8314800Stron /* This function must be called after all other session-related 37*e8314800Stron /* work is finished including postscreen cache updates. 38*e8314800Stron /* 39*e8314800Stron /* In case of an immediate error, psc_send_socket() sends a 421 40*e8314800Stron /* reply to the remote SMTP client and closes the connection. 41*e8314800Stron /* LICENSE 42*e8314800Stron /* .ad 43*e8314800Stron /* .fi 44*e8314800Stron /* The Secure Mailer license must be distributed with this software. 45*e8314800Stron /* AUTHOR(S) 46*e8314800Stron /* Wietse Venema 47*e8314800Stron /* IBM T.J. Watson Research 48*e8314800Stron /* P.O. Box 704 49*e8314800Stron /* Yorktown Heights, NY 10598, USA 50*e8314800Stron /*--*/ 51*e8314800Stron 52*e8314800Stron /* System library. */ 53*e8314800Stron 54*e8314800Stron #include <sys_defs.h> 55*e8314800Stron #include <string.h> 56*e8314800Stron #include <errno.h> 57*e8314800Stron 58*e8314800Stron /* Utility library. */ 59*e8314800Stron 60*e8314800Stron #include <msg.h> 61*e8314800Stron #include <iostuff.h> 62*e8314800Stron #include <connect.h> 63*e8314800Stron 64*e8314800Stron /* Global library. */ 65*e8314800Stron 66*e8314800Stron #include <mail_params.h> 67*e8314800Stron #include <smtp_reply_footer.h> 68*e8314800Stron 69*e8314800Stron /* Application-specific. */ 70*e8314800Stron 71*e8314800Stron #include <postscreen.h> 72*e8314800Stron 73*e8314800Stron /* 74*e8314800Stron * This program screens all inbound SMTP connections, so it better not waste 75*e8314800Stron * time. 76*e8314800Stron */ 77*e8314800Stron #define PSC_SEND_SOCK_CONNECT_TIMEOUT 1 78*e8314800Stron #define PSC_SEND_SOCK_NOTIFY_TIMEOUT 100 79*e8314800Stron 80*e8314800Stron /* psc_send_reply - send reply to remote SMTP client */ 81*e8314800Stron 82*e8314800Stron int psc_send_reply(PSC_STATE *state, const char *text) 83*e8314800Stron { 84*e8314800Stron ssize_t start; 85*e8314800Stron int ret; 86*e8314800Stron 87*e8314800Stron if (msg_verbose) 88*e8314800Stron msg_info("> [%s]:%s: %.*s", state->smtp_client_addr, 89*e8314800Stron state->smtp_client_port, (int) strlen(text) - 2, text); 90*e8314800Stron 91*e8314800Stron /* 92*e8314800Stron * Append the new text to earlier text that could not be sent because the 93*e8314800Stron * output was throttled. 94*e8314800Stron */ 95*e8314800Stron start = VSTRING_LEN(state->send_buf); 96*e8314800Stron vstring_strcat(state->send_buf, text); 97*e8314800Stron 98*e8314800Stron /* 99*e8314800Stron * For soft_bounce support, we also fix the REJECT logging before the 100*e8314800Stron * dummy SMTP engine calls the psc_send_reply() output routine. We do 101*e8314800Stron * some double work, but it is for debugging only. 102*e8314800Stron */ 103*e8314800Stron if (var_soft_bounce) { 104*e8314800Stron if (text[0] == '5') 105*e8314800Stron STR(state->send_buf)[start + 0] = '4'; 106*e8314800Stron if (text[4] == '5') 107*e8314800Stron STR(state->send_buf)[start + 4] = '4'; 108*e8314800Stron } 109*e8314800Stron 110*e8314800Stron /* 111*e8314800Stron * Append the optional reply footer. 112*e8314800Stron */ 113*e8314800Stron if (*var_psc_rej_footer && (*text == '4' || *text == '5')) 114*e8314800Stron smtp_reply_footer(state->send_buf, start, var_psc_rej_footer, 115*e8314800Stron STR(psc_expand_filter), psc_expand_lookup, 116*e8314800Stron (char *) state); 117*e8314800Stron 118*e8314800Stron /* 119*e8314800Stron * Do a best effort sending text, but don't block when the output is 120*e8314800Stron * throttled by a hostile peer. 121*e8314800Stron */ 122*e8314800Stron ret = write(vstream_fileno(state->smtp_client_stream), 123*e8314800Stron STR(state->send_buf), LEN(state->send_buf)); 124*e8314800Stron if (ret > 0) 125*e8314800Stron vstring_truncate(state->send_buf, ret - LEN(state->send_buf)); 126*e8314800Stron if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET) 127*e8314800Stron msg_warn("write [%s]:%s: %m", state->smtp_client_addr, 128*e8314800Stron state->smtp_client_port); 129*e8314800Stron return (ret < 0 && errno != EAGAIN); 130*e8314800Stron } 131*e8314800Stron 132*e8314800Stron /* psc_send_socket_close_event - file descriptor has arrived or timeout */ 133*e8314800Stron 134*e8314800Stron static void psc_send_socket_close_event(int event, char *context) 135*e8314800Stron { 136*e8314800Stron const char *myname = "psc_send_socket_close_event"; 137*e8314800Stron PSC_STATE *state = (PSC_STATE *) context; 138*e8314800Stron 139*e8314800Stron if (msg_verbose > 1) 140*e8314800Stron msg_info("%s: sq=%d cq=%d event %d on send socket %d from [%s]:%s", 141*e8314800Stron myname, psc_post_queue_length, psc_check_queue_length, 142*e8314800Stron event, state->smtp_server_fd, state->smtp_client_addr, 143*e8314800Stron state->smtp_client_port); 144*e8314800Stron 145*e8314800Stron /* 146*e8314800Stron * The real SMTP server has closed the local IPC channel, or we have 147*e8314800Stron * reached the limit of our patience. In the latter case it is still 148*e8314800Stron * possible that the real SMTP server will receive the socket so we 149*e8314800Stron * should not interfere. 150*e8314800Stron */ 151*e8314800Stron PSC_CLEAR_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, 152*e8314800Stron context); 153*e8314800Stron if (event == EVENT_TIME) 154*e8314800Stron msg_warn("timeout sending connection to service %s", 155*e8314800Stron psc_smtpd_service_name); 156*e8314800Stron psc_free_session_state(state); 157*e8314800Stron } 158*e8314800Stron 159*e8314800Stron /* psc_send_socket - send socket to real SMTP server process */ 160*e8314800Stron 161*e8314800Stron void psc_send_socket(PSC_STATE *state) 162*e8314800Stron { 163*e8314800Stron const char *myname = "psc_send_socket"; 164*e8314800Stron int server_fd; 165*e8314800Stron 166*e8314800Stron if (msg_verbose > 1) 167*e8314800Stron msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", 168*e8314800Stron myname, psc_post_queue_length, psc_check_queue_length, 169*e8314800Stron vstream_fileno(state->smtp_client_stream), 170*e8314800Stron state->smtp_client_addr, state->smtp_client_port); 171*e8314800Stron 172*e8314800Stron /* 173*e8314800Stron * Connect to the real SMTP service over a local IPC channel, send the 174*e8314800Stron * file descriptor, and close the file descriptor to save resources. 175*e8314800Stron * Experience has shown that some systems will discard information when 176*e8314800Stron * we close a channel immediately after writing. Thus, we waste resources 177*e8314800Stron * waiting for the remote side to close the local IPC channel first. The 178*e8314800Stron * good side of waiting is that we learn when the real SMTP server is 179*e8314800Stron * falling behind. 180*e8314800Stron * 181*e8314800Stron * This is where we would forward the connection to an SMTP server that 182*e8314800Stron * provides an appropriate level of service for this client class. For 183*e8314800Stron * example, a server that is more forgiving, or one that is more 184*e8314800Stron * suspicious. Alternatively, we could send attributes along with the 185*e8314800Stron * socket with client reputation information, making everything even more 186*e8314800Stron * Postfix-specific. 187*e8314800Stron */ 188*e8314800Stron if ((server_fd = 189*e8314800Stron PASS_CONNECT(psc_smtpd_service_name, NON_BLOCKING, 190*e8314800Stron PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { 191*e8314800Stron msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); 192*e8314800Stron PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); 193*e8314800Stron psc_free_session_state(state); 194*e8314800Stron return; 195*e8314800Stron } 196*e8314800Stron PSC_ADD_SERVER_STATE(state, server_fd); 197*e8314800Stron if (LOCAL_SEND_FD(state->smtp_server_fd, 198*e8314800Stron vstream_fileno(state->smtp_client_stream)) < 0) { 199*e8314800Stron msg_warn("cannot pass connection to service %s: %m", 200*e8314800Stron psc_smtpd_service_name); 201*e8314800Stron PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); 202*e8314800Stron psc_free_session_state(state); 203*e8314800Stron return; 204*e8314800Stron } else { 205*e8314800Stron 206*e8314800Stron /* 207*e8314800Stron * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug 208*e8314800Stron * where smtp-source sometimes sees the connection being closed after 209*e8314800Stron * it has already received the real SMTP server's 220 greeting! 210*e8314800Stron */ 211*e8314800Stron #if 0 212*e8314800Stron PSC_DEL_CLIENT_STATE(state); 213*e8314800Stron #endif 214*e8314800Stron PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, 215*e8314800Stron (char *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); 216*e8314800Stron return; 217*e8314800Stron } 218*e8314800Stron } 219