void psc_conclude(PSC_STATE *state) { const char *myname = "psc_conclude"; if (msg_verbose) msg_info("flags for %s: %s", myname, psc_print_state_flags(state->flags, myname)); /* * Handle clients that passed at least one test other than permanent * whitelisting, and that didn't fail any test including permanent * blacklisting. There may still be unfinished tests; those tests will * need to be completed when the client returns in a later session. */ if (state->flags & PSC_STATE_MASK_ANY_FAIL) state->flags &= ~PSC_STATE_MASK_ANY_PASS; /* * Log our final blessing when all unfinished tests were completed. */ if ((state->flags & PSC_STATE_MASK_ANY_PASS) != 0 && (state->flags & PSC_STATE_MASK_ANY_PASS) == PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_ANY_TODO)) msg_info("PASS %s [%s]:%s", (state->flags & PSC_STATE_FLAG_NEW) == 0 || state->client_info->pass_new_count++ > 0 ? "OLD" : "NEW", PSC_CLIENT_ADDR_PORT(state)); /* * Update the postscreen cache. This still supports a scenario where a * client gets whitelisted in the course of multiple sessions, as long as * that client does not "fail" any test. Don't try to optimize away cache * updates; we want cached information to be up-to-date even if a test * result is renewed during overlapping SMTP sessions, and even if * 'postfix reload' happens in the middle of that. */ if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) != 0 && psc_cache_map != 0) { psc_print_tests(psc_temp, state); psc_cache_update(psc_cache_map, state->smtp_client_addr, STR(psc_temp)); } /* * Either hand off the socket to a real SMTP engine, or say bye-bye. */ if ((state->flags & PSC_STATE_FLAG_NOFORWARD) == 0) { psc_send_socket(state); } else { if ((state->flags & PSC_STATE_FLAG_HANGUP) == 0) (void) PSC_SEND_REPLY(state, state->final_reply); msg_info("DISCONNECT [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_free_session_state(state); } }
static void psc_send_socket_close_event(int event, char *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); }
void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; 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 = PASS_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; } if (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 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, (char *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; } }