1/*++
2/* NAME
3/*	postscreen_state 3
4/* SUMMARY
5/*	postscreen session state and queue length management
6/* SYNOPSIS
7/*	#include <postscreen.h>
8/*
9/*	PSC_STATE *psc_new_session_state(stream, client_addr, client_port,
10/*						server_addr, server_port)
11/*	VSTREAM *stream;
12/*	const char *client_addr;
13/*	const char *client_port;
14/*	const char *server_addr;
15/*	const char *server_port;
16/*
17/*	void	psc_free_session_state(state)
18/*	PSC_STATE *state;
19/*
20/*	char	*psc_print_state_flags(flags, context)
21/*	int	flags;
22/*	const char *context;
23/*
24/*	void	PSC_ADD_SERVER_STATE(state, server_fd)
25/*	PSC_STATE *state;
26/*	int	server_fd;
27/*
28/*	void	PSC_DEL_CLIENT_STATE(state)
29/*	PSC_STATE *state;
30/*
31/*	void	PSC_DROP_SESSION_STATE(state, final_reply)
32/*	PSC_STATE *state;
33/*	const char *final_reply;
34/*
35/*	void	PSC_ENFORCE_SESSION_STATE(state, rcpt_reply)
36/*	PSC_STATE *state;
37/*	const char *rcpt_reply;
38/*
39/*	void	PSC_PASS_SESSION_STATE(state, testname, pass_flag)
40/*	PSC_STATE *state;
41/*	const char *testname;
42/*	int	pass_flag;
43/*
44/*	void	PSC_FAIL_SESSION_STATE(state, fail_flag)
45/*	PSC_STATE *state;
46/*	int	fail_flag;
47/*
48/*	void	PSC_UNFAIL_SESSION_STATE(state, fail_flag)
49/*	PSC_STATE *state;
50/*	int	fail_flag;
51/* DESCRIPTION
52/*	This module maintains per-client session state, and two
53/*	global file descriptor counters:
54/* .IP psc_check_queue_length
55/*	The total number of remote SMTP client sockets.
56/* .IP psc_post_queue_length
57/*	The total number of server file descriptors that are currently
58/*	in use for client file descriptor passing. This number
59/*	equals the number of client file descriptors in transit.
60/* .PP
61/*	psc_new_session_state() creates a new session state object
62/*	for the specified client stream, and increments the
63/*	psc_check_queue_length counter.  The flags and per-test time
64/*	stamps are initialized with PSC_INIT_TESTS().  The addr and
65/*	port arguments are null-terminated strings with the remote
66/*	SMTP client endpoint. The _reply members are set to
67/*	polite "try again" SMTP replies. The protocol member is set
68/*	to "SMTP".
69/*
70/*	The psc_stress variable is set to non-zero when
71/*	psc_check_queue_length passes over a high-water mark.
72/*
73/*	psc_free_session_state() destroys the specified session state
74/*	object, closes the applicable I/O channels, and decrements
75/*	the applicable file descriptor counters: psc_check_queue_length
76/*	and psc_post_queue_length.
77/*
78/*	The psc_stress variable is reset to zero when psc_check_queue_length
79/*	passes under a low-water mark.
80/*
81/*	psc_print_state_flags() converts per-session flags into
82/*	human-readable form. The context is for error reporting.
83/*	The result is overwritten upon each call.
84/*
85/*	PSC_ADD_SERVER_STATE() updates the specified session state
86/*	object with the specified server file descriptor, and
87/*	increments the global psc_post_queue_length file descriptor
88/*	counter.
89/*
90/*	PSC_DEL_CLIENT_STATE() updates the specified session state
91/*	object, closes the client stream, and decrements the global
92/*	psc_check_queue_length file descriptor counter.
93/*
94/*	PSC_DROP_SESSION_STATE() updates the specified session state
95/*	object and closes the client stream after sending the
96/*	specified SMTP reply.
97/*
98/*	PSC_ENFORCE_SESSION_STATE() updates the specified session
99/*	state object. It arranges that the built-in SMTP engine
100/*	logs sender/recipient information and rejects all RCPT TO
101/*	commands with the specified SMTP reply.
102/*
103/*	PSC_PASS_SESSION_STATE() sets the specified "pass" flag.
104/*	The testname is used for debug logging.
105/*
106/*	PSC_FAIL_SESSION_STATE() sets the specified "fail" flag.
107/*
108/*	PSC_UNFAIL_SESSION_STATE() unsets the specified "fail" flag.
109/* LICENSE
110/* .ad
111/* .fi
112/*	The Secure Mailer license must be distributed with this software.
113/* AUTHOR(S)
114/*	Wietse Venema
115/*	IBM T.J. Watson Research
116/*	P.O. Box 704
117/*	Yorktown Heights, NY 10598, USA
118/*--*/
119
120/* System library. */
121
122#include <sys_defs.h>
123
124/* Utility library. */
125
126#include <msg.h>
127#include <mymalloc.h>
128#include <name_mask.h>
129#include <htable.h>
130
131/* Global library. */
132
133#include <mail_proto.h>
134
135/* Master server protocols. */
136
137#include <mail_server.h>
138
139/* Application-specific. */
140
141#include <postscreen.h>
142
143/* psc_new_session_state - fill in connection state for event processing */
144
145PSC_STATE *psc_new_session_state(VSTREAM *stream,
146				         const char *client_addr,
147				         const char *client_port,
148				         const char *server_addr,
149				         const char *server_port)
150{
151    PSC_STATE *state;
152    HTABLE_INFO *ht;
153
154    state = (PSC_STATE *) mymalloc(sizeof(*state));
155    PSC_INIT_TESTS(state);
156    if ((state->smtp_client_stream = stream) != 0)
157	psc_check_queue_length++;
158    state->smtp_server_fd = (-1);
159    state->smtp_client_addr = mystrdup(client_addr);
160    state->smtp_client_port = mystrdup(client_port);
161    state->smtp_server_addr = mystrdup(server_addr);
162    state->smtp_server_port = mystrdup(server_port);
163    state->send_buf = vstring_alloc(100);
164    state->test_name = "TEST NAME HERE";
165    state->dnsbl_reply = 0;
166    state->final_reply = "421 4.3.2 Service currently unavailable\r\n";
167    state->rcpt_reply = "450 4.3.2 Service currently unavailable\r\n";
168    state->command_count = 0;
169    state->protocol = MAIL_PROTO_SMTP;
170    state->helo_name = 0;
171    state->sender = 0;
172    state->cmd_buffer = 0;
173    state->read_state = 0;
174    state->ehlo_discard_mask = 0;		/* XXX Should be ~0 */
175    state->expand_buf = 0;
176    state->where = PSC_SMTPD_CMD_CONNECT;
177
178    /*
179     * Update the stress level.
180     */
181    if (psc_stress == 0
182	&& psc_check_queue_length >= psc_hiwat_check_queue_length) {
183	psc_stress = 1;
184	msg_info("entering STRESS mode with %d connections",
185		 psc_check_queue_length);
186    }
187
188    /*
189     * Update the per-client session count.
190     */
191    if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0)
192	ht = htable_enter(psc_client_concurrency, client_addr, (char *) 0);
193    ht->value += 1;
194    state->client_concurrency = CAST_CHAR_PTR_TO_INT(ht->value);
195
196    return (state);
197}
198
199/* psc_free_session_state - destroy connection state including connections */
200
201void    psc_free_session_state(PSC_STATE *state)
202{
203    const char *myname = "psc_free_session_state";
204    HTABLE_INFO *ht;
205
206    /*
207     * Update the per-client session count.
208     */
209    if ((ht = htable_locate(psc_client_concurrency,
210			    state->smtp_client_addr)) == 0)
211	msg_panic("%s: unknown client address: %s",
212		  myname, state->smtp_client_addr);
213    if (--(ht->value) == 0)
214	htable_delete(psc_client_concurrency, state->smtp_client_addr,
215		      (void (*) (char *)) 0);
216
217    if (state->smtp_client_stream != 0) {
218	event_server_disconnect(state->smtp_client_stream);
219	psc_check_queue_length--;
220    }
221    if (state->smtp_server_fd >= 0) {
222	close(state->smtp_server_fd);
223	psc_post_queue_length--;
224    }
225    if (state->send_buf != 0)
226	state->send_buf = vstring_free(state->send_buf);
227    myfree(state->smtp_client_addr);
228    myfree(state->smtp_client_port);
229    myfree(state->smtp_server_addr);
230    myfree(state->smtp_server_port);
231    if (state->dnsbl_reply)
232	vstring_free(state->dnsbl_reply);
233    if (state->helo_name)
234	myfree(state->helo_name);
235    if (state->sender)
236	myfree(state->sender);
237    if (state->cmd_buffer)
238	vstring_free(state->cmd_buffer);
239    if (state->expand_buf)
240	vstring_free(state->expand_buf);
241    myfree((char *) state);
242
243    if (psc_check_queue_length < 0 || psc_post_queue_length < 0)
244	msg_panic("bad queue length: check_queue=%d, post_queue=%d",
245		  psc_check_queue_length, psc_post_queue_length);
246
247    /*
248     * Update the stress level.
249     */
250    if (psc_stress != 0
251	&& psc_check_queue_length <= psc_lowat_check_queue_length) {
252	psc_stress = 0;
253	msg_info("leaving STRESS mode with %d connections",
254		 psc_check_queue_length);
255    }
256}
257
258/* psc_print_state_flags - format state flags */
259
260const char *psc_print_state_flags(int flags, const char *context)
261{
262    static const NAME_MASK flags_mask[] = {
263	"NOFORWARD", PSC_STATE_FLAG_NOFORWARD,
264	"USING_TLS", PSC_STATE_FLAG_USING_TLS,
265	"NEW", PSC_STATE_FLAG_NEW,
266	"BLIST_FAIL", PSC_STATE_FLAG_BLIST_FAIL,
267	"HANGUP", PSC_STATE_FLAG_HANGUP,
268	/* unused */
269	"WLIST_FAIL", PSC_STATE_FLAG_WLIST_FAIL,
270
271	"PREGR_FAIL", PSC_STATE_FLAG_PREGR_FAIL,
272	"PREGR_PASS", PSC_STATE_FLAG_PREGR_PASS,
273	"PREGR_TODO", PSC_STATE_FLAG_PREGR_TODO,
274	"PREGR_DONE", PSC_STATE_FLAG_PREGR_DONE,
275
276	"DNSBL_FAIL", PSC_STATE_FLAG_DNSBL_FAIL,
277	"DNSBL_PASS", PSC_STATE_FLAG_DNSBL_PASS,
278	"DNSBL_TODO", PSC_STATE_FLAG_DNSBL_TODO,
279	"DNSBL_DONE", PSC_STATE_FLAG_DNSBL_DONE,
280
281	"PIPEL_FAIL", PSC_STATE_FLAG_PIPEL_FAIL,
282	"PIPEL_PASS", PSC_STATE_FLAG_PIPEL_PASS,
283	"PIPEL_TODO", PSC_STATE_FLAG_PIPEL_TODO,
284	"PIPEL_SKIP", PSC_STATE_FLAG_PIPEL_SKIP,
285
286	"NSMTP_FAIL", PSC_STATE_FLAG_NSMTP_FAIL,
287	"NSMTP_PASS", PSC_STATE_FLAG_NSMTP_PASS,
288	"NSMTP_TODO", PSC_STATE_FLAG_NSMTP_TODO,
289	"NSMTP_SKIP", PSC_STATE_FLAG_NSMTP_SKIP,
290
291	"BARLF_FAIL", PSC_STATE_FLAG_BARLF_FAIL,
292	"BARLF_PASS", PSC_STATE_FLAG_BARLF_PASS,
293	"BARLF_TODO", PSC_STATE_FLAG_BARLF_TODO,
294	"BARLF_SKIP", PSC_STATE_FLAG_BARLF_SKIP,
295	0,
296    };
297
298    return (str_name_mask_opt((VSTRING *) 0, context, flags_mask, flags,
299			      NAME_MASK_PIPE | NAME_MASK_NUMBER));
300}
301