/*++ /* NAME /* postscreen_send 3 /* SUMMARY /* postscreen low-level output /* SYNOPSIS /* #include /* /* void pcs_send_pre_jail_init(void) /* /* int psc_send_reply(state, text) /* PSC_STATE *state; /* const char *text; /* /* int PSC_SEND_REPLY(state, text) /* PSC_STATE *state; /* const char *text; /* /* void psc_send_socket(state) /* PSC_STATE *state; /* DESCRIPTION /* pcs_send_pre_jail_init() performs one-time initialization. /* /* psc_send_reply() sends the specified text to the specified /* remote SMTP client. In case of an immediate error, it logs /* a warning (except EPIPE) with the client address and port, /* and returns a non-zero result (all errors including EPIPE). /* /* psc_send_reply() does a best effort to send the reply, but /* it won't block when the output is throttled by a hostile /* peer. /* /* PSC_SEND_REPLY() is a legacy wrapper for psc_send_reply(). /* It will eventually be replaced by its expansion. /* /* psc_send_socket() sends the specified socket to the real /* Postfix SMTP server. The socket is delivered in the background. /* This function must be called after all other session-related /* work is finished including postscreen cache updates. /* /* In case of an immediate error, psc_send_socket() sends a 421 /* reply to the remote SMTP client and closes the connection. /* If the 220- greeting was sent, sending 421 would be invalid; /* instead, the client is redirected to the dummy SMTP engine /* which sends the 421 reply at the first legitimate opportunity. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include #include #include /* Utility library. */ #include #include #include #include #include /* Global library. */ #include #include #include #include /* Application-specific. */ #include static MAPS *psc_rej_ftr_maps; /* * This program screens all inbound SMTP connections, so it better not waste * time. */ #define PSC_SEND_SOCK_CONNECT_TIMEOUT 1 #define PSC_SEND_SOCK_NOTIFY_TIMEOUT 100 /* pcs_send_pre_jail_init - initialize */ void pcs_send_pre_jail_init(void) { static int init_count = 0; if (init_count++ != 0) msg_panic("pcs_send_pre_jail_init: multiple calls"); /* * SMTP server reject footer. */ if (*var_psc_rej_ftr_maps) psc_rej_ftr_maps = maps_create(VAR_SMTPD_REJ_FTR_MAPS, var_psc_rej_ftr_maps, DICT_FLAG_LOCK); } /* psc_get_footer - find that footer */ static const char *psc_get_footer(const char *text, ssize_t text_len) { static VSTRING *footer_buf = 0; if (footer_buf == 0) footer_buf = vstring_alloc(100); /* Strip the \r\n for consistency with smtpd. */ vstring_strncpy(footer_buf, text, text_len); return (psc_maps_find(psc_rej_ftr_maps, STR(footer_buf), 0)); } /* psc_send_reply - send reply to remote SMTP client */ int psc_send_reply(PSC_STATE *state, const char *text) { ssize_t start; int ret; const char *footer; ssize_t text_len = strlen(text) - 2; if (msg_verbose) msg_info("> [%s]:%s: %.*s", state->smtp_client_addr, state->smtp_client_port, (int) text_len, text); /* * Append the new text to earlier text that could not be sent because the * output was throttled. */ start = VSTRING_LEN(state->send_buf); vstring_strcat(state->send_buf, text); /* * For soft_bounce support, we also fix the REJECT logging before the * dummy SMTP engine calls the psc_send_reply() output routine. We do * some double work, but it is for debugging only. */ if (var_soft_bounce) { if (text[0] == '5') STR(state->send_buf)[start + 0] = '4'; if (text[4] == '5') STR(state->send_buf)[start + 4] = '4'; } /* * Append the optional reply footer. */ if ((*text == '4' || *text == '5') && ((psc_rej_ftr_maps != 0 && (footer = psc_get_footer(text, text_len)) != 0) || *(footer = var_psc_rej_footer) != 0)) smtp_reply_footer(state->send_buf, start, footer, STR(psc_expand_filter), psc_expand_lookup, (void *) state); /* * Do a best effort sending text, but don't block when the output is * throttled by a hostile peer. */ ret = write(vstream_fileno(state->smtp_client_stream), STR(state->send_buf), LEN(state->send_buf)); if (ret > 0) vstring_truncate(state->send_buf, ret - LEN(state->send_buf)); if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET) msg_warn("write [%s]:%s: %m", state->smtp_client_addr, state->smtp_client_port); return (ret < 0 && errno != EAGAIN); } /* psc_send_socket_close_event - file descriptor has arrived or timeout */ static void psc_send_socket_close_event(int event, void *context) { const char *myname = "psc_send_socket_close_event"; PSC_STATE *state = (PSC_STATE *) context; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d event %d on send socket %d from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, event, state->smtp_server_fd, state->smtp_client_addr, state->smtp_client_port); /* * The real SMTP server has closed the local IPC channel, or we have * reached the limit of our patience. In the latter case it is still * possible that the real SMTP server will receive the socket so we * should not interfere. */ PSC_CLEAR_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, context); if (event == EVENT_TIME) msg_warn("timeout sending connection to service %s", psc_smtpd_service_name); psc_free_session_state(state); } /* psc_send_socket - send socket to real SMTP server process */ void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; int pass_err; VSTREAM *fp; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, vstream_fileno(state->smtp_client_stream), state->smtp_client_addr, state->smtp_client_port); /* * Connect to the real SMTP service over a local IPC channel, send the * file descriptor, and close the file descriptor to save resources. * Experience has shown that some systems will discard information when * we close a channel immediately after writing. Thus, we waste resources * waiting for the remote side to close the local IPC channel first. The * good side of waiting is that we learn when the real SMTP server is * falling behind. * * This is where we would forward the connection to an SMTP server that * provides an appropriate level of service for this client class. For * example, a server that is more forgiving, or one that is more * suspicious. Alternatively, we could send attributes along with the * socket with client reputation information, making everything even more * Postfix-specific. */ if ((server_fd = LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING, PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); psc_free_session_state(state); } return; } /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */ fp = vstream_fdopen(server_fd, O_RDWR); pass_err = (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 0 || (attr_print(fp, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port), ATTR_TYPE_END) || vstream_fflush(fp))); /* XXX Note: no read between attr_print() and vstream_fdclose(). */ (void) vstream_fdclose(fp); if (pass_err != 0) { msg_warn("cannot pass connection to service %s: %m", psc_smtpd_service_name); (void) close(server_fd); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); psc_free_session_state(state); } return; } else { /* * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug * where smtp-source sometimes sees the connection being closed after * it has already received the real SMTP server's 220 greeting! */ #if 0 PSC_DEL_CLIENT_STATE(state); #endif PSC_ADD_SERVER_STATE(state, server_fd); PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, (void *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; } }