/*++ /* NAME /* postscreen_state 3 /* SUMMARY /* postscreen session state and queue length management /* SYNOPSIS /* #include /* /* PSC_STATE *psc_new_session_state(stream, client_addr, client_port, /* server_addr, server_port) /* VSTREAM *stream; /* const char *client_addr; /* const char *client_port; /* const char *server_addr; /* const char *server_port; /* /* void psc_free_session_state(state) /* PSC_STATE *state; /* /* char *psc_print_state_flags(flags, context) /* int flags; /* const char *context; /* /* void PSC_ADD_SERVER_STATE(state, server_fd) /* PSC_STATE *state; /* int server_fd; /* /* void PSC_DEL_SERVER_STATE(state) /* PSC_STATE *state; /* /* void PSC_DEL_CLIENT_STATE(state) /* PSC_STATE *state; /* /* void PSC_DROP_SESSION_STATE(state, final_reply) /* PSC_STATE *state; /* const char *final_reply; /* /* void PSC_ENFORCE_SESSION_STATE(state, rcpt_reply) /* PSC_STATE *state; /* const char *rcpt_reply; /* /* void PSC_PASS_SESSION_STATE(state, testname, pass_flag) /* PSC_STATE *state; /* const char *testname; /* int pass_flag; /* /* void PSC_FAIL_SESSION_STATE(state, fail_flag) /* PSC_STATE *state; /* int fail_flag; /* /* void PSC_UNFAIL_SESSION_STATE(state, fail_flag) /* PSC_STATE *state; /* int fail_flag; /* DESCRIPTION /* This module maintains per-client session state, and two /* global file descriptor counters: /* .IP psc_check_queue_length /* The total number of remote SMTP client sockets. /* .IP psc_post_queue_length /* The total number of server file descriptors that are currently /* in use for client file descriptor passing. This number /* equals the number of client file descriptors in transit. /* .PP /* psc_new_session_state() creates a new session state object /* for the specified client stream, and increments the /* psc_check_queue_length counter. The flags and per-test time /* stamps are initialized with PSC_INIT_TESTS(), or for concurrent /* sessions, with PSC_INIT_TEST_FLAGS_ONLY(). The addr and /* port arguments are null-terminated strings with the remote /* SMTP client endpoint. The _reply members are set to /* polite "try again" SMTP replies. The protocol member is set /* to "SMTP". /* /* The psc_stress variable is set to non-zero when /* psc_check_queue_length passes over a high-water mark. /* /* psc_free_session_state() destroys the specified session state /* object, closes the applicable I/O channels, and decrements /* the applicable file descriptor counters: psc_check_queue_length /* and psc_post_queue_length. /* /* The psc_stress variable is reset to zero when psc_check_queue_length /* passes under a low-water mark. /* /* psc_print_state_flags() converts per-session flags into /* human-readable form. The context is for error reporting. /* The result is overwritten upon each call. /* /* PSC_ADD_SERVER_STATE() updates the specified session state /* object with the specified server file descriptor, and /* increments the global psc_post_queue_length file descriptor /* counter. /* /* PSC_DEL_SERVER_STATE() closes the specified session state /* object's server file descriptor, and decrements the global /* psc_post_queue_length file descriptor counter. /* /* PSC_DEL_CLIENT_STATE() updates the specified session state /* object, closes the client stream, and decrements the global /* psc_check_queue_length file descriptor counter. /* /* PSC_DROP_SESSION_STATE() updates the specified session state /* object and closes the client stream after sending the /* specified SMTP reply. /* /* PSC_ENFORCE_SESSION_STATE() updates the specified session /* state object. It arranges that the built-in SMTP engine /* logs sender/recipient information and rejects all RCPT TO /* commands with the specified SMTP reply. /* /* PSC_PASS_SESSION_STATE() sets the specified "pass" flag. /* The testname is used for debug logging. /* /* PSC_FAIL_SESSION_STATE() sets the specified "fail" flag. /* /* PSC_UNFAIL_SESSION_STATE() unsets the specified "fail" flag. /* 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 /* Master server protocols. */ #include /* Application-specific. */ #include /* psc_new_session_state - fill in connection state for event processing */ PSC_STATE *psc_new_session_state(VSTREAM *stream, const char *client_addr, const char *client_port, const char *server_addr, const char *server_port) { PSC_STATE *state; state = (PSC_STATE *) mymalloc(sizeof(*state)); if ((state->smtp_client_stream = stream) != 0) psc_check_queue_length++; state->smtp_server_fd = (-1); state->smtp_client_addr = mystrdup(client_addr); state->smtp_client_port = mystrdup(client_port); state->smtp_server_addr = mystrdup(server_addr); state->smtp_server_port = mystrdup(server_port); state->send_buf = vstring_alloc(100); state->test_name = "TEST NAME HERE"; state->dnsbl_reply = 0; state->final_reply = "421 4.3.2 Service currently unavailable\r\n"; state->rcpt_reply = "450 4.3.2 Service currently unavailable\r\n"; state->command_count = 0; state->protocol = MAIL_PROTO_SMTP; state->helo_name = 0; state->sender = 0; state->cmd_buffer = 0; state->read_state = 0; state->ehlo_discard_mask = 0; /* XXX Should be ~0 */ state->expand_buf = 0; state->where = PSC_SMTPD_CMD_CONNECT; /* * Update the stress level. */ if (psc_stress == 0 && psc_check_queue_length >= psc_hiwat_check_queue_length) { psc_stress = 1; msg_info("entering STRESS mode with %d connections", psc_check_queue_length); } /* * Update the per-client session count. */ if ((state->client_info = (PSC_CLIENT_INFO *) htable_find(psc_client_concurrency, client_addr)) == 0) { state->client_info = (PSC_CLIENT_INFO *) mymalloc(sizeof(state->client_info[0])); (void) htable_enter(psc_client_concurrency, client_addr, (void *) state->client_info); PSC_INIT_TESTS(state); state->client_info->concurrency = 1; state->client_info->pass_new_count = 0; } else { PSC_INIT_TEST_FLAGS_ONLY(state); state->client_info->concurrency += 1; } return (state); } /* psc_free_session_state - destroy connection state including connections */ void psc_free_session_state(PSC_STATE *state) { const char *myname = "psc_free_session_state"; HTABLE_INFO *ht; /* * Update the per-client session count. */ if ((ht = htable_locate(psc_client_concurrency, state->smtp_client_addr)) == 0) msg_panic("%s: unknown client address: %s", myname, state->smtp_client_addr); if (--(state->client_info->concurrency) == 0) htable_delete(psc_client_concurrency, state->smtp_client_addr, myfree); if (state->smtp_client_stream != 0) { PSC_DEL_CLIENT_STATE(state); } if (state->smtp_server_fd >= 0) { PSC_DEL_SERVER_STATE(state); } if (state->send_buf != 0) state->send_buf = vstring_free(state->send_buf); myfree(state->smtp_client_addr); myfree(state->smtp_client_port); myfree(state->smtp_server_addr); myfree(state->smtp_server_port); if (state->dnsbl_reply) vstring_free(state->dnsbl_reply); if (state->helo_name) myfree(state->helo_name); if (state->sender) myfree(state->sender); if (state->cmd_buffer) vstring_free(state->cmd_buffer); if (state->expand_buf) vstring_free(state->expand_buf); myfree((void *) state); if (psc_check_queue_length < 0 || psc_post_queue_length < 0) msg_panic("bad queue length: check_queue=%d, post_queue=%d", psc_check_queue_length, psc_post_queue_length); /* * Update the stress level. */ if (psc_stress != 0 && psc_check_queue_length <= psc_lowat_check_queue_length) { psc_stress = 0; msg_info("leaving STRESS mode with %d connections", psc_check_queue_length); } } /* psc_print_state_flags - format state flags */ const char *psc_print_state_flags(int flags, const char *context) { static const NAME_MASK flags_mask[] = { "NOFORWARD", PSC_STATE_FLAG_NOFORWARD, "USING_TLS", PSC_STATE_FLAG_USING_TLS, "NEW", PSC_STATE_FLAG_NEW, "DNLIST_FAIL", PSC_STATE_FLAG_DNLIST_FAIL, "HANGUP", PSC_STATE_FLAG_HANGUP, /* unused */ "ALLIST_FAIL", PSC_STATE_FLAG_ALLIST_FAIL, "PREGR_FAIL", PSC_STATE_FLAG_PREGR_FAIL, "PREGR_PASS", PSC_STATE_FLAG_PREGR_PASS, "PREGR_TODO", PSC_STATE_FLAG_PREGR_TODO, "PREGR_DONE", PSC_STATE_FLAG_PREGR_DONE, "DNSBL_FAIL", PSC_STATE_FLAG_DNSBL_FAIL, "DNSBL_PASS", PSC_STATE_FLAG_DNSBL_PASS, "DNSBL_TODO", PSC_STATE_FLAG_DNSBL_TODO, "DNSBL_DONE", PSC_STATE_FLAG_DNSBL_DONE, "PIPEL_FAIL", PSC_STATE_FLAG_PIPEL_FAIL, "PIPEL_PASS", PSC_STATE_FLAG_PIPEL_PASS, "PIPEL_TODO", PSC_STATE_FLAG_PIPEL_TODO, "PIPEL_SKIP", PSC_STATE_FLAG_PIPEL_SKIP, "NSMTP_FAIL", PSC_STATE_FLAG_NSMTP_FAIL, "NSMTP_PASS", PSC_STATE_FLAG_NSMTP_PASS, "NSMTP_TODO", PSC_STATE_FLAG_NSMTP_TODO, "NSMTP_SKIP", PSC_STATE_FLAG_NSMTP_SKIP, "BARLF_FAIL", PSC_STATE_FLAG_BARLF_FAIL, "BARLF_PASS", PSC_STATE_FLAG_BARLF_PASS, "BARLF_TODO", PSC_STATE_FLAG_BARLF_TODO, "BARLF_SKIP", PSC_STATE_FLAG_BARLF_SKIP, 0, }; return (str_name_mask_opt((VSTRING *) 0, context, flags_mask, flags, NAME_MASK_PIPE | NAME_MASK_NUMBER)); }