7 /* SMTP_SESSION structure management
11 /* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr,
12 /* port, start, flags)
21 /* void smtp_session_free(session)
22 /* SMTP_SESSION *session;
24 /* int smtp_session_passivate(session, dest_prop, endp_prop)
25 /* SMTP_SESSION *session;
26 /* VSTRING *dest_prop;
27 /* VSTRING *endp_prop;
29 /* SMTP_SESSION *smtp_session_activate(fd, dest_prop, endp_prop)
31 /* VSTRING *dest_prop;
32 /* VSTRING *endp_prop;
34 /* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
35 /* and initializes it with the given stream and destination, host name
36 /* and address information. The host name and address strings are
37 /* copied. The port is in network byte order.
38 /* When TLS is enabled, smtp_session_alloc() looks up the
39 /* per-site TLS policies for TLS enforcement and certificate
40 /* verification. The resulting policy is stored into the
41 /* SMTP_SESSION object.
43 /* smtp_session_free() destroys an SMTP_SESSION structure and its
44 /* members, making memory available for reuse. It will handle the
45 /* case of a null stream and will assume it was given a different
48 /* smtp_session_passivate() flattens an SMTP session so that
49 /* it can be cached. The SMTP_SESSION structure is destroyed.
51 /* smtp_session_activate() inflates a flattened SMTP session
52 /* so that it can be used. The input is modified.
56 /* A full-duplex stream.
58 /* The unmodified next-hop or fall-back destination including
59 /* the optional [] and including the optional port or service.
61 /* The name of the host that we are connected to.
63 /* The address of the host that we are connected to.
65 /* The remote port, network byte order.
67 /* The time when this connection was opened.
69 /* Zero or more of the following:
71 /* .IP SMTP_MISC_FLAG_CONN_LOAD
72 /* Enable re-use of cached SMTP or LMTP connections.
73 /* .IP SMTP_MISC_FLAG_CONN_STORE
74 /* Enable saving of cached SMTP or LMTP connections.
76 /* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
78 /* Destination specific session properties: the server is the
79 /* best MX host for the current logical destination.
81 /* Endpoint specific session properties: all the features
82 /* advertised by the remote server.
86 /* The Secure Mailer license must be distributed with this software.
89 /* IBM T.J. Watson Research
91 /* Yorktown Heights, NY 10598, USA
93 /* TLS support originally by:
96 /* Allgemeine Elektrotechnik
97 /* Universitaetsplatz 3-4
98 /* D-03044 Cottbus, Germany
101 /* System library. */
103 #include <sys_defs.h>
106 #include <netinet/in.h>
108 #ifdef STRCASECMP_IN_STRINGS_H
112 /* Utility library. */
115 #include <mymalloc.h>
118 #include <stringops.h>
119 #include <valid_hostname.h>
120 #include <name_code.h>
122 /* Global library. */
124 #include <mime_state.h>
125 #include <debug_peer.h>
126 #include <mail_params.h>
129 /* Application-specific. */
132 #include "smtp_sasl.h"
136 static MAPS
*tls_policy
; /* lookup table(s) */
137 static MAPS
*tls_per_site
; /* lookup table(s) */
139 /* smtp_tls_list_init - initialize per-site policy lists */
141 void smtp_tls_list_init(void)
143 if (*var_smtp_tls_policy
) {
144 tls_policy
= maps_create(VAR_SMTP_TLS_POLICY
, var_smtp_tls_policy
,
145 DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX
);
146 if (*var_smtp_tls_per_site
)
147 msg_warn("%s ignored when %s is not empty.",
148 VAR_SMTP_TLS_PER_SITE
, VAR_SMTP_TLS_POLICY
);
151 if (*var_smtp_tls_per_site
) {
152 tls_per_site
= maps_create(VAR_SMTP_TLS_PER_SITE
, var_smtp_tls_per_site
,
153 DICT_FLAG_LOCK
| DICT_FLAG_FOLD_FIX
);
157 /* policy_name - printable tls policy level */
159 static const char *policy_name(int tls_level
)
161 const char *name
= str_tls_level(tls_level
);
168 /* tls_site_lookup - look up per-site TLS security level */
170 static void tls_site_lookup(int *site_level
, const char *site_name
,
171 const char *site_class
)
176 * Look up a non-default policy. In case of multiple lookup results, the
177 * precedence order is a permutation of the TLS enforcement level order:
178 * VERIFY, ENCRYPT, NONE, MAY, NOTFOUND. I.e. we override MAY with a more
179 * specific policy including NONE, otherwise we choose the stronger
182 if ((lookup
= maps_find(tls_per_site
, site_name
, 0)) != 0) {
183 if (!strcasecmp(lookup
, "NONE")) {
184 /* NONE overrides MAY or NOTFOUND. */
185 if (*site_level
<= TLS_LEV_MAY
)
186 *site_level
= TLS_LEV_NONE
;
187 } else if (!strcasecmp(lookup
, "MAY")) {
188 /* MAY overrides NOTFOUND but not NONE. */
189 if (*site_level
< TLS_LEV_NONE
)
190 *site_level
= TLS_LEV_MAY
;
191 } else if (!strcasecmp(lookup
, "MUST_NOPEERMATCH")) {
192 if (*site_level
< TLS_LEV_ENCRYPT
)
193 *site_level
= TLS_LEV_ENCRYPT
;
194 } else if (!strcasecmp(lookup
, "MUST")) {
195 if (*site_level
< TLS_LEV_VERIFY
)
196 *site_level
= TLS_LEV_VERIFY
;
198 msg_warn("Table %s: ignoring unknown TLS policy '%s' for %s %s",
199 var_smtp_tls_per_site
, lookup
, site_class
, site_name
);
204 /* tls_policy_lookup_one - look up destination TLS policy */
206 static int tls_policy_lookup_one(SMTP_SESSION
*session
, int *site_level
,
207 const char *site_name
,
208 const char *site_class
)
217 static VSTRING
*cbuf
;
220 #define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0)
222 if ((lookup
= maps_find(tls_policy
, site_name
, 0)) == 0)
226 cbuf
= vstring_alloc(10);
229 vstring_str(vstring_sprintf(cbuf, "TLS policy table, %s \"%s\"", \
230 site_class, site_name))
232 saved_policy
= policy
= mystrdup(lookup
);
234 if ((tok
= mystrtok(&policy
, "\t\n\r ,")) == 0) {
235 msg_warn("%s: invalid empty policy", WHERE
);
236 *site_level
= TLS_LEV_INVALID
;
237 FREE_RETURN(1); /* No further lookups */
239 *site_level
= tls_level_lookup(tok
);
240 if (*site_level
== TLS_LEV_INVALID
) {
241 /* tls_level_lookup() logs no warning. */
242 msg_warn("%s: invalid security level \"%s\"", WHERE
, tok
);
243 FREE_RETURN(1); /* No further lookups */
247 * Warn about ignored attributes when TLS is disabled.
249 if (*site_level
< TLS_LEV_MAY
) {
250 while ((tok
= mystrtok(&policy
, "\t\n\r ,")) != 0)
251 msg_warn("%s: ignoring attribute \"%s\" with TLS disabled",
257 * Errors in attributes may have security consequences, don't ignore
258 * errors that can degrade security.
260 while ((tok
= mystrtok(&policy
, "\t\n\r ,")) != 0) {
261 if ((err
= split_nameval(tok
, &name
, &val
)) != 0) {
262 *site_level
= TLS_LEV_INVALID
;
263 msg_warn("%s: malformed attribute/value pair \"%s\": %s",
267 /* Only one instance per policy. */
268 if (!strcasecmp(name
, "ciphers")) {
270 msg_warn("%s: attribute \"%s\" has empty value", WHERE
, name
);
271 *site_level
= TLS_LEV_INVALID
;
274 if (session
->tls_grade
) {
275 msg_warn("%s: attribute \"%s\" is specified multiple times",
277 *site_level
= TLS_LEV_INVALID
;
280 session
->tls_grade
= mystrdup(val
);
283 /* Only one instance per policy. */
284 if (!strcasecmp(name
, "protocols")) {
285 if (session
->tls_protocols
) {
286 msg_warn("%s: attribute \"%s\" is specified multiple times",
288 *site_level
= TLS_LEV_INVALID
;
291 session
->tls_protocols
= mystrdup(val
);
294 /* Multiple instance(s) per policy. */
295 if (!strcasecmp(name
, "match")) {
296 char *delim
= *site_level
== TLS_LEV_FPRINT
? "|" : ":";
298 if (*site_level
<= TLS_LEV_ENCRYPT
) {
299 msg_warn("%s: attribute \"%s\" invalid at security level \"%s\"",
300 WHERE
, name
, policy_name(*site_level
));
301 *site_level
= TLS_LEV_INVALID
;
305 msg_warn("%s: attribute \"%s\" has empty value", WHERE
, name
);
306 *site_level
= TLS_LEV_INVALID
;
309 if (session
->tls_matchargv
== 0)
310 session
->tls_matchargv
= argv_split(val
, delim
);
312 argv_split_append(session
->tls_matchargv
, val
, delim
);
315 /* Only one instance per policy. */
316 if (!strcasecmp(name
, "exclude")) {
317 if (session
->tls_exclusions
) {
318 msg_warn("%s: attribute \"%s\" is specified multiple times",
320 *site_level
= TLS_LEV_INVALID
;
323 session
->tls_exclusions
= vstring_strcpy(vstring_alloc(10), val
);
326 msg_warn("%s: invalid attribute name: \"%s\"", WHERE
, name
);
327 *site_level
= TLS_LEV_INVALID
;
334 /* tls_policy_lookup - look up destination TLS policy */
336 static void tls_policy_lookup(SMTP_SESSION
*session
, int *site_level
,
337 const char *site_name
,
338 const char *site_class
)
342 * Only one lookup with [nexthop]:port, [nexthop] or nexthop:port These
343 * are never the domain part of localpart@domain, rather they are
344 * explicit nexthops from transport:nexthop, and match only the
345 * corresponding policy. Parent domain matching (below) applies only to
346 * sub-domains of the recipient domain.
348 if (!valid_hostname(site_name
, DONT_GRIPE
)) {
349 tls_policy_lookup_one(session
, site_level
, site_name
, site_class
);
354 * XXX For clarity consider using ``do { .. } while'', instead of using
355 * ``while { .. }'' with loop control at the bottom.
358 /* Try the given domain */
359 if (tls_policy_lookup_one(session
, site_level
, site_name
, site_class
))
361 /* Re-try with parent domain */
362 if ((site_name
= strchr(site_name
+ 1, '.')) == 0)
367 /* set_cipher_grade - Set cipher grade and exclusions */
369 static void set_cipher_grade(SMTP_SESSION
*session
)
371 const char *mand_exclude
= "";
372 const char *also_exclude
= "";
375 * Use main.cf cipher level if no per-destination value specified. With
376 * mandatory encryption at least encrypt, and with mandatory verification
377 * at least authenticate!
379 switch (session
->tls_level
) {
380 case TLS_LEV_INVALID
:
385 if (session
->tls_grade
== 0)
386 session
->tls_grade
= mystrdup(var_smtp_tls_ciph
);
389 case TLS_LEV_ENCRYPT
:
390 if (session
->tls_grade
== 0)
391 session
->tls_grade
= mystrdup(var_smtp_tls_mand_ciph
);
392 mand_exclude
= var_smtp_tls_mand_excl
;
393 also_exclude
= "eNULL";
399 if (session
->tls_grade
== 0)
400 session
->tls_grade
= mystrdup(var_smtp_tls_mand_ciph
);
401 mand_exclude
= var_smtp_tls_mand_excl
;
402 also_exclude
= "aNULL";
406 #define ADD_EXCLUDE(vstr, str) \
409 vstring_sprintf_append((vstr), "%s%s", \
410 VSTRING_LEN(vstr) ? " " : "", (str)); \
414 * The "exclude" policy table attribute overrides main.cf exclusion
417 if (session
->tls_exclusions
== 0) {
418 session
->tls_exclusions
= vstring_alloc(10);
419 ADD_EXCLUDE(session
->tls_exclusions
, var_smtp_tls_excl_ciph
);
420 ADD_EXCLUDE(session
->tls_exclusions
, mand_exclude
);
422 ADD_EXCLUDE(session
->tls_exclusions
, also_exclude
);
425 /* session_tls_init - session TLS parameters */
427 static void session_tls_init(SMTP_SESSION
*session
, const char *dest
,
428 const char *host
, int flags
)
430 const char *myname
= "session_tls_init";
435 * Initialize all TLS related session properties.
437 session
->tls_context
= 0;
438 session
->tls_nexthop
= 0;
439 session
->tls_level
= TLS_LEV_NONE
;
440 session
->tls_retry_plain
= 0;
441 session
->tls_protocols
= 0;
442 session
->tls_grade
= 0;
443 session
->tls_exclusions
= 0;
444 session
->tls_matchargv
= 0;
447 * Compute the global TLS policy. This is the default policy level when
448 * no per-site policy exists. It also is used to override a wild-card
451 if (*var_smtp_tls_level
) {
452 /* Require that var_smtp_tls_level is sanitized upon startup. */
453 global_level
= tls_level_lookup(var_smtp_tls_level
);
454 if (global_level
== TLS_LEV_INVALID
)
455 msg_panic("%s: invalid TLS security level: \"%s\"",
456 myname
, var_smtp_tls_level
);
457 } else if (var_smtp_enforce_tls
) {
458 global_level
= var_smtp_tls_enforce_peername
?
459 TLS_LEV_VERIFY
: TLS_LEV_ENCRYPT
;
461 global_level
= var_smtp_use_tls
?
462 TLS_LEV_MAY
: TLS_LEV_NONE
;
465 msg_info("%s TLS level: %s", "global", policy_name(global_level
));
468 * Compute the per-site TLS enforcement level. For compatibility with the
469 * original TLS patch, this algorithm is gives equal precedence to host
470 * and next-hop policies.
472 site_level
= TLS_LEV_NOTFOUND
;
475 tls_policy_lookup(session
, &site_level
, dest
, "next-hop destination");
476 } else if (tls_per_site
) {
477 tls_site_lookup(&site_level
, dest
, "next-hop destination");
478 if (strcasecmp(dest
, host
) != 0)
479 tls_site_lookup(&site_level
, host
, "server hostname");
481 msg_info("%s TLS level: %s", "site", policy_name(site_level
));
484 * Override a wild-card per-site policy with a more specific global
487 * With the original TLS patch, 1) a per-site ENCRYPT could not override
488 * a global VERIFY, and 2) a combined per-site (NONE+MAY) policy
489 * produced inconsistent results: it changed a global VERIFY into
490 * NONE, while producing MAY with all weaker global policy settings.
492 * With the current implementation, a combined per-site (NONE+MAY)
493 * consistently overrides global policy with NONE, and global policy
494 * can override only a per-site MAY wildcard. That is, specific
495 * policies consistently override wildcard policies, and
496 * (non-wildcard) per-site policies consistently override global
499 if (site_level
== TLS_LEV_MAY
&& global_level
> TLS_LEV_MAY
)
500 site_level
= global_level
;
502 if (site_level
== TLS_LEV_NOTFOUND
)
503 session
->tls_level
= global_level
;
505 session
->tls_level
= site_level
;
508 * Use main.cf protocols setting if not set in per-destination table.
510 if (session
->tls_level
> TLS_LEV_NONE
&& session
->tls_protocols
== 0)
511 session
->tls_protocols
=
512 mystrdup((session
->tls_level
== TLS_LEV_MAY
) ?
513 var_smtp_tls_proto
: var_smtp_tls_mand_proto
);
516 * Compute cipher grade (if set in per-destination table, else
517 * set_cipher() uses main.cf settings) and security level dependent
518 * cipher exclusion list.
520 set_cipher_grade(session
);
523 * Use main.cf cert_match setting if not set in per-destination table.
525 if (session
->tls_matchargv
== 0) {
526 switch (session
->tls_level
) {
527 case TLS_LEV_INVALID
:
530 case TLS_LEV_ENCRYPT
:
533 session
->tls_matchargv
=
534 argv_split(var_smtp_tls_fpt_cmatch
, "\t\n\r, |");
537 session
->tls_matchargv
=
538 argv_split(var_smtp_tls_vfy_cmatch
, "\t\n\r, :");
541 session
->tls_matchargv
=
542 argv_split(var_smtp_tls_sec_cmatch
, "\t\n\r, :");
545 msg_panic("unexpected TLS security level: %d",
549 if (msg_verbose
&& (tls_policy
|| tls_per_site
))
550 msg_info("%s TLS level: %s", "effective",
551 policy_name(session
->tls_level
));
556 /* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
558 SMTP_SESSION
*smtp_session_alloc(VSTREAM
*stream
, const char *dest
,
559 const char *host
, const char *addr
,
560 unsigned port
, time_t start
,
563 SMTP_SESSION
*session
;
565 session
= (SMTP_SESSION
*) mymalloc(sizeof(*session
));
566 session
->stream
= stream
;
567 session
->dest
= mystrdup(dest
);
568 session
->host
= mystrdup(host
);
569 session
->addr
= mystrdup(addr
);
570 session
->namaddr
= concatenate(host
, "[", addr
, "]", (char *) 0);
572 session
->port
= port
;
573 session
->features
= 0;
575 session
->size_limit
= 0;
576 session
->error_mask
= 0;
577 session
->buffer
= vstring_alloc(100);
578 session
->scratch
= vstring_alloc(100);
579 session
->scratch2
= vstring_alloc(100);
580 smtp_chat_init(session
);
581 session
->mime_state
= 0;
584 vstring_sprintf(session
->buffer
, "%s:%d",
585 session
->namaddr
, ntohs(session
->port
));
586 session
->namaddrport
= mystrdup(STR(session
->buffer
));
588 session
->namaddrport
= mystrdup(session
->namaddr
);
590 session
->send_proto_helo
= 0;
592 if (flags
& SMTP_MISC_FLAG_CONN_STORE
)
593 CACHE_THIS_SESSION_UNTIL(start
+ var_smtp_reuse_time
);
595 DONT_CACHE_THIS_SESSION
;
596 session
->reuse_count
= 0;
597 USE_NEWBORN_SESSION
; /* He's not dead Jim! */
600 smtp_sasl_connect(session
);
604 * Need to pass the session as a parameter when the new-style per-nexthop
605 * policies can specify not only security level thresholds, but also how
606 * security levels are defined.
609 session_tls_init(session
, dest
, host
, flags
);
612 debug_peer_check(host
, addr
);
616 /* smtp_session_free - destroy SMTP_SESSION structure and contents */
618 void smtp_session_free(SMTP_SESSION
*session
)
621 if (session
->stream
) {
622 vstream_fflush(session
->stream
);
623 if (session
->tls_context
)
624 tls_client_stop(smtp_tls_ctx
, session
->stream
,
625 var_smtp_starttls_tmout
, 0, session
->tls_context
);
627 if (session
->tls_protocols
)
628 myfree(session
->tls_protocols
);
629 if (session
->tls_grade
)
630 myfree(session
->tls_grade
);
631 if (session
->tls_exclusions
)
632 vstring_free(session
->tls_exclusions
);
633 if (session
->tls_matchargv
)
634 argv_free(session
->tls_matchargv
);
637 vstream_fclose(session
->stream
);
638 myfree(session
->dest
);
639 myfree(session
->host
);
640 myfree(session
->addr
);
641 myfree(session
->namaddr
);
642 myfree(session
->namaddrport
);
644 myfree(session
->helo
);
646 vstring_free(session
->buffer
);
647 vstring_free(session
->scratch
);
648 vstring_free(session
->scratch2
);
650 if (session
->history
)
651 smtp_chat_reset(session
);
652 if (session
->mime_state
)
653 mime_state_free(session
->mime_state
);
656 smtp_sasl_cleanup(session
);
659 debug_peer_restore();
660 myfree((char *) session
);
663 /* smtp_session_passivate - passivate an SMTP_SESSION object */
665 int smtp_session_passivate(SMTP_SESSION
*session
, VSTRING
*dest_prop
,
671 * Encode the local-to-physical binding properties: whether or not this
672 * server is best MX host for the next-hop or fall-back logical
673 * destination (this information is needed for loop handling in
676 * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
677 * serialize the properties with attr_print() instead of using ad-hoc,
678 * non-reusable, code and hard-coded format strings.
680 vstring_sprintf(dest_prop
, "%u",
681 session
->features
& SMTP_FEATURE_DESTINATION_MASK
);
684 * Encode the physical endpoint properties: all the session properties
685 * except for "session from cache", "best MX", or "RSET failure".
687 * XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
688 * serialize the properties with attr_print() instead of using obscure
689 * hard-coded format strings.
691 * XXX Should also record an absolute time when a session must be closed,
692 * how many non-delivering mail transactions there were during this
693 * session, and perhaps other statistics, so that we don't reuse a
696 * XXX Be sure to use unsigned types in the format string. Sign characters
697 * would be rejected by the alldig() test on the reading end.
699 vstring_sprintf(endp_prop
, "%u\n%s\n%s\n%s\n%u\n%u\n%lu",
700 session
->reuse_count
,
701 session
->dest
, session
->host
,
702 session
->addr
, session
->port
,
703 session
->features
& SMTP_FEATURE_ENDPOINT_MASK
,
704 (long) session
->expire_time
);
707 * Append the passivated SASL attributes.
710 if (smtp_sasl_enable
)
711 smtp_sasl_passivate(endp_prop
, session
);
715 * Salvage the underlying file descriptor, and destroy the session
718 fd
= vstream_fileno(session
->stream
);
719 vstream_fdclose(session
->stream
);
721 smtp_session_free(session
);
726 /* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
728 SMTP_SESSION
*smtp_session_activate(int fd
, VSTRING
*dest_prop
,
731 const char *myname
= "smtp_session_activate";
732 SMTP_SESSION
*session
;
740 unsigned features
; /* server features */
741 time_t expire_time
; /* session re-use expiration time */
742 unsigned reuse_count
; /* # times reused */
745 * XXX it would be nice to have a VSTRING to VSTREAM adapter so that we
746 * can de-serialize the properties with attr_scan(), instead of using
747 * ad-hoc, non-reusable code.
749 * XXX As a preliminary solution we use mystrtok(), but that function is not
750 * suitable for zero-length fields.
752 endp_props
= STR(endp_prop
);
753 if ((prop
= mystrtok(&endp_props
, "\n")) == 0 || !alldig(prop
)) {
754 msg_warn("%s: bad cached session reuse count property", myname
);
757 reuse_count
= atoi(prop
);
758 if ((dest
= mystrtok(&endp_props
, "\n")) == 0) {
759 msg_warn("%s: missing cached session destination property", myname
);
762 if ((host
= mystrtok(&endp_props
, "\n")) == 0) {
763 msg_warn("%s: missing cached session hostname property", myname
);
766 if ((addr
= mystrtok(&endp_props
, "\n")) == 0) {
767 msg_warn("%s: missing cached session address property", myname
);
770 if ((prop
= mystrtok(&endp_props
, "\n")) == 0 || !alldig(prop
)) {
771 msg_warn("%s: bad cached session port property", myname
);
776 if ((prop
= mystrtok(&endp_props
, "\n")) == 0 || !alldig(prop
)) {
777 msg_warn("%s: bad cached session features property", myname
);
780 features
= atoi(prop
);
782 if ((prop
= mystrtok(&endp_props
, "\n")) == 0 || !alldig(prop
)) {
783 msg_warn("%s: bad cached session expiration time property", myname
);
786 #ifdef MISSING_STRTOUL
787 expire_time
= strtol(prop
, 0, 10);
789 expire_time
= strtoul(prop
, 0, 10);
792 if (dest_prop
&& VSTRING_LEN(dest_prop
)) {
793 dest_props
= STR(dest_prop
);
794 if ((prop
= mystrtok(&dest_props
, "\n")) == 0 || !alldig(prop
)) {
795 msg_warn("%s: bad cached destination features property", myname
);
798 features
|= atoi(prop
);
802 * Allright, bundle up what we have sofar.
806 session
= smtp_session_alloc(vstream_fdopen(fd
, O_RDWR
), dest
, host
,
807 addr
, port
, (time_t) 0, NO_FLAGS
);
808 session
->features
= (features
| SMTP_FEATURE_FROM_CACHE
);
809 CACHE_THIS_SESSION_UNTIL(expire_time
);
810 session
->reuse_count
= ++reuse_count
;
813 msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
815 myname
, dest
, host
, addr
, ntohs(port
), features
,
816 (long) (expire_time
- time((time_t *) 0)), reuse_count
);
819 * Re-activate the SASL attributes.
822 if (smtp_sasl_enable
&& smtp_sasl_activate(session
, endp_props
) < 0) {
823 vstream_fdclose(session
->stream
);
825 smtp_session_free(session
);