1*e8314800Stron /*	$NetBSD: postscreen_send.c,v 1.1.1.1 2011/03/02 19:32:26 tron Exp $	*/
2*e8314800Stron 
3*e8314800Stron /*++
4*e8314800Stron /* NAME
5*e8314800Stron /*	postscreen_send 3
6*e8314800Stron /* SUMMARY
7*e8314800Stron /*	postscreen low-level output
8*e8314800Stron /* SYNOPSIS
9*e8314800Stron /*	#include <postscreen.h>
10*e8314800Stron /*
11*e8314800Stron /*	int	psc_send_reply(state, text)
12*e8314800Stron /*	PSC_STATE *state;
13*e8314800Stron /*	const char *text;
14*e8314800Stron /*
15*e8314800Stron /*	int	PSC_SEND_REPLY(state, text)
16*e8314800Stron /*	PSC_STATE *state;
17*e8314800Stron /*	const char *text;
18*e8314800Stron /*
19*e8314800Stron /*	void	psc_send_socket(state)
20*e8314800Stron /*	PSC_STATE *state;
21*e8314800Stron /* DESCRIPTION
22*e8314800Stron /*	psc_send_reply() sends the specified text to the specified
23*e8314800Stron /*	remote SMTP client.  In case of an immediate error, it logs
24*e8314800Stron /*	a warning (except EPIPE) with the client address and port,
25*e8314800Stron /*	and returns a non-zero result (all errors including EPIPE).
26*e8314800Stron /*
27*e8314800Stron /*	psc_send_reply() does a best effort to send the reply, but
28*e8314800Stron /*	it won't block when the output is throttled by a hostile
29*e8314800Stron /*	peer.
30*e8314800Stron /*
31*e8314800Stron /*	PSC_SEND_REPLY() is a legacy wrapper for psc_send_reply().
32*e8314800Stron /*	It will eventually be replaced by its expansion.
33*e8314800Stron /*
34*e8314800Stron /*	psc_send_socket() sends the specified socket to the real
35*e8314800Stron /*	Postfix SMTP server. The socket is delivered in the background.
36*e8314800Stron /*	This function must be called after all other session-related
37*e8314800Stron /*	work is finished including postscreen cache updates.
38*e8314800Stron /*
39*e8314800Stron /*	In case of an immediate error, psc_send_socket() sends a 421
40*e8314800Stron /*	reply to the remote SMTP client and closes the connection.
41*e8314800Stron /* LICENSE
42*e8314800Stron /* .ad
43*e8314800Stron /* .fi
44*e8314800Stron /*	The Secure Mailer license must be distributed with this software.
45*e8314800Stron /* AUTHOR(S)
46*e8314800Stron /*	Wietse Venema
47*e8314800Stron /*	IBM T.J. Watson Research
48*e8314800Stron /*	P.O. Box 704
49*e8314800Stron /*	Yorktown Heights, NY 10598, USA
50*e8314800Stron /*--*/
51*e8314800Stron 
52*e8314800Stron /* System library. */
53*e8314800Stron 
54*e8314800Stron #include <sys_defs.h>
55*e8314800Stron #include <string.h>
56*e8314800Stron #include <errno.h>
57*e8314800Stron 
58*e8314800Stron /* Utility library. */
59*e8314800Stron 
60*e8314800Stron #include <msg.h>
61*e8314800Stron #include <iostuff.h>
62*e8314800Stron #include <connect.h>
63*e8314800Stron 
64*e8314800Stron /* Global library. */
65*e8314800Stron 
66*e8314800Stron #include <mail_params.h>
67*e8314800Stron #include <smtp_reply_footer.h>
68*e8314800Stron 
69*e8314800Stron /* Application-specific. */
70*e8314800Stron 
71*e8314800Stron #include <postscreen.h>
72*e8314800Stron 
73*e8314800Stron  /*
74*e8314800Stron   * This program screens all inbound SMTP connections, so it better not waste
75*e8314800Stron   * time.
76*e8314800Stron   */
77*e8314800Stron #define PSC_SEND_SOCK_CONNECT_TIMEOUT	1
78*e8314800Stron #define PSC_SEND_SOCK_NOTIFY_TIMEOUT	100
79*e8314800Stron 
80*e8314800Stron /* psc_send_reply - send reply to remote SMTP client */
81*e8314800Stron 
82*e8314800Stron int     psc_send_reply(PSC_STATE *state, const char *text)
83*e8314800Stron {
84*e8314800Stron     ssize_t start;
85*e8314800Stron     int     ret;
86*e8314800Stron 
87*e8314800Stron     if (msg_verbose)
88*e8314800Stron 	msg_info("> [%s]:%s: %.*s", state->smtp_client_addr,
89*e8314800Stron 		 state->smtp_client_port, (int) strlen(text) - 2, text);
90*e8314800Stron 
91*e8314800Stron     /*
92*e8314800Stron      * Append the new text to earlier text that could not be sent because the
93*e8314800Stron      * output was throttled.
94*e8314800Stron      */
95*e8314800Stron     start = VSTRING_LEN(state->send_buf);
96*e8314800Stron     vstring_strcat(state->send_buf, text);
97*e8314800Stron 
98*e8314800Stron     /*
99*e8314800Stron      * For soft_bounce support, we also fix the REJECT logging before the
100*e8314800Stron      * dummy SMTP engine calls the psc_send_reply() output routine. We do
101*e8314800Stron      * some double work, but it is for debugging only.
102*e8314800Stron      */
103*e8314800Stron     if (var_soft_bounce) {
104*e8314800Stron 	if (text[0] == '5')
105*e8314800Stron 	    STR(state->send_buf)[start + 0] = '4';
106*e8314800Stron 	if (text[4] == '5')
107*e8314800Stron 	    STR(state->send_buf)[start + 4] = '4';
108*e8314800Stron     }
109*e8314800Stron 
110*e8314800Stron     /*
111*e8314800Stron      * Append the optional reply footer.
112*e8314800Stron      */
113*e8314800Stron     if (*var_psc_rej_footer && (*text == '4' || *text == '5'))
114*e8314800Stron 	smtp_reply_footer(state->send_buf, start, var_psc_rej_footer,
115*e8314800Stron 			  STR(psc_expand_filter), psc_expand_lookup,
116*e8314800Stron 			  (char *) state);
117*e8314800Stron 
118*e8314800Stron     /*
119*e8314800Stron      * Do a best effort sending text, but don't block when the output is
120*e8314800Stron      * throttled by a hostile peer.
121*e8314800Stron      */
122*e8314800Stron     ret = write(vstream_fileno(state->smtp_client_stream),
123*e8314800Stron 		STR(state->send_buf), LEN(state->send_buf));
124*e8314800Stron     if (ret > 0)
125*e8314800Stron 	vstring_truncate(state->send_buf, ret - LEN(state->send_buf));
126*e8314800Stron     if (ret < 0 && errno != EAGAIN && errno != EPIPE && errno != ECONNRESET)
127*e8314800Stron 	msg_warn("write [%s]:%s: %m", state->smtp_client_addr,
128*e8314800Stron 		 state->smtp_client_port);
129*e8314800Stron     return (ret < 0 && errno != EAGAIN);
130*e8314800Stron }
131*e8314800Stron 
132*e8314800Stron /* psc_send_socket_close_event - file descriptor has arrived or timeout */
133*e8314800Stron 
134*e8314800Stron static void psc_send_socket_close_event(int event, char *context)
135*e8314800Stron {
136*e8314800Stron     const char *myname = "psc_send_socket_close_event";
137*e8314800Stron     PSC_STATE *state = (PSC_STATE *) context;
138*e8314800Stron 
139*e8314800Stron     if (msg_verbose > 1)
140*e8314800Stron 	msg_info("%s: sq=%d cq=%d event %d on send socket %d from [%s]:%s",
141*e8314800Stron 		 myname, psc_post_queue_length, psc_check_queue_length,
142*e8314800Stron 		 event, state->smtp_server_fd, state->smtp_client_addr,
143*e8314800Stron 		 state->smtp_client_port);
144*e8314800Stron 
145*e8314800Stron     /*
146*e8314800Stron      * The real SMTP server has closed the local IPC channel, or we have
147*e8314800Stron      * reached the limit of our patience. In the latter case it is still
148*e8314800Stron      * possible that the real SMTP server will receive the socket so we
149*e8314800Stron      * should not interfere.
150*e8314800Stron      */
151*e8314800Stron     PSC_CLEAR_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event,
152*e8314800Stron 			    context);
153*e8314800Stron     if (event == EVENT_TIME)
154*e8314800Stron 	msg_warn("timeout sending connection to service %s",
155*e8314800Stron 		 psc_smtpd_service_name);
156*e8314800Stron     psc_free_session_state(state);
157*e8314800Stron }
158*e8314800Stron 
159*e8314800Stron /* psc_send_socket - send socket to real SMTP server process */
160*e8314800Stron 
161*e8314800Stron void    psc_send_socket(PSC_STATE *state)
162*e8314800Stron {
163*e8314800Stron     const char *myname = "psc_send_socket";
164*e8314800Stron     int     server_fd;
165*e8314800Stron 
166*e8314800Stron     if (msg_verbose > 1)
167*e8314800Stron 	msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s",
168*e8314800Stron 		 myname, psc_post_queue_length, psc_check_queue_length,
169*e8314800Stron 		 vstream_fileno(state->smtp_client_stream),
170*e8314800Stron 		 state->smtp_client_addr, state->smtp_client_port);
171*e8314800Stron 
172*e8314800Stron     /*
173*e8314800Stron      * Connect to the real SMTP service over a local IPC channel, send the
174*e8314800Stron      * file descriptor, and close the file descriptor to save resources.
175*e8314800Stron      * Experience has shown that some systems will discard information when
176*e8314800Stron      * we close a channel immediately after writing. Thus, we waste resources
177*e8314800Stron      * waiting for the remote side to close the local IPC channel first. The
178*e8314800Stron      * good side of waiting is that we learn when the real SMTP server is
179*e8314800Stron      * falling behind.
180*e8314800Stron      *
181*e8314800Stron      * This is where we would forward the connection to an SMTP server that
182*e8314800Stron      * provides an appropriate level of service for this client class. For
183*e8314800Stron      * example, a server that is more forgiving, or one that is more
184*e8314800Stron      * suspicious. Alternatively, we could send attributes along with the
185*e8314800Stron      * socket with client reputation information, making everything even more
186*e8314800Stron      * Postfix-specific.
187*e8314800Stron      */
188*e8314800Stron     if ((server_fd =
189*e8314800Stron 	 PASS_CONNECT(psc_smtpd_service_name, NON_BLOCKING,
190*e8314800Stron 		      PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) {
191*e8314800Stron 	msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name);
192*e8314800Stron 	PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n");
193*e8314800Stron 	psc_free_session_state(state);
194*e8314800Stron 	return;
195*e8314800Stron     }
196*e8314800Stron     PSC_ADD_SERVER_STATE(state, server_fd);
197*e8314800Stron     if (LOCAL_SEND_FD(state->smtp_server_fd,
198*e8314800Stron 		      vstream_fileno(state->smtp_client_stream)) < 0) {
199*e8314800Stron 	msg_warn("cannot pass connection to service %s: %m",
200*e8314800Stron 		 psc_smtpd_service_name);
201*e8314800Stron 	PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n");
202*e8314800Stron 	psc_free_session_state(state);
203*e8314800Stron 	return;
204*e8314800Stron     } else {
205*e8314800Stron 
206*e8314800Stron 	/*
207*e8314800Stron 	 * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug
208*e8314800Stron 	 * where smtp-source sometimes sees the connection being closed after
209*e8314800Stron 	 * it has already received the real SMTP server's 220 greeting!
210*e8314800Stron 	 */
211*e8314800Stron #if 0
212*e8314800Stron 	PSC_DEL_CLIENT_STATE(state);
213*e8314800Stron #endif
214*e8314800Stron 	PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event,
215*e8314800Stron 			       (char *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT);
216*e8314800Stron 	return;
217*e8314800Stron     }
218*e8314800Stron }
219