1/*++
2/* NAME
3/*	smtp_reuse 3
4/* SUMMARY
5/*	SMTP session cache glue
6/* SYNOPSIS
7/*	#include <smtp.h>
8/*	#include <smtp_reuse.h>
9/*
10/*	void	smtp_save_session(state, name_key_flags, endp_key_flags)
11/*	SMTP_STATE *state;
12/*	int	name_key_flags;
13/*	int	endp_key_flags;
14/*
15/*	SMTP_SESSION *smtp_reuse_nexthop(state, name_key_flags)
16/*	SMTP_STATE *state;
17/*	int	name_key_flags;
18/*
19/*	SMTP_SESSION *smtp_reuse_addr(state, endp_key_flags)
20/*	SMTP_STATE *state;
21/*	int	endp_key_flags;
22/* DESCRIPTION
23/*	This module implements the SMTP client specific interface to
24/*	the generic session cache infrastructure.
25/*
26/*	A cached connection is closed when the TLS policy requires
27/*	that TLS is enabled.
28/*
29/*	smtp_save_session() stores the current session under the
30/*	next-hop logical destination (if available) and under the
31/*	remote server address.  The SMTP_SESSION object is destroyed.
32/*
33/*	smtp_reuse_nexthop() looks up a cached session by its logical
34/*	destination, and verifies that the session is still alive.
35/*	The restored session information includes the "best MX" bit
36/*	and overrides the iterator dest, host and addr fields.
37/*	The result is null in case of failure.
38/*
39/*	smtp_reuse_addr() looks up a cached session by its server
40/*	address, and verifies that the session is still alive.
41/*	The restored session information does not include the "best
42/*	MX" bit, and does not override the iterator dest, host and
43/*	addr fields.
44/*	The result is null in case of failure.
45/*
46/*	Arguments:
47/* .IP state
48/*	SMTP client state, including the current session, the original
49/*	next-hop domain, etc.
50/* .IP name_key_flags
51/*	Explicit declaration of context that should be used to look
52/*	up a cached connection by its logical destination.
53/*	See smtp_key(3) for details.
54/* .IP endp_key_flags
55/*	Explicit declaration of context that should be used to look
56/*	up a cached connection by its server address.
57/*	See smtp_key(3) for details.
58/* LICENSE
59/* .ad
60/* .fi
61/*	The Secure Mailer license must be distributed with this software.
62/* AUTHOR(S)
63/*	Wietse Venema
64/*	IBM T.J. Watson Research
65/*	P.O. Box 704
66/*	Yorktown Heights, NY 10598, USA
67/*--*/
68
69/* System library. */
70
71#include <sys_defs.h>
72#include <sys/socket.h>
73#include <netinet/in.h>
74#include <arpa/inet.h>
75#include <unistd.h>
76#include <string.h>
77
78/* Utility library. */
79
80#include <msg.h>
81#include <mymalloc.h>
82#include <vstream.h>
83#include <vstring.h>
84#include <htable.h>
85#include <stringops.h>
86
87/* Global library. */
88
89#include <scache.h>
90#include <mail_params.h>
91
92/* Application-specific. */
93
94#include <smtp.h>
95#include <smtp_reuse.h>
96
97 /*
98  * Key field delimiter, and place holder field value for
99  * unavailable/inapplicable information.
100  */
101#define SMTP_REUSE_KEY_DELIM_NA	"\n*"
102
103/* smtp_save_session - save session under next-hop name and server address */
104
105void    smtp_save_session(SMTP_STATE *state, int name_key_flags,
106			          int endp_key_flags)
107{
108    SMTP_SESSION *session = state->session;
109    int     fd;
110
111    /*
112     * Encode the next-hop logical destination, if available. Reuse storage
113     * that is also used for cache lookup queries.
114     */
115    if (HAVE_NEXTHOP_STATE(state))
116	smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
117			state->iterator, name_key_flags);
118
119    /*
120     * Encode the physical endpoint name. Reuse storage that is also used for
121     * cache lookup queries.
122     */
123    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
124		    state->iterator, endp_key_flags);
125
126    /*
127     * Passivate the SMTP_SESSION object, destroying the object in the
128     * process. Reuse storage that is also used for cache lookup results.
129     */
130    fd = smtp_session_passivate(session, state->dest_prop, state->endp_prop);
131    state->session = 0;
132
133    /*
134     * Save the session under the next-hop name, if available.
135     *
136     * XXX The logical to physical binding can be kept for as long as the DNS
137     * allows us to (but that could result in the caching of lots of unused
138     * bindings). The session should be idle for no more than 30 seconds or
139     * so.
140     */
141    if (HAVE_NEXTHOP_STATE(state))
142	scache_save_dest(smtp_scache, var_smtp_cache_conn, STR(state->dest_label),
143			 STR(state->dest_prop), STR(state->endp_label));
144
145    /*
146     * Save every good session under its physical endpoint address.
147     */
148    scache_save_endp(smtp_scache, var_smtp_cache_conn, STR(state->endp_label),
149		     STR(state->endp_prop), fd);
150}
151
152/* smtp_reuse_common - common session reuse code */
153
154static SMTP_SESSION *smtp_reuse_common(SMTP_STATE *state, int fd,
155				               const char *label)
156{
157    const char *myname = "smtp_reuse_common";
158    SMTP_ITERATOR *iter = state->iterator;
159    SMTP_SESSION *session;
160
161    /*
162     * Can't happen. Both smtp_reuse_nexthop() and smtp_reuse_addr() decline
163     * the request when the TLS policy is not TLS_LEV_NONE.
164     */
165#ifdef USE_TLS
166    if (state->tls->level > TLS_LEV_NONE)
167	msg_panic("%s: unexpected plain-text cached session to %s",
168		  myname, label);
169#endif
170
171    /*
172     * Re-activate the SMTP_SESSION object.
173     */
174    session = smtp_session_activate(fd, state->iterator, state->dest_prop,
175				    state->endp_prop);
176    if (session == 0) {
177	msg_warn("%s: bad cached session attribute for %s", myname, label);
178	(void) close(fd);
179	return (0);
180    }
181    state->session = session;
182    session->state = state;
183#ifdef USE_TLS
184    session->tls = state->tls;			/* TEMPORARY */
185#endif
186
187    /*
188     * Send an RSET probe to verify that the session is still good.
189     */
190    if (smtp_rset(state) < 0
191	|| (session->features & SMTP_FEATURE_RSET_REJECTED) != 0) {
192	smtp_session_free(session);
193	return (state->session = 0);
194    }
195
196    /*
197     * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
198     */
199    vstream_tweak_sock(session->stream);
200
201    /*
202     * Update the list of used cached addresses.
203     */
204    htable_enter(state->cache_used, STR(iter->addr), (char *) 0);
205
206    return (session);
207}
208
209/* smtp_reuse_nexthop - reuse session cached under nexthop name */
210
211SMTP_SESSION *smtp_reuse_nexthop(SMTP_STATE *state, int name_key_flags)
212{
213    SMTP_SESSION *session;
214    int     fd;
215
216    /*
217     * Don't look up an existing plaintext connection when a new connection
218     * would (try to) use TLS.
219     */
220#ifdef USE_TLS
221    if (state->tls->level > TLS_LEV_NONE)
222	return (0);
223#endif
224
225    /*
226     * Look up the session by its logical name.
227     */
228    smtp_key_prefix(state->dest_label, SMTP_REUSE_KEY_DELIM_NA,
229		    state->iterator, name_key_flags);
230    if ((fd = scache_find_dest(smtp_scache, STR(state->dest_label),
231			       state->dest_prop, state->endp_prop)) < 0)
232	return (0);
233
234    /*
235     * Re-activate the SMTP_SESSION object, and verify that the session is
236     * still good.
237     */
238    session = smtp_reuse_common(state, fd, STR(state->dest_label));
239    return (session);
240}
241
242/* smtp_reuse_addr - reuse session cached under numerical address */
243
244SMTP_SESSION *smtp_reuse_addr(SMTP_STATE *state, int endp_key_flags)
245{
246    SMTP_SESSION *session;
247    int     fd;
248
249    /*
250     * Don't look up an existing plaintext connection when a new connection
251     * would (try to) use TLS.
252     */
253#ifdef USE_TLS
254    if (state->tls->level > TLS_LEV_NONE)
255	return (0);
256#endif
257
258    /*
259     * Look up the session by its IP address. This means that we have no
260     * destination-to-address binding properties.
261     */
262    smtp_key_prefix(state->endp_label, SMTP_REUSE_KEY_DELIM_NA,
263		    state->iterator, endp_key_flags);
264    if ((fd = scache_find_endp(smtp_scache, STR(state->endp_label),
265			       state->endp_prop)) < 0)
266	return (0);
267    VSTRING_RESET(state->dest_prop);
268    VSTRING_TERMINATE(state->dest_prop);
269
270    /*
271     * Re-activate the SMTP_SESSION object, and verify that the session is
272     * still good.
273     */
274    session = smtp_reuse_common(state, fd, STR(state->endp_label));
275
276    return (session);
277}
278