7 /* SMTP session cache glue
10 /* #include <smtp_reuse.h>
12 /* void smtp_save_session(state)
15 /* SMTP_SESSION *smtp_reuse_domain(state, lookup_mx, domain, port)
21 /* SMTP_SESSION *smtp_reuse_addr(state, addr, port)
26 /* This module implements the SMTP client specific interface to
27 /* the generic session cache infrastructure.
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.
33 /* smtp_reuse_domain() 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 /* The result is null in case of failure.
38 /* smtp_reuse_addr() looks up a cached session by its server
39 /* address, and verifies that the session is still alive.
40 /* This operation is disabled when the legacy tls_per_site
41 /* or smtp_sasl_password_maps features are enabled.
42 /* The result is null in case of failure.
46 /* SMTP client state, including the current session, the original
47 /* next-hop domain, etc.
49 /* Whether or not the domain is subject to MX lookup.
51 /* Domain name or bare numerical address.
53 /* The remote server address as printable text.
55 /* The remote server port, network byte order.
59 /* The Secure Mailer license must be distributed with this software.
62 /* IBM T.J. Watson Research
64 /* Yorktown Heights, NY 10598, USA
70 #include <sys/socket.h>
71 #include <netinet/in.h>
72 #include <arpa/inet.h>
76 /* Utility library. */
83 #include <stringops.h>
88 #include <mail_params.h>
90 /* Application-specific. */
93 #include <smtp_reuse.h>
96 * We encode the MX lookup/A lookup method into the name under which SMTP
97 * session information is cached. The following macros serve to make the
98 * remainder of the code less obscure.
100 #define NO_MX_LOOKUP 0
102 #define SMTP_SCACHE_LABEL(mx_lookup_flag) \
103 ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u")
105 /* smtp_save_session - save session under next-hop name and server address */
107 void smtp_save_session(SMTP_STATE
*state
)
109 SMTP_SESSION
*session
= state
->session
;
113 * Encode the next-hop logical destination, if available. Reuse storage
114 * that is also used for cache lookup queries.
116 * Note: if the label needs to be made more specific (with e.g., SASL login
117 * information), just append the text with vstring_sprintf_append().
119 if (HAVE_NEXTHOP_STATE(state
))
120 vstring_sprintf(state
->dest_label
,
121 SMTP_SCACHE_LABEL(state
->nexthop_lookup_mx
),
122 state
->service
, state
->nexthop_domain
,
123 ntohs(state
->nexthop_port
));
126 * Encode the physical endpoint name. Reuse storage that is also used for
127 * cache lookup queries.
129 * Note: if the label needs to be made more specific (with e.g., SASL login
130 * information), just append the text with vstring_sprintf_append().
132 vstring_sprintf(state
->endp_label
,
133 SMTP_SCACHE_LABEL(NO_MX_LOOKUP
),
134 state
->service
, session
->addr
, ntohs(session
->port
));
137 * Passivate the SMTP_SESSION object, destroying the object in the
138 * process. Reuse storage that is also used for cache lookup results.
140 fd
= smtp_session_passivate(session
, state
->dest_prop
, state
->endp_prop
);
144 * Save the session under the next-hop name, if available.
146 * XXX The logical to physical binding can be kept for as long as the DNS
147 * allows us to (but that could result in the caching of lots of unused
148 * bindings). The session should be idle for no more than 30 seconds or
151 if (HAVE_NEXTHOP_STATE(state
))
152 scache_save_dest(smtp_scache
, var_smtp_cache_conn
, STR(state
->dest_label
),
153 STR(state
->dest_prop
), STR(state
->endp_label
));
156 * Save every good session under its physical endpoint address.
158 scache_save_endp(smtp_scache
, var_smtp_cache_conn
, STR(state
->endp_label
),
159 STR(state
->endp_prop
), fd
);
162 /* smtp_reuse_common - common session reuse code */
164 static SMTP_SESSION
*smtp_reuse_common(SMTP_STATE
*state
, int fd
,
167 const char *myname
= "smtp_reuse_common";
168 SMTP_SESSION
*session
;
171 * Re-activate the SMTP_SESSION object.
173 session
= smtp_session_activate(fd
, state
->dest_prop
, state
->endp_prop
);
175 msg_warn("%s: bad cached session attribute for %s", myname
, label
);
179 state
->session
= session
;
180 session
->state
= state
;
185 * Cached connections are always plaintext. They must never be reused when
186 * TLS encryption is required.
188 * As long as we support the legacy smtp_tls_per_site feature, we must
189 * search the connection cache before making TLS policy decisions. This
190 * is because the policy can depend on the server name. For example, a
191 * site could have a global policy that requires encryption, with
192 * per-server exceptions that allow plaintext.
194 * With the newer smtp_tls_policy_maps feature, the policy depends on the
195 * next-hop destination only. We can avoid unnecessary connection cache
196 * lookups, because we can compute the TLS policy much earlier.
199 if (session
->tls_level
>= TLS_LEV_ENCRYPT
) {
201 msg_info("%s: skipping plain-text cached session to %s",
203 smtp_quit(state
); /* Close politely */
204 smtp_session_free(session
); /* And avoid leaks */
205 return (state
->session
= 0);
210 * Send an RSET probe to verify that the session is still good.
212 if (smtp_rset(state
) < 0
213 || (session
->features
& SMTP_FEATURE_RSET_REJECTED
) != 0) {
214 smtp_session_free(session
);
215 return (state
->session
= 0);
219 * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
221 vstream_tweak_sock(session
->stream
);
224 * Update the list of used cached addresses.
226 htable_enter(state
->cache_used
, session
->addr
, (char *) 0);
231 /* smtp_reuse_domain - reuse session cached under domain name */
233 SMTP_SESSION
*smtp_reuse_domain(SMTP_STATE
*state
, int lookup_mx
,
234 const char *domain
, unsigned port
)
236 SMTP_SESSION
*session
;
240 * Look up the session by its logical name.
242 * Note: if the label needs to be made more specific (with e.g., SASL login
243 * information), just append the text with vstring_sprintf_append().
245 vstring_sprintf(state
->dest_label
, SMTP_SCACHE_LABEL(lookup_mx
),
246 state
->service
, domain
, ntohs(port
));
247 if ((fd
= scache_find_dest(smtp_scache
, STR(state
->dest_label
),
248 state
->dest_prop
, state
->endp_prop
)) < 0)
252 * Re-activate the SMTP_SESSION object, and verify that the session is
255 session
= smtp_reuse_common(state
, fd
, STR(state
->dest_label
));
259 /* smtp_reuse_addr - reuse session cached under numerical address */
261 SMTP_SESSION
*smtp_reuse_addr(SMTP_STATE
*state
, const char *addr
,
264 SMTP_SESSION
*session
;
268 * XXX Disable connection cache lookup by server IP address when the
269 * tls_per_site policy or smtp_sasl_password_maps features are enabled.
270 * This connection may have been created under a different hostname that
271 * resolves to the same IP address. We don't want to use the wrong SASL
272 * credentials or the wrong TLS policy.
274 if ((var_smtp_tls_per_site
&& *var_smtp_tls_per_site
)
275 || (var_smtp_sasl_passwd
&& *var_smtp_sasl_passwd
))
279 * Look up the session by its IP address. This means that we have no
280 * destination-to-address binding properties.
282 * Note: if the label needs to be made more specific (with e.g., SASL login
283 * information), just append the text with vstring_sprintf_append().
285 vstring_sprintf(state
->endp_label
, SMTP_SCACHE_LABEL(NO_MX_LOOKUP
),
286 state
->service
, addr
, ntohs(port
));
287 if ((fd
= scache_find_endp(smtp_scache
, STR(state
->endp_label
),
288 state
->endp_prop
)) < 0)
290 VSTRING_RESET(state
->dest_prop
);
291 VSTRING_TERMINATE(state
->dest_prop
);
294 * Re-activate the SMTP_SESSION object, and verify that the session is
297 session
= smtp_reuse_common(state
, fd
, STR(state
->endp_label
));
300 * XXX What if hostnames don't match (addr->name versus session->name),
301 * or if the SASL login name for this host does not match the SASL login
302 * name that was used when opening this session? If something depends
303 * critically on such information being identical, then that information
304 * should be included in the logical and physical labels under which a