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