Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / smtp / smtp_session.c
blob8b6ca53d7082fd2cb9ed89f968e1786596e15d8a
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* smtp_session 3
6 /* SUMMARY
7 /* SMTP_SESSION structure management
8 /* SYNOPSIS
9 /* #include "smtp.h"
11 /* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr,
12 /* port, start, flags)
13 /* VSTREAM *stream;
14 /* char *dest;
15 /* char *host;
16 /* char *addr;
17 /* unsigned port;
18 /* time_t start;
19 /* int 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)
30 /* int fd;
31 /* VSTRING *dest_prop;
32 /* VSTRING *endp_prop;
33 /* DESCRIPTION
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
46 /* purpose.
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.
54 /* Arguments:
55 /* .IP stream
56 /* A full-duplex stream.
57 /* .IP dest
58 /* The unmodified next-hop or fall-back destination including
59 /* the optional [] and including the optional port or service.
60 /* .IP host
61 /* The name of the host that we are connected to.
62 /* .IP addr
63 /* The address of the host that we are connected to.
64 /* .IP port
65 /* The remote port, network byte order.
66 /* .IP start
67 /* The time when this connection was opened.
68 /* .IP flags
69 /* Zero or more of the following:
70 /* .RS
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.
75 /* .RE
76 /* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
77 /* .IP dest_prop
78 /* Destination specific session properties: the server is the
79 /* best MX host for the current logical destination.
80 /* .IP endp_prop
81 /* Endpoint specific session properties: all the features
82 /* advertised by the remote server.
83 /* LICENSE
84 /* .ad
85 /* .fi
86 /* The Secure Mailer license must be distributed with this software.
87 /* AUTHOR(S)
88 /* Wietse Venema
89 /* IBM T.J. Watson Research
90 /* P.O. Box 704
91 /* Yorktown Heights, NY 10598, USA
93 /* TLS support originally by:
94 /* Lutz Jaenicke
95 /* BTU Cottbus
96 /* Allgemeine Elektrotechnik
97 /* Universitaetsplatz 3-4
98 /* D-03044 Cottbus, Germany
99 /*--*/
101 /* System library. */
103 #include <sys_defs.h>
104 #include <stdlib.h>
105 #include <string.h>
106 #include <netinet/in.h>
108 #ifdef STRCASECMP_IN_STRINGS_H
109 #include <strings.h>
110 #endif
112 /* Utility library. */
114 #include <msg.h>
115 #include <mymalloc.h>
116 #include <vstring.h>
117 #include <vstream.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>
127 #include <maps.h>
129 /* Application-specific. */
131 #include "smtp.h"
132 #include "smtp_sasl.h"
134 #ifdef USE_TLS
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);
149 return;
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);
163 if (name == 0)
164 name = "unknown";
165 return name;
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)
173 const char *lookup;
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
180 * enforcement level.
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;
197 } else {
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)
210 const char *lookup;
211 char *policy;
212 char *saved_policy;
213 char *tok;
214 const char *err;
215 char *name;
216 char *val;
217 static VSTRING *cbuf;
219 #undef FREE_RETURN
220 #define FREE_RETURN(x) do { myfree(saved_policy); return (x); } while (0)
222 if ((lookup = maps_find(tls_policy, site_name, 0)) == 0)
223 return (0);
225 if (cbuf == 0)
226 cbuf = vstring_alloc(10);
228 #define WHERE \
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",
252 WHERE, tok);
253 FREE_RETURN(1);
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",
264 WHERE, tok, err);
265 break;
267 /* Only one instance per policy. */
268 if (!strcasecmp(name, "ciphers")) {
269 if (*val == 0) {
270 msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
271 *site_level = TLS_LEV_INVALID;
272 break;
274 if (session->tls_grade) {
275 msg_warn("%s: attribute \"%s\" is specified multiple times",
276 WHERE, name);
277 *site_level = TLS_LEV_INVALID;
278 break;
280 session->tls_grade = mystrdup(val);
281 continue;
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",
287 WHERE, name);
288 *site_level = TLS_LEV_INVALID;
289 break;
291 session->tls_protocols = mystrdup(val);
292 continue;
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;
302 break;
304 if (*val == 0) {
305 msg_warn("%s: attribute \"%s\" has empty value", WHERE, name);
306 *site_level = TLS_LEV_INVALID;
307 break;
309 if (session->tls_matchargv == 0)
310 session->tls_matchargv = argv_split(val, delim);
311 else
312 argv_split_append(session->tls_matchargv, val, delim);
313 continue;
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",
319 WHERE, name);
320 *site_level = TLS_LEV_INVALID;
321 break;
323 session->tls_exclusions = vstring_strcpy(vstring_alloc(10), val);
324 continue;
325 } else {
326 msg_warn("%s: invalid attribute name: \"%s\"", WHERE, name);
327 *site_level = TLS_LEV_INVALID;
328 break;
331 FREE_RETURN(1);
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);
350 return;
354 * XXX For clarity consider using ``do { .. } while'', instead of using
355 * ``while { .. }'' with loop control at the bottom.
357 while (1) {
358 /* Try the given domain */
359 if (tls_policy_lookup_one(session, site_level, site_name, site_class))
360 return;
361 /* Re-try with parent domain */
362 if ((site_name = strchr(site_name + 1, '.')) == 0)
363 return;
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:
381 case TLS_LEV_NONE:
382 return;
384 case TLS_LEV_MAY:
385 if (session->tls_grade == 0)
386 session->tls_grade = mystrdup(var_smtp_tls_ciph);
387 break;
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";
394 break;
396 case TLS_LEV_FPRINT:
397 case TLS_LEV_VERIFY:
398 case TLS_LEV_SECURE:
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";
403 break;
406 #define ADD_EXCLUDE(vstr, str) \
407 do { \
408 if (*(str)) \
409 vstring_sprintf_append((vstr), "%s%s", \
410 VSTRING_LEN(vstr) ? " " : "", (str)); \
411 } while (0)
414 * The "exclude" policy table attribute overrides main.cf exclusion
415 * lists.
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";
431 int global_level;
432 int site_level;
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
449 * per-site policy.
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;
460 } else {
461 global_level = var_smtp_use_tls ?
462 TLS_LEV_MAY : TLS_LEV_NONE;
464 if (msg_verbose)
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;
474 if (tls_policy) {
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");
480 if (msg_verbose)
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
485 * policy.
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
497 * policies.
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;
504 else
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:
528 case TLS_LEV_NONE:
529 case TLS_LEV_MAY:
530 case TLS_LEV_ENCRYPT:
531 break;
532 case TLS_LEV_FPRINT:
533 session->tls_matchargv =
534 argv_split(var_smtp_tls_fpt_cmatch, "\t\n\r, |");
535 break;
536 case TLS_LEV_VERIFY:
537 session->tls_matchargv =
538 argv_split(var_smtp_tls_vfy_cmatch, "\t\n\r, :");
539 break;
540 case TLS_LEV_SECURE:
541 session->tls_matchargv =
542 argv_split(var_smtp_tls_sec_cmatch, "\t\n\r, :");
543 break;
544 default:
545 msg_panic("unexpected TLS security level: %d",
546 session->tls_level);
549 if (msg_verbose && (tls_policy || tls_per_site))
550 msg_info("%s TLS level: %s", "effective",
551 policy_name(session->tls_level));
554 #endif
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,
561 int flags)
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);
571 session->helo = 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;
583 if (session->port) {
584 vstring_sprintf(session->buffer, "%s:%d",
585 session->namaddr, ntohs(session->port));
586 session->namaddrport = mystrdup(STR(session->buffer));
587 } else
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);
594 else
595 DONT_CACHE_THIS_SESSION;
596 session->reuse_count = 0;
597 USE_NEWBORN_SESSION; /* He's not dead Jim! */
599 #ifdef USE_SASL_AUTH
600 smtp_sasl_connect(session);
601 #endif
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.
608 #ifdef USE_TLS
609 session_tls_init(session, dest, host, flags);
610 #endif
611 session->state = 0;
612 debug_peer_check(host, addr);
613 return (session);
616 /* smtp_session_free - destroy SMTP_SESSION structure and contents */
618 void smtp_session_free(SMTP_SESSION *session)
620 #ifdef USE_TLS
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);
635 #endif
636 if (session->stream)
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);
643 if (session->helo)
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);
655 #ifdef USE_SASL_AUTH
656 smtp_sasl_cleanup(session);
657 #endif
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,
666 VSTRING *endp_prop)
668 int fd;
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
674 * smtp_proto()).
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
694 * session too much.
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.
709 #ifdef notdef
710 if (smtp_sasl_enable)
711 smtp_sasl_passivate(endp_prop, session);
712 #endif
715 * Salvage the underlying file descriptor, and destroy the session
716 * object.
718 fd = vstream_fileno(session->stream);
719 vstream_fdclose(session->stream);
720 session->stream = 0;
721 smtp_session_free(session);
723 return (fd);
726 /* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
728 SMTP_SESSION *smtp_session_activate(int fd, VSTRING *dest_prop,
729 VSTRING *endp_prop)
731 const char *myname = "smtp_session_activate";
732 SMTP_SESSION *session;
733 char *dest_props;
734 char *endp_props;
735 const char *prop;
736 const char *dest;
737 const char *host;
738 const char *addr;
739 unsigned port;
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);
755 return (0);
757 reuse_count = atoi(prop);
758 if ((dest = mystrtok(&endp_props, "\n")) == 0) {
759 msg_warn("%s: missing cached session destination property", myname);
760 return (0);
762 if ((host = mystrtok(&endp_props, "\n")) == 0) {
763 msg_warn("%s: missing cached session hostname property", myname);
764 return (0);
766 if ((addr = mystrtok(&endp_props, "\n")) == 0) {
767 msg_warn("%s: missing cached session address property", myname);
768 return (0);
770 if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
771 msg_warn("%s: bad cached session port property", myname);
772 return (0);
774 port = atoi(prop);
776 if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
777 msg_warn("%s: bad cached session features property", myname);
778 return (0);
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);
784 return (0);
786 #ifdef MISSING_STRTOUL
787 expire_time = strtol(prop, 0, 10);
788 #else
789 expire_time = strtoul(prop, 0, 10);
790 #endif
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);
796 return (0);
798 features |= atoi(prop);
802 * Allright, bundle up what we have sofar.
804 #define NO_FLAGS 0
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;
812 if (msg_verbose)
813 msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
814 "ttl=%ld, reuse=%d",
815 myname, dest, host, addr, ntohs(port), features,
816 (long) (expire_time - time((time_t *) 0)), reuse_count);
819 * Re-activate the SASL attributes.
821 #ifdef notdef
822 if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
823 vstream_fdclose(session->stream);
824 session->stream = 0;
825 smtp_session_free(session);
826 return (0);
828 #endif
830 return (session);