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