1 /* $NetBSD: postscreen_send.c,v 1.2 2017/02/14 01:16:47 christos Exp $ */ 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 /* If the 220- greeting was sent, sending 421 would be invalid; 42 /* instead, the client is redirected to the dummy SMTP engine 43 /* which sends the 421 reply at the first legitimate opportunity. 44 /* LICENSE 45 /* .ad 46 /* .fi 47 /* The Secure Mailer license must be distributed with this software. 48 /* AUTHOR(S) 49 /* Wietse Venema 50 /* IBM T.J. Watson Research 51 /* P.O. Box 704 52 /* Yorktown Heights, NY 10598, USA 53 /*--*/ 54 55 /* System library. */ 56 57 #include <sys_defs.h> 58 #include <string.h> 59 #include <errno.h> 60 61 /* Utility library. */ 62 63 #include <msg.h> 64 #include <iostuff.h> 65 #include <connect.h> 66 #include <attr.h> 67 #include <vstream.h> 68 69 /* Global library. */ 70 71 #include <mail_params.h> 72 #include <smtp_reply_footer.h> 73 #include <mail_proto.h> 74 75 /* Application-specific. */ 76 77 #include <postscreen.h> 78 79 /* 80 * This program screens all inbound SMTP connections, so it better not waste 81 * time. 82 */ 83 #define PSC_SEND_SOCK_CONNECT_TIMEOUT 1 84 #define PSC_SEND_SOCK_NOTIFY_TIMEOUT 100 85 86 /* psc_send_reply - send reply to remote SMTP client */ 87 88 int psc_send_reply(PSC_STATE *state, const char *text) 89 { 90 ssize_t start; 91 int ret; 92 93 if (msg_verbose) 94 msg_info("> [%s]:%s: %.*s", state->smtp_client_addr, 95 state->smtp_client_port, (int) strlen(text) - 2, text); 96 97 /* 98 * Append the new text to earlier text that could not be sent because the 99 * output was throttled. 100 */ 101 start = VSTRING_LEN(state->send_buf); 102 vstring_strcat(state->send_buf, text); 103 104 /* 105 * For soft_bounce support, we also fix the REJECT logging before the 106 * dummy SMTP engine calls the psc_send_reply() output routine. We do 107 * some double work, but it is for debugging only. 108 */ 109 if (var_soft_bounce) { 110 if (text[0] == '5') 111 STR(state->send_buf)[start + 0] = '4'; 112 if (text[4] == '5') 113 STR(state->send_buf)[start + 4] = '4'; 114 } 115 116 /* 117 * Append the optional reply footer. 118 */ 119 if (*var_psc_rej_footer && (*text == '4' || *text == '5')) 120 smtp_reply_footer(state->send_buf, start, var_psc_rej_footer, 121 STR(psc_expand_filter), psc_expand_lookup, 122 (void *) state); 123 124 /* 125 * Do a best effort sending text, but don't block when the output is 126 * throttled by a hostile peer. 127 */ 128 ret = write(vstream_fileno(state->smtp_client_stream), 129 STR(state->send_buf), LEN(state->send_buf)); 130 if (ret > 0) 131 vstring_truncate(state->send_buf, ret - LEN(state->send_buf)); 132 if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET) 133 msg_warn("write [%s]:%s: %m", state->smtp_client_addr, 134 state->smtp_client_port); 135 return (ret < 0 && errno != EAGAIN); 136 } 137 138 /* psc_send_socket_close_event - file descriptor has arrived or timeout */ 139 140 static void psc_send_socket_close_event(int event, void *context) 141 { 142 const char *myname = "psc_send_socket_close_event"; 143 PSC_STATE *state = (PSC_STATE *) context; 144 145 if (msg_verbose > 1) 146 msg_info("%s: sq=%d cq=%d event %d on send socket %d from [%s]:%s", 147 myname, psc_post_queue_length, psc_check_queue_length, 148 event, state->smtp_server_fd, state->smtp_client_addr, 149 state->smtp_client_port); 150 151 /* 152 * The real SMTP server has closed the local IPC channel, or we have 153 * reached the limit of our patience. In the latter case it is still 154 * possible that the real SMTP server will receive the socket so we 155 * should not interfere. 156 */ 157 PSC_CLEAR_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, 158 context); 159 if (event == EVENT_TIME) 160 msg_warn("timeout sending connection to service %s", 161 psc_smtpd_service_name); 162 psc_free_session_state(state); 163 } 164 165 /* psc_send_socket - send socket to real SMTP server process */ 166 167 void psc_send_socket(PSC_STATE *state) 168 { 169 const char *myname = "psc_send_socket"; 170 int server_fd; 171 int pass_err; 172 VSTREAM *fp; 173 174 if (msg_verbose > 1) 175 msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", 176 myname, psc_post_queue_length, psc_check_queue_length, 177 vstream_fileno(state->smtp_client_stream), 178 state->smtp_client_addr, state->smtp_client_port); 179 180 /* 181 * Connect to the real SMTP service over a local IPC channel, send the 182 * file descriptor, and close the file descriptor to save resources. 183 * Experience has shown that some systems will discard information when 184 * we close a channel immediately after writing. Thus, we waste resources 185 * waiting for the remote side to close the local IPC channel first. The 186 * good side of waiting is that we learn when the real SMTP server is 187 * falling behind. 188 * 189 * This is where we would forward the connection to an SMTP server that 190 * provides an appropriate level of service for this client class. For 191 * example, a server that is more forgiving, or one that is more 192 * suspicious. Alternatively, we could send attributes along with the 193 * socket with client reputation information, making everything even more 194 * Postfix-specific. 195 */ 196 if ((server_fd = 197 LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING, 198 PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { 199 msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); 200 if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { 201 PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); 202 } else { 203 PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); 204 psc_free_session_state(state); 205 } 206 return; 207 } 208 /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */ 209 fp = vstream_fdopen(server_fd, O_RDWR); 210 pass_err = 211 (LOCAL_SEND_FD(server_fd, 212 vstream_fileno(state->smtp_client_stream)) < 0 213 || (attr_print(fp, ATTR_FLAG_NONE, 214 SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr), 215 SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port), 216 SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr), 217 SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port), 218 ATTR_TYPE_END) || vstream_fflush(fp))); 219 /* XXX Note: no read between attr_print() and vstream_fdclose(). */ 220 (void) vstream_fdclose(fp); 221 if (pass_err != 0) { 222 msg_warn("cannot pass connection to service %s: %m", 223 psc_smtpd_service_name); 224 (void) close(server_fd); 225 if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { 226 PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); 227 } else { 228 PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); 229 psc_free_session_state(state); 230 } 231 return; 232 } else { 233 234 /* 235 * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug 236 * where smtp-source sometimes sees the connection being closed after 237 * it has already received the real SMTP server's 220 greeting! 238 */ 239 #if 0 240 PSC_DEL_CLIENT_STATE(state); 241 #endif 242 PSC_ADD_SERVER_STATE(state, server_fd); 243 PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, 244 (void *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); 245 return; 246 } 247 } 248