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