1/*++ 2/* NAME 3/* smtp_session 3 4/* SUMMARY 5/* SMTP_SESSION structure management 6/* SYNOPSIS 7/* #include "smtp.h" 8/* 9/* SMTP_SESSION *smtp_session_alloc(stream, iter, start, flags) 10/* VSTREAM *stream; 11/* SMTP_ITERATOR *iter; 12/* time_t start; 13/* int flags; 14/* 15/* void smtp_session_free(session) 16/* SMTP_SESSION *session; 17/* 18/* int smtp_session_passivate(session, dest_prop, endp_prop) 19/* SMTP_SESSION *session; 20/* VSTRING *dest_prop; 21/* VSTRING *endp_prop; 22/* 23/* SMTP_SESSION *smtp_session_activate(fd, iter, dest_prop, endp_prop) 24/* int fd; 25/* SMTP_ITERATOR *iter; 26/* VSTRING *dest_prop; 27/* VSTRING *endp_prop; 28/* DESCRIPTION 29/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure 30/* and initializes it with the given stream and destination, host name 31/* and address information. The host name and address strings are 32/* copied. The port is in network byte order. 33/* 34/* smtp_session_free() destroys an SMTP_SESSION structure and its 35/* members, making memory available for reuse. It will handle the 36/* case of a null stream and will assume it was given a different 37/* purpose. 38/* 39/* smtp_session_passivate() flattens an SMTP session so that 40/* it can be cached. The SMTP_SESSION structure is destroyed. 41/* 42/* smtp_session_activate() inflates a flattened SMTP session 43/* so that it can be used. The input property arguments are 44/* modified. 45/* 46/* Arguments: 47/* .IP stream 48/* A full-duplex stream. 49/* .IP iter 50/* The literal next-hop or fall-back destination including 51/* the optional [] and including the :port or :service; 52/* the name of the remote host; 53/* the printable address of the remote host; 54/* the remote port in network byte order. 55/* .IP start 56/* The time when this connection was opened. 57/* .IP flags 58/* Zero or more of the following: 59/* .RS 60/* .IP SMTP_MISC_FLAG_CONN_LOAD 61/* Enable re-use of cached SMTP or LMTP connections. 62/* .IP SMTP_MISC_FLAG_CONN_STORE 63/* Enable saving of cached SMTP or LMTP connections. 64/* .RE 65/* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE. 66/* .IP dest_prop 67/* Destination specific session properties: the server is the 68/* best MX host for the current logical destination, the dest, 69/* host, and addr properties. When dest_prop is non-empty, it 70/* overrides the iterator dest, host, and addr properties. It 71/* is the caller's responsibility to save the current nexthop 72/* with SMTP_ITER_SAVE_DEST() and to restore it afterwards 73/* with SMTP_ITER_RESTORE_DEST() before trying alternatives. 74/* .IP endp_prop 75/* Endpoint specific session properties: all the features 76/* advertised by the remote server. 77/* LICENSE 78/* .ad 79/* .fi 80/* The Secure Mailer license must be distributed with this software. 81/* AUTHOR(S) 82/* Wietse Venema 83/* IBM T.J. Watson Research 84/* P.O. Box 704 85/* Yorktown Heights, NY 10598, USA 86/* 87/* Viktor Dukhovni 88/*--*/ 89 90/* System library. */ 91 92#include <sys_defs.h> 93#include <stdlib.h> 94#include <string.h> 95#include <netinet/in.h> 96 97#ifdef STRCASECMP_IN_STRINGS_H 98#include <strings.h> 99#endif 100 101/* Utility library. */ 102 103#include <msg.h> 104#include <mymalloc.h> 105#include <vstring.h> 106#include <vstream.h> 107#include <stringops.h> 108 109/* Global library. */ 110 111#include <mime_state.h> 112#include <debug_peer.h> 113#include <mail_params.h> 114 115/* Application-specific. */ 116 117#include "smtp.h" 118#include "smtp_sasl.h" 119 120/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */ 121 122SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter, 123 time_t start, int flags) 124{ 125 SMTP_SESSION *session; 126 const char *host = STR(iter->host); 127 const char *addr = STR(iter->addr); 128 unsigned port = iter->port; 129 130 session = (SMTP_SESSION *) mymalloc(sizeof(*session)); 131 session->stream = stream; 132 session->iterator = iter; 133 session->namaddr = concatenate(host, "[", addr, "]", (char *) 0); 134 session->helo = 0; 135 session->port = port; 136 session->features = 0; 137 138 session->size_limit = 0; 139 session->error_mask = 0; 140 session->buffer = vstring_alloc(100); 141 session->scratch = vstring_alloc(100); 142 session->scratch2 = vstring_alloc(100); 143 smtp_chat_init(session); 144 session->mime_state = 0; 145 146 if (session->port) { 147 vstring_sprintf(session->buffer, "%s:%d", 148 session->namaddr, ntohs(session->port)); 149 session->namaddrport = mystrdup(STR(session->buffer)); 150 } else 151 session->namaddrport = mystrdup(session->namaddr); 152 153 session->send_proto_helo = 0; 154 155 if (flags & SMTP_MISC_FLAG_CONN_STORE) 156 CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time); 157 else 158 DONT_CACHE_THIS_SESSION; 159 session->reuse_count = 0; 160 USE_NEWBORN_SESSION; /* He's not dead Jim! */ 161 162#ifdef USE_SASL_AUTH 163 smtp_sasl_connect(session); 164#endif 165 166#ifdef USE_TLS 167 session->tls_context = 0; 168 session->tls_retry_plain = 0; 169 session->tls_nexthop = 0; 170 session->tls = 0; /* TEMPORARY */ 171#endif 172 session->state = 0; 173 debug_peer_check(host, addr); 174 return (session); 175} 176 177/* smtp_session_free - destroy SMTP_SESSION structure and contents */ 178 179void smtp_session_free(SMTP_SESSION *session) 180{ 181#ifdef USE_TLS 182 if (session->stream) { 183 vstream_fflush(session->stream); 184 if (session->tls_context) 185 tls_client_stop(smtp_tls_ctx, session->stream, 186 var_smtp_starttls_tmout, 0, session->tls_context); 187 } 188#endif 189 if (session->stream) 190 vstream_fclose(session->stream); 191 myfree(session->namaddr); 192 myfree(session->namaddrport); 193 if (session->helo) 194 myfree(session->helo); 195 196 vstring_free(session->buffer); 197 vstring_free(session->scratch); 198 vstring_free(session->scratch2); 199 200 if (session->history) 201 smtp_chat_reset(session); 202 if (session->mime_state) 203 mime_state_free(session->mime_state); 204 205#ifdef USE_SASL_AUTH 206 smtp_sasl_cleanup(session); 207#endif 208 209 debug_peer_restore(); 210 myfree((char *) session); 211} 212 213/* smtp_session_passivate - passivate an SMTP_SESSION object */ 214 215int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, 216 VSTRING *endp_prop) 217{ 218 SMTP_ITERATOR *iter = session->iterator; 219 int fd; 220 221 /* 222 * Encode the local-to-physical binding properties: whether or not this 223 * server is best MX host for the next-hop or fall-back logical 224 * destination (this information is needed for loop handling in 225 * smtp_proto()). 226 * 227 * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can 228 * serialize the properties with attr_print() instead of using ad-hoc, 229 * non-reusable, code and hard-coded format strings. 230 * 231 * TODO: save SASL username and password information so that we can 232 * correctly save a reused authenticated connection. 233 * 234 */ 235 vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u", 236 STR(iter->dest), STR(iter->host), STR(iter->addr), 237 session->features & SMTP_FEATURE_DESTINATION_MASK); 238 239 /* 240 * Encode the physical endpoint properties: all the session properties 241 * except for "session from cache", "best MX", or "RSET failure". 242 * 243 * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can 244 * serialize the properties with attr_print() instead of using obscure 245 * hard-coded format strings. 246 * 247 * XXX Should also record an absolute time when a session must be closed, 248 * how many non-delivering mail transactions there were during this 249 * session, and perhaps other statistics, so that we don't reuse a 250 * session too much. 251 * 252 * XXX Be sure to use unsigned types in the format string. Sign characters 253 * would be rejected by the alldig() test on the reading end. 254 */ 255 vstring_sprintf(endp_prop, "%u\n%u\n%lu", 256 session->reuse_count, 257 session->features & SMTP_FEATURE_ENDPOINT_MASK, 258 (long) session->expire_time); 259 260 /* 261 * Append the passivated SASL attributes. 262 */ 263#ifdef notdef 264 if (smtp_sasl_enable) 265 smtp_sasl_passivate(endp_prop, session); 266#endif 267 268 /* 269 * Salvage the underlying file descriptor, and destroy the session 270 * object. 271 */ 272 fd = vstream_fileno(session->stream); 273 vstream_fdclose(session->stream); 274 session->stream = 0; 275 smtp_session_free(session); 276 277 return (fd); 278} 279 280/* smtp_session_activate - re-activate a passivated SMTP_SESSION object */ 281 282SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter, 283 VSTRING *dest_prop, 284 VSTRING *endp_prop) 285{ 286 const char *myname = "smtp_session_activate"; 287 SMTP_SESSION *session; 288 char *dest_props; 289 char *endp_props; 290 const char *prop; 291 const char *dest; 292 const char *host; 293 const char *addr; 294 unsigned features; /* server features */ 295 time_t expire_time; /* session re-use expiration time */ 296 unsigned reuse_count; /* # times reused */ 297 298 /* 299 * XXX it would be nice to have a VSTRING to VSTREAM adapter so that we 300 * can de-serialize the properties with attr_scan(), instead of using 301 * ad-hoc, non-reusable code. 302 * 303 * XXX As a preliminary solution we use mystrtok(), but that function is not 304 * suitable for zero-length fields. 305 */ 306 endp_props = STR(endp_prop); 307 if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) { 308 msg_warn("%s: bad cached session reuse count property", myname); 309 return (0); 310 } 311 reuse_count = atoi(prop); 312 if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) { 313 msg_warn("%s: bad cached session features property", myname); 314 return (0); 315 } 316 features = atoi(prop); 317 if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) { 318 msg_warn("%s: bad cached session expiration time property", myname); 319 return (0); 320 } 321#ifdef MISSING_STRTOUL 322 expire_time = strtol(prop, 0, 10); 323#else 324 expire_time = strtoul(prop, 0, 10); 325#endif 326 327 /* 328 * Clobber the iterator's current nexthop, host and address fields with 329 * cached-connection information. This is done when a session is looked 330 * up by request nexthop instead of address and port. It is the caller's 331 * responsibility to save and restore the request nexthop with 332 * SMTP_ITER_SAVE_DEST() and SMTP_ITER_RESTORE_DEST(). 333 * 334 * TODO: Eliminate the duplication between SMTP_ITERATOR and SMTP_SESSION. 335 * 336 * TODO: restore SASL username and password information so that we can 337 * correctly save a reused authenticated connection. 338 */ 339 if (dest_prop && VSTRING_LEN(dest_prop)) { 340 dest_props = STR(dest_prop); 341 if ((dest = mystrtok(&dest_props, "\n")) == 0) { 342 msg_warn("%s: missing cached session destination property", myname); 343 return (0); 344 } 345 if ((host = mystrtok(&dest_props, "\n")) == 0) { 346 msg_warn("%s: missing cached session hostname property", myname); 347 return (0); 348 } 349 if ((addr = mystrtok(&dest_props, "\n")) == 0) { 350 msg_warn("%s: missing cached session address property", myname); 351 return (0); 352 } 353 if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) { 354 msg_warn("%s: bad cached destination features property", myname); 355 return (0); 356 } 357 features |= atoi(prop); 358 SMTP_ITER_CLOBBER(iter, dest, host, addr); 359 } 360 361 /* 362 * Allright, bundle up what we have sofar. 363 */ 364#define NO_FLAGS 0 365 366 session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter, 367 (time_t) 0, NO_FLAGS); 368 session->features = (features | SMTP_FEATURE_FROM_CACHE); 369 CACHE_THIS_SESSION_UNTIL(expire_time); 370 session->reuse_count = ++reuse_count; 371 372 if (msg_verbose) 373 msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, " 374 "ttl=%ld, reuse=%d", 375 myname, STR(iter->dest), STR(iter->host), 376 STR(iter->addr), ntohs(iter->port), features, 377 (long) (expire_time - time((time_t *) 0)), 378 reuse_count); 379 380 /* 381 * Re-activate the SASL attributes. 382 */ 383#ifdef notdef 384 if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) { 385 vstream_fdclose(session->stream); 386 session->stream = 0; 387 smtp_session_free(session); 388 return (0); 389 } 390#endif 391 392 return (session); 393} 394