void psc_parse_tests(PSC_STATE *state, const char *stamp_str, time_t time_value) { unsigned long pregr_stamp; unsigned long dnsbl_stamp; unsigned long pipel_stamp; unsigned long nsmtp_stamp; unsigned long barlf_stamp; unsigned long penal_stamp; #ifdef NONPROD time_t penalty_left; #endif /* * We don't know what tests have expired or have never passed. */ state->flags = 0; /* * Parse the cache entry, and allow for older postscreen versions that * implemented fewer tests. We pretend that the newer tests were disabled * at the time that the cache entry was written. * * Flag the cache entry as "new" when the cache entry has fields for all * enabled tests, but the remote SMTP client has not yet passed all those * tests. */ switch (sscanf(stamp_str, "%lu;%lu;%lu;%lu;%lu;%lu", &pregr_stamp, &dnsbl_stamp, &pipel_stamp, &nsmtp_stamp, &barlf_stamp, &penal_stamp)) { case 0: pregr_stamp = PSC_TIME_STAMP_DISABLED; case 1: dnsbl_stamp = PSC_TIME_STAMP_DISABLED; case 2: pipel_stamp = PSC_TIME_STAMP_DISABLED; case 3: nsmtp_stamp = PSC_TIME_STAMP_DISABLED; case 4: barlf_stamp = PSC_TIME_STAMP_DISABLED; case 5: penal_stamp = PSC_TIME_STAMP_DISABLED; default: break; } state->pregr_stamp = pregr_stamp; state->dnsbl_stamp = dnsbl_stamp; state->pipel_stamp = pipel_stamp; state->nsmtp_stamp = nsmtp_stamp; state->barlf_stamp = barlf_stamp; state->penal_stamp = penal_stamp; if (pregr_stamp == PSC_TIME_STAMP_NEW || dnsbl_stamp == PSC_TIME_STAMP_NEW || pipel_stamp == PSC_TIME_STAMP_NEW || nsmtp_stamp == PSC_TIME_STAMP_NEW || barlf_stamp == PSC_TIME_STAMP_NEW) state->flags |= PSC_STATE_FLAG_NEW; /* * Don't flag disabled tests as "todo", because there would be no way to * make those bits go away. */ if (PSC_PREGR_TEST_ENABLE() && time_value > state->pregr_stamp) state->flags |= PSC_STATE_FLAG_PREGR_TODO; if (PSC_DNSBL_TEST_ENABLE() && time_value > state->dnsbl_stamp) state->flags |= PSC_STATE_FLAG_DNSBL_TODO; if (var_psc_pipel_enable && time_value > state->pipel_stamp) state->flags |= PSC_STATE_FLAG_PIPEL_TODO; if (var_psc_nsmtp_enable && time_value > state->nsmtp_stamp) state->flags |= PSC_STATE_FLAG_NSMTP_TODO; if (var_psc_barlf_enable && time_value > state->barlf_stamp) state->flags |= PSC_STATE_FLAG_BARLF_TODO; /* * If any test has expired, proactively refresh tests that will expire * soon. This can increase the occurrence of client-visible delays, but * avoids questions about why a client can pass some test and then fail * within seconds. The proactive refresh time is really a surrogate for * the user's curiosity level, and therefore hard to choose optimally. */ #ifdef VAR_PSC_REFRESH_TIME if ((state->flags & PSC_STATE_MASK_ANY_TODO) != 0 && var_psc_refresh_time > 0) { time_t refresh_time = time_value + var_psc_refresh_time; if (PSC_PREGR_TEST_ENABLE() && refresh_time > state->pregr_stamp) state->flags |= PSC_STATE_FLAG_PREGR_TODO; if (PSC_DNSBL_TEST_ENABLE() && refresh_time > state->dnsbl_stamp) state->flags |= PSC_STATE_FLAG_DNSBL_TODO; if (var_psc_pipel_enable && refresh_time > state->pipel_stamp) state->flags |= PSC_STATE_FLAG_PIPEL_TODO; if (var_psc_nsmtp_enable && refresh_time > state->nsmtp_stamp) state->flags |= PSC_STATE_FLAG_NSMTP_TODO; if (var_psc_barlf_enable && refresh_time > state->barlf_stamp) state->flags |= PSC_STATE_FLAG_BARLF_TODO; } #endif /* * Gratuitously make postscreen logging more useful by turning on all * enabled pre-handshake tests when any pre-handshake test is turned on. * * XXX Don't enable PREGREET gratuitously before the test expires. With a * short TTL for DNSBL whitelisting, turning on PREGREET would force a * full postscreen_greet_wait too frequently. */ #if 0 if (state->flags & PSC_STATE_MASK_EARLY_TODO) { if (PSC_PREGR_TEST_ENABLE()) state->flags |= PSC_STATE_FLAG_PREGR_TODO; if (PSC_DNSBL_TEST_ENABLE()) state->flags |= PSC_STATE_FLAG_DNSBL_TODO; } #endif /* * Apply unexpired penalty for past behavior. * * XXX Before we can drop connections, change this function to return * success/fail, to inform the caller that the state object no longer * exists. */ #ifdef NONPROD if ((penalty_left = state->penal_stamp - event_time()) > 0) { msg_info("PENALTY %ld for %s", (long) penalty_left, state->smtp_client_addr); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); #if 0 switch (psc_penal_action) { case PSC_ACT_DROP: PSC_DROP_SESSION_STATE(state, "421 4.3.2 Service currently unavailable\r\n"); break; case PSC_ACT_ENFORCE: #endif PSC_ENFORCE_SESSION_STATE(state, "450 4.3.2 Service currently unavailable\r\n"); #if 0 break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PENAL_FAIL); break; default: msg_panic("%s: unknown penalty action value %d", myname, psc_penal_action); } #endif } #endif /* NONPROD */ }
static void psc_early_event(int event, char *context) { const char *myname = "psc_early_event"; PSC_STATE *state = (PSC_STATE *) context; char read_buf[PSC_READ_BUF_SIZE]; int read_count; DELTA_TIME elapsed; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s", myname, psc_post_queue_length, psc_check_queue_length, event, vstream_fileno(state->smtp_client_stream), state->smtp_client_addr, state->smtp_client_port, psc_print_state_flags(state->flags, myname)); PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream), psc_early_event, context); /* * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a * memory leak. * * XXX We can avoid "forgetting" to do this by keeping a pointer to the * DNSBL lookup buffer in the PSC_STATE structure. This also allows us to * shave off a hash table lookup when retrieving the DNSBL result. * * A direct pointer increases the odds of dangling pointers. Hash-table * lookup is safer, and that is why it's done that way. */ switch (event) { /* * We either reached the end of the early tests time limit, or all * early tests completed before the pregreet timer would go off. */ case EVENT_TIME: /* * Check if the SMTP client spoke before its turn. */ if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0 && (state->flags & PSC_STATE_MASK_PREGR_FAIL_DONE) == 0) { state->pregr_stamp = event_time() + var_psc_pregr_ttl; PSC_PASS_SESSION_STATE(state, "pregreet test", PSC_STATE_FLAG_PREGR_PASS); } if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL) && psc_pregr_action == PSC_ACT_IGNORE) { PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL); /* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */ } /* * Collect the DNSBL score, and whitelist other tests if applicable. * Note: this score will be partial when some DNS lookup did not * complete before the pregreet timer expired. * * If the client is DNS blocklisted, drop the connection, send the * client to a dummy protocol engine, or continue to the next test. */ #define PSC_DNSBL_FORMAT \ "%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n" #define NO_DNSBL_SCORE INT_MAX if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) { if (state->dnsbl_score == NO_DNSBL_SCORE) { state->dnsbl_score = psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, state->dnsbl_index); if (var_psc_dnsbl_wthresh < 0) psc_whitelist_non_dnsbl(state); } if (state->dnsbl_score < var_psc_dnsbl_thresh) { state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl; PSC_PASS_SESSION_STATE(state, "dnsbl test", PSC_STATE_FLAG_DNSBL_PASS); } else { msg_info("DNSBL rank %d for [%s]:%s", state->dnsbl_score, PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL); switch (psc_dnsbl_action) { case PSC_ACT_DROP: state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), PSC_DNSBL_FORMAT, "521", state->smtp_client_addr, state->dnsbl_name); PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply)); return; case PSC_ACT_ENFORCE: state->dnsbl_reply = vstring_sprintf(vstring_alloc(100), PSC_DNSBL_FORMAT, "550", state->smtp_client_addr, state->dnsbl_name); PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply)); break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL); /* Not: PSC_PASS_SESSION_STATE. Repeat this test. */ break; default: msg_panic("%s: unknown dnsbl action value %d", myname, psc_dnsbl_action); } } } /* * Pass the connection to a real SMTP server, or enter the dummy * engine for deep tests. */ if ((state->flags & PSC_STATE_FLAG_NOFORWARD) != 0 || ((state->flags & PSC_STATE_MASK_SMTPD_PASS) != PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_SMTPD_TODO))) psc_smtpd_tests(state); else psc_conclude(state); return; /* * EOF, or the client spoke before its turn. We simply drop the * connection, or we continue waiting and allow DNS replies to * trickle in. */ default: if ((read_count = recv(vstream_fileno(state->smtp_client_stream), read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { /* Avoid memory leak. */ if (state->dnsbl_score == NO_DNSBL_SCORE && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) (void) psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, state->dnsbl_index); /* XXX Wait for DNS replies to come in. */ psc_hangup_event(state); return; } read_buf[read_count] = 0; escape(psc_escape_buf, read_buf, read_count); msg_info("PREGREET %d after %s from [%s]:%s: %.100s", read_count, psc_format_delta_time(psc_temp, state->start_time, &elapsed), PSC_CLIENT_ADDR_PORT(state), STR(psc_escape_buf)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL); switch (psc_pregr_action) { case PSC_ACT_DROP: /* Avoid memory leak. */ if (state->dnsbl_score == NO_DNSBL_SCORE && (state->flags & PSC_STATE_FLAG_DNSBL_TODO)) (void) psc_dnsbl_retrieve(state->smtp_client_addr, &state->dnsbl_name, state->dnsbl_index); PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n"); return; case PSC_ACT_ENFORCE: /* We call psc_dnsbl_retrieve() when the timer expires. */ PSC_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n"); break; case PSC_ACT_IGNORE: /* We call psc_dnsbl_retrieve() when the timer expires. */ /* We must handle this case after the timer expires. */ break; default: msg_panic("%s: unknown pregreet action value %d", myname, psc_pregr_action); } /* * Terminate the greet delay if we're just waiting for the pregreet * test to complete. It is safe to call psc_early_event directly, * since we are already in that function. * * XXX After this code passes all tests, swap around the two blocks in * this switch statement and fall through from EVENT_READ into * EVENT_TIME, instead of calling psc_early_event recursively. */ state->flags |= PSC_STATE_FLAG_PREGR_DONE; if (elapsed.dt_sec >= PSC_EFF_GREET_WAIT || ((state->flags & PSC_STATE_MASK_EARLY_DONE) == PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO))) psc_early_event(EVENT_TIME, context); else event_request_timer(psc_early_event, context, PSC_EFF_GREET_WAIT - elapsed.dt_sec); return; } }
static void psc_service(VSTREAM *smtp_client_stream, char *unused_service, char **unused_argv) { const char *myname = "psc_service"; PSC_STATE *state; struct sockaddr_storage addr_storage; SOCKADDR_SIZE addr_storage_len = sizeof(addr_storage); MAI_HOSTADDR_STR smtp_client_addr; MAI_SERVPORT_STR smtp_client_port; MAI_HOSTADDR_STR smtp_server_addr; MAI_SERVPORT_STR smtp_server_port; int aierr; const char *stamp_str; int saved_flags; /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This program handles all incoming connections, so it must not block. * We use event-driven code for all operations that introduce latency. * * Note: instead of using VSTREAM-level timeouts, we enforce limits on the * total amount of time to receive a complete SMTP command line. */ non_blocking(vstream_fileno(smtp_client_stream), NON_BLOCKING); /* * We use the event_server framework. This means we get already-accepted * connections so we have to invoke getpeername() to find out the remote * address and port. */ /* Best effort - if this non-blocking write(2) fails, so be it. */ #define PSC_SERVICE_DISCONNECT_AND_RETURN(stream) do { \ (void) write(vstream_fileno(stream), \ "421 4.3.2 No system resources\r\n", \ sizeof("421 4.3.2 No system resources\r\n") - 1); \ event_server_disconnect(stream); \ return; \ } while (0); /* * Look up the remote SMTP client address and port. */ if (getpeername(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getpeername: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the remote SMTP client address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_client_addr, &smtp_client_port, 0)) != 0) { msg_warn("cannot convert client address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_client_addr.buf, 7) == 0) memmove(smtp_client_addr.buf, smtp_client_addr.buf + 7, sizeof(smtp_client_addr.buf) - 7); if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d connect from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, smtp_client_addr.buf, smtp_client_port.buf); /* * Look up the local SMTP server address and port. */ if (getsockname(vstream_fileno(smtp_client_stream), (struct sockaddr *) & addr_storage, &addr_storage_len) < 0) { msg_warn("getsockname: %m -- dropping this connection"); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } /* * Convert the local SMTP server address and port to printable form for * logging and access control. */ if ((aierr = sockaddr_to_hostaddr((struct sockaddr *) & addr_storage, addr_storage_len, &smtp_server_addr, &smtp_server_port, 0)) != 0) { msg_warn("cannot convert server address/port to string: %s" " -- dropping this connection", MAI_STRERROR(aierr)); PSC_SERVICE_DISCONNECT_AND_RETURN(smtp_client_stream); } if (strncasecmp("::ffff:", smtp_server_addr.buf, 7) == 0) memmove(smtp_server_addr.buf, smtp_server_addr.buf + 7, sizeof(smtp_server_addr.buf) - 7); msg_info("CONNECT from [%s]:%s to [%s]:%s", smtp_client_addr.buf, smtp_client_port.buf, smtp_server_addr.buf, smtp_server_port.buf); /* * Bundle up all the loose session pieces. This zeroes all flags and time * stamps. */ state = psc_new_session_state(smtp_client_stream, smtp_client_addr.buf, smtp_client_port.buf); /* * Reply with 421 when the client has too many open connections. */ if (var_psc_cconn_limit > 0 && state->client_concurrency > var_psc_cconn_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: too many connections", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.7.0 Error: too many connections\r\n"); return; } /* * Reply with 421 when we can't forward more connections. */ if (var_psc_post_queue_limit > 0 && psc_post_queue_length >= var_psc_post_queue_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: all server ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All server ports are busy\r\n"); return; } /* * The permanent white/blacklist has highest precedence. */ if (psc_acl != 0) { switch (psc_acl_eval(state, psc_acl, VAR_PSC_ACL)) { /* * Permanently blacklisted. */ case PSC_ACL_ACT_BLACKLIST: msg_info("BLACKLISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); switch (psc_blist_action) { case PSC_ACT_DROP: PSC_DROP_SESSION_STATE(state, "521 5.3.2 Service currently unavailable\r\n"); return; case PSC_ACT_ENFORCE: PSC_ENFORCE_SESSION_STATE(state, "550 5.3.2 Service currently unavailable\r\n"); break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); /* * Not: PSC_PASS_SESSION_STATE. Repeat this test the next * time. */ break; default: msg_panic("%s: unknown blacklist action value %d", myname, psc_blist_action); } break; /* * Permanently whitelisted. */ case PSC_ACL_ACT_WHITELIST: msg_info("WHITELISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; /* * Other: dunno (don't know) or error. */ default: break; } } /* * The temporary whitelist (i.e. the postscreen cache) has the lowest * precedence. This cache contains information about the results of prior * tests. Whitelist the client when all enabled test results are still * valid. */ if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0 && psc_cache_map != 0 && (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) { saved_flags = state->flags; psc_parse_tests(state, stamp_str, event_time()); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: cached + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); if ((state->flags & PSC_STATE_MASK_ANY_TODO_FAIL) == 0) { msg_info("PASS OLD [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; } } else { saved_flags = state->flags; psc_new_tests(state); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: new + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); } /* * Don't whitelist clients that connect to backup MX addresses. Fail * "closed" on error. */ if (addr_match_list_match(psc_wlist_if, smtp_server_addr.buf) == 0) { state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD); msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); } /* * Reply with 421 when we can't analyze more connections. That also means * no deep protocol tests when the noforward flag is raised. */ if (var_psc_pre_queue_limit > 0 && psc_check_queue_length - psc_post_queue_length >= var_psc_pre_queue_limit) { msg_info("reject: connect from [%s]:%s: all screening ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All screening ports are busy\r\n"); return; } /* * If the client has no up-to-date results for some tests, do those tests * first. Otherwise, skip the tests and hand off the connection. */ if (state->flags & PSC_STATE_MASK_EARLY_TODO) psc_early_tests(state); else if (state->flags & (PSC_STATE_MASK_SMTPD_TODO | PSC_STATE_FLAG_NOFORWARD)) psc_smtpd_tests(state); else psc_conclude(state); }
static void psc_endpt_lookup_done(int endpt_status, VSTREAM *smtp_client_stream, MAI_HOSTADDR_STR *smtp_client_addr, MAI_SERVPORT_STR *smtp_client_port, MAI_HOSTADDR_STR *smtp_server_addr, MAI_SERVPORT_STR *smtp_server_port) { const char *myname = "psc_endpt_lookup_done"; PSC_STATE *state; const char *stamp_str; int saved_flags; /* * Best effort - if this non-blocking write(2) fails, so be it. */ if (endpt_status < 0) { (void) write(vstream_fileno(smtp_client_stream), "421 4.3.2 No system resources\r\n", sizeof("421 4.3.2 No system resources\r\n") - 1); event_server_disconnect(smtp_client_stream); return; } if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d connect from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, smtp_client_addr->buf, smtp_client_port->buf); msg_info("CONNECT from [%s]:%s to [%s]:%s", smtp_client_addr->buf, smtp_client_port->buf, smtp_server_addr->buf, smtp_server_port->buf); /* * Bundle up all the loose session pieces. This zeroes all flags and time * stamps. */ state = psc_new_session_state(smtp_client_stream, smtp_client_addr->buf, smtp_client_port->buf, smtp_server_addr->buf, smtp_server_port->buf); /* * Reply with 421 when the client has too many open connections. */ if (var_psc_cconn_limit > 0 && state->client_concurrency > var_psc_cconn_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: too many connections", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.7.0 Error: too many connections\r\n"); return; } /* * Reply with 421 when we can't forward more connections. */ if (var_psc_post_queue_limit > 0 && psc_post_queue_length >= var_psc_post_queue_limit) { msg_info("NOQUEUE: reject: CONNECT from [%s]:%s: all server ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All server ports are busy\r\n"); return; } /* * The permanent white/blacklist has highest precedence. */ if (psc_acl != 0) { switch (psc_acl_eval(state, psc_acl, VAR_PSC_ACL)) { /* * Permanently blacklisted. */ case PSC_ACL_ACT_BLACKLIST: msg_info("BLACKLISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); switch (psc_blist_action) { case PSC_ACT_DROP: PSC_DROP_SESSION_STATE(state, "521 5.3.2 Service currently unavailable\r\n"); return; case PSC_ACT_ENFORCE: PSC_ENFORCE_SESSION_STATE(state, "550 5.3.2 Service currently unavailable\r\n"); break; case PSC_ACT_IGNORE: PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_BLIST_FAIL); /* * Not: PSC_PASS_SESSION_STATE. Repeat this test the next * time. */ break; default: msg_panic("%s: unknown blacklist action value %d", myname, psc_blist_action); } break; /* * Permanently whitelisted. */ case PSC_ACL_ACT_WHITELIST: msg_info("WHITELISTED [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; /* * Other: dunno (don't know) or error. */ default: break; } } /* * The temporary whitelist (i.e. the postscreen cache) has the lowest * precedence. This cache contains information about the results of prior * tests. Whitelist the client when all enabled test results are still * valid. */ if ((state->flags & PSC_STATE_MASK_ANY_FAIL) == 0 && psc_cache_map != 0 && (stamp_str = psc_cache_lookup(psc_cache_map, state->smtp_client_addr)) != 0) { saved_flags = state->flags; psc_parse_tests(state, stamp_str, event_time()); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: cached + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); if ((state->flags & PSC_STATE_MASK_ANY_TODO_FAIL) == 0) { msg_info("PASS OLD [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); psc_conclude(state); return; } } else { saved_flags = state->flags; psc_new_tests(state); state->flags |= saved_flags; if (msg_verbose) msg_info("%s: new + recent flags: %s", myname, psc_print_state_flags(state->flags, myname)); } /* * Don't whitelist clients that connect to backup MX addresses. Fail * "closed" on error. */ if (addr_match_list_match(psc_wlist_if, smtp_server_addr->buf) == 0) { state->flags |= (PSC_STATE_FLAG_WLIST_FAIL | PSC_STATE_FLAG_NOFORWARD); msg_info("WHITELIST VETO [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); } /* * Reply with 421 when we can't analyze more connections. That also means * no deep protocol tests when the noforward flag is raised. */ if (var_psc_pre_queue_limit > 0 && psc_check_queue_length - psc_post_queue_length >= var_psc_pre_queue_limit) { msg_info("reject: connect from [%s]:%s: all screening ports busy", state->smtp_client_addr, state->smtp_client_port); PSC_DROP_SESSION_STATE(state, "421 4.3.2 All screening ports are busy\r\n"); return; } /* * If the client has no up-to-date results for some tests, do those tests * first. Otherwise, skip the tests and hand off the connection. */ if (state->flags & PSC_STATE_MASK_EARLY_TODO) psc_early_tests(state); else if (state->flags & (PSC_STATE_MASK_SMTPD_TODO | PSC_STATE_FLAG_NOFORWARD)) psc_smtpd_tests(state); else psc_conclude(state); }