Current File : //root/postfix-3.2.0/src/smtp/smtp_session.c |
/*++
/* NAME
/* smtp_session 3
/* SUMMARY
/* SMTP_SESSION structure management
/* SYNOPSIS
/* #include "smtp.h"
/*
/* SMTP_SESSION *smtp_session_alloc(stream, iter, start, flags)
/* VSTREAM *stream;
/* SMTP_ITERATOR *iter;
/* time_t start;
/* int flags;
/*
/* void smtp_session_free(session)
/* SMTP_SESSION *session;
/*
/* int smtp_session_passivate(session, dest_prop, endp_prop)
/* SMTP_SESSION *session;
/* VSTRING *dest_prop;
/* VSTRING *endp_prop;
/*
/* SMTP_SESSION *smtp_session_activate(fd, iter, dest_prop, endp_prop)
/* int fd;
/* SMTP_ITERATOR *iter;
/* VSTRING *dest_prop;
/* VSTRING *endp_prop;
/* DESCRIPTION
/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
/* and initializes it with the given stream and destination, host name
/* and address information. The host name and address strings are
/* copied. The port is in network byte order.
/*
/* smtp_session_free() destroys an SMTP_SESSION structure and its
/* members, making memory available for reuse. It will handle the
/* case of a null stream and will assume it was given a different
/* purpose.
/*
/* smtp_session_passivate() flattens an SMTP session so that
/* it can be cached. The SMTP_SESSION structure is destroyed.
/*
/* smtp_session_activate() inflates a flattened SMTP session
/* so that it can be used. The input property arguments are
/* modified.
/*
/* Arguments:
/* .IP stream
/* A full-duplex stream.
/* .IP iter
/* The literal next-hop or fall-back destination including
/* the optional [] and including the :port or :service;
/* the name of the remote host;
/* the printable address of the remote host;
/* the remote port in network byte order.
/* .IP start
/* The time when this connection was opened.
/* .IP flags
/* Zero or more of the following:
/* .RS
/* .IP SMTP_MISC_FLAG_CONN_LOAD
/* Enable re-use of cached SMTP or LMTP connections.
/* .IP SMTP_MISC_FLAG_CONN_STORE
/* Enable saving of cached SMTP or LMTP connections.
/* .RE
/* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
/* .IP dest_prop
/* Destination specific session properties: the server is the
/* best MX host for the current logical destination, the dest,
/* host, and addr properties. When dest_prop is non-empty, it
/* overrides the iterator dest, host, and addr properties. It
/* is the caller's responsibility to save the current nexthop
/* with SMTP_ITER_SAVE_DEST() and to restore it afterwards
/* with SMTP_ITER_RESTORE_DEST() before trying alternatives.
/* .IP endp_prop
/* Endpoint specific session properties: all the features
/* advertised by the remote server.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Viktor Dukhovni
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <stringops.h>
/* Global library. */
#include <mime_state.h>
#include <debug_peer.h>
#include <mail_params.h>
/* Application-specific. */
#include "smtp.h"
#include "smtp_sasl.h"
/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, SMTP_ITERATOR *iter,
time_t start, int flags)
{
SMTP_SESSION *session;
const char *host = STR(iter->host);
const char *addr = STR(iter->addr);
unsigned port = iter->port;
session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream;
session->iterator = iter;
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
session->helo = 0;
session->port = port;
session->features = 0;
session->size_limit = 0;
session->error_mask = 0;
session->buffer = vstring_alloc(100);
session->scratch = vstring_alloc(100);
session->scratch2 = vstring_alloc(100);
smtp_chat_init(session);
session->mime_state = 0;
if (session->port) {
vstring_sprintf(session->buffer, "%s:%d",
session->namaddr, ntohs(session->port));
session->namaddrport = mystrdup(STR(session->buffer));
} else
session->namaddrport = mystrdup(session->namaddr);
session->send_proto_helo = 0;
if (flags & SMTP_MISC_FLAG_CONN_STORE)
CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
else
DONT_CACHE_THIS_SESSION;
session->reuse_count = 0;
USE_NEWBORN_SESSION; /* He's not dead Jim! */
#ifdef USE_SASL_AUTH
smtp_sasl_connect(session);
#endif
#ifdef USE_TLS
session->tls_context = 0;
session->tls_retry_plain = 0;
session->tls_nexthop = 0;
#endif
session->state = 0;
debug_peer_check(host, addr);
return (session);
}
/* smtp_session_free - destroy SMTP_SESSION structure and contents */
void smtp_session_free(SMTP_SESSION *session)
{
#ifdef USE_TLS
if (session->stream) {
vstream_fflush(session->stream);
if (session->tls_context)
tls_client_stop(smtp_tls_ctx, session->stream,
var_smtp_starttls_tmout, 0, session->tls_context);
}
#endif
if (session->stream)
vstream_fclose(session->stream);
myfree(session->namaddr);
myfree(session->namaddrport);
if (session->helo)
myfree(session->helo);
vstring_free(session->buffer);
vstring_free(session->scratch);
vstring_free(session->scratch2);
if (session->history)
smtp_chat_reset(session);
if (session->mime_state)
mime_state_free(session->mime_state);
#ifdef USE_SASL_AUTH
smtp_sasl_cleanup(session);
#endif
debug_peer_restore();
myfree((void *) session);
}
/* smtp_session_passivate - passivate an SMTP_SESSION object */
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop,
VSTRING *endp_prop)
{
SMTP_ITERATOR *iter = session->iterator;
int fd;
/*
* Encode the local-to-physical binding properties: whether or not this
* server is best MX host for the next-hop or fall-back logical
* destination (this information is needed for loop handling in
* smtp_proto()).
*
* XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
* serialize the properties with attr_print() instead of using ad-hoc,
* non-reusable, code and hard-coded format strings.
*
* TODO: save SASL username and password information so that we can
* correctly save a reused authenticated connection.
*
*/
vstring_sprintf(dest_prop, "%s\n%s\n%s\n%u",
STR(iter->dest), STR(iter->host), STR(iter->addr),
session->features & SMTP_FEATURE_DESTINATION_MASK);
/*
* Encode the physical endpoint properties: all the session properties
* except for "session from cache", "best MX", or "RSET failure".
*
* XXX It would be nice to have a VSTRING to VSTREAM adapter so that we can
* serialize the properties with attr_print() instead of using obscure
* hard-coded format strings.
*
* XXX Should also record an absolute time when a session must be closed,
* how many non-delivering mail transactions there were during this
* session, and perhaps other statistics, so that we don't reuse a
* session too much.
*
* XXX Be sure to use unsigned types in the format string. Sign characters
* would be rejected by the alldig() test on the reading end.
*/
vstring_sprintf(endp_prop, "%u\n%u\n%lu",
session->reuse_count,
session->features & SMTP_FEATURE_ENDPOINT_MASK,
(long) session->expire_time);
/*
* Append the passivated SASL attributes.
*/
#ifdef notdef
if (smtp_sasl_enable)
smtp_sasl_passivate(endp_prop, session);
#endif
/*
* Salvage the underlying file descriptor, and destroy the session
* object.
*/
fd = vstream_fileno(session->stream);
vstream_fdclose(session->stream);
session->stream = 0;
smtp_session_free(session);
return (fd);
}
/* smtp_session_activate - re-activate a passivated SMTP_SESSION object */
SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
VSTRING *dest_prop,
VSTRING *endp_prop)
{
const char *myname = "smtp_session_activate";
SMTP_SESSION *session;
char *dest_props;
char *endp_props;
const char *prop;
const char *dest;
const char *host;
const char *addr;
unsigned features; /* server features */
time_t expire_time; /* session re-use expiration time */
unsigned reuse_count; /* # times reused */
/*
* XXX it would be nice to have a VSTRING to VSTREAM adapter so that we
* can de-serialize the properties with attr_scan(), instead of using
* ad-hoc, non-reusable code.
*
* XXX As a preliminary solution we use mystrtok(), but that function is not
* suitable for zero-length fields.
*/
endp_props = STR(endp_prop);
if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
msg_warn("%s: bad cached session reuse count property", myname);
return (0);
}
reuse_count = atoi(prop);
if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
msg_warn("%s: bad cached session features property", myname);
return (0);
}
features = atoi(prop);
if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) {
msg_warn("%s: bad cached session expiration time property", myname);
return (0);
}
#ifdef MISSING_STRTOUL
expire_time = strtol(prop, 0, 10);
#else
expire_time = strtoul(prop, 0, 10);
#endif
/*
* Clobber the iterator's current nexthop, host and address fields with
* cached-connection information. This is done when a session is looked
* up by request nexthop instead of address and port. It is the caller's
* responsibility to save and restore the request nexthop with
* SMTP_ITER_SAVE_DEST() and SMTP_ITER_RESTORE_DEST().
*
* TODO: Eliminate the duplication between SMTP_ITERATOR and SMTP_SESSION.
*
* TODO: restore SASL username and password information so that we can
* correctly save a reused authenticated connection.
*/
if (dest_prop && VSTRING_LEN(dest_prop)) {
dest_props = STR(dest_prop);
if ((dest = mystrtok(&dest_props, "\n")) == 0) {
msg_warn("%s: missing cached session destination property", myname);
return (0);
}
if ((host = mystrtok(&dest_props, "\n")) == 0) {
msg_warn("%s: missing cached session hostname property", myname);
return (0);
}
if ((addr = mystrtok(&dest_props, "\n")) == 0) {
msg_warn("%s: missing cached session address property", myname);
return (0);
}
if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) {
msg_warn("%s: bad cached destination features property", myname);
return (0);
}
features |= atoi(prop);
SMTP_ITER_CLOBBER(iter, dest, host, addr);
}
/*
* Allright, bundle up what we have sofar.
*/
#define NO_FLAGS 0
session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), iter,
(time_t) 0, NO_FLAGS);
session->features = (features | SMTP_FEATURE_FROM_CACHE);
CACHE_THIS_SESSION_UNTIL(expire_time);
session->reuse_count = ++reuse_count;
if (msg_verbose)
msg_info("%s: dest=%s host=%s addr=%s port=%u features=0x%x, "
"ttl=%ld, reuse=%d",
myname, STR(iter->dest), STR(iter->host),
STR(iter->addr), ntohs(iter->port), features,
(long) (expire_time - time((time_t *) 0)),
reuse_count);
/*
* Re-activate the SASL attributes.
*/
#ifdef notdef
if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
vstream_fdclose(session->stream);
session->stream = 0;
smtp_session_free(session);
return (0);
}
#endif
return (session);
}