summaryrefslogtreecommitdiffstats
path: root/src/postscreen/postscreen_state.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/postscreen/postscreen_state.c')
-rw-r--r--src/postscreen/postscreen_state.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/postscreen/postscreen_state.c b/src/postscreen/postscreen_state.c
new file mode 100644
index 0000000..2b5db3c
--- /dev/null
+++ b/src/postscreen/postscreen_state.c
@@ -0,0 +1,317 @@
+/*++
+/* NAME
+/* postscreen_state 3
+/* SUMMARY
+/* postscreen session state and queue length management
+/* SYNOPSIS
+/* #include <postscreen.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <name_mask.h>
+#include <htable.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+
+/* Master server protocols. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+#include <postscreen.h>
+
+/* 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));
+}