/*++ /* NAME /* postscreen_misc 3 /* SUMMARY /* postscreen misc routines /* SYNOPSIS /* #include /* /* char *psc_format_delta_time(buf, tv, delta) /* VSTRING *buf; /* struct timeval tv; /* DELTA_TIME *delta; /* /* void psc_conclude(state) /* PSC_STATE *state; /* /* void psc_hangup_event(state) /* PSC_STATE *state; /* DESCRIPTION /* psc_format_delta_time() computes the time difference between /* tv (past) and the present, formats the time difference with /* sub-second resolution in a human-readable way, and returns /* the integer time difference in seconds through the delta /* argument. /* /* psc_conclude() logs when a client passes all necessary tests, /* updates the postscreen cache for any testes that were passed, /* and either forwards the connection to a real SMTP server or /* replies with the text in state->error_reply and hangs up the /* connection (by default, state->error_reply is set to a /* default 421 reply). /* /* psc_hangup_event() cleans up after a client connection breaks /* unexpectedly. If logs the test where the break happened, /* and how much time as spent in that test before the connection /* broke. /* 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 /* Utility library. */ #include #include #include #include /* Global library. */ #include /* Application-specific. */ #include /* psc_format_delta_time - pretty-formatted delta time */ char *psc_format_delta_time(VSTRING *buf, struct timeval tv, DELTA_TIME *delta) { DELTA_TIME pdelay; struct timeval now; GETTIMEOFDAY(&now); PSC_CALC_DELTA(pdelay, now, tv); VSTRING_RESET(buf); format_tv(buf, pdelay.dt_sec, pdelay.dt_usec, SIG_DIGS, var_delay_max_res); *delta = pdelay; return (STR(buf)); } /* psc_conclude - bring this session to a conclusion */ 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 * allowlisting, and that didn't fail any test including permanent * denylisting. 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 allowlisted 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); } } /* psc_hangup_event - handle unexpected disconnect */ void psc_hangup_event(PSC_STATE *state) { DELTA_TIME elapsed; /* * Sessions can break at any time, even after the client passes all tests * (some MTAs including Postfix don't send QUIT when connection reuse is * enabled). This must not be treated as a protocol test failure. * * Log the current test phase, and the elapsed time after the start of that * phase. */ state->flags |= PSC_STATE_FLAG_HANGUP; msg_info("HANGUP after %s from [%s]:%s in %s", psc_format_delta_time(psc_temp, state->start_time, &elapsed), PSC_CLIENT_ADDR_PORT(state), state->test_name); state->flags |= PSC_STATE_FLAG_NOFORWARD; psc_conclude(state); }