Example #1
0
int     tls_mgr_policy(const char *cache_type, int *cachable, int *timeout)
{
    int     status;

    /*
     * Create the tlsmgr client handle.
     */
    if (tls_mgr == 0)
	tls_mgr_open();

    /*
     * Request policy.
     */
    if (attr_clnt_request(tls_mgr,
			  ATTR_FLAG_NONE,	/* Request attributes */
			SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_POLICY),
			  SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply attributes */
			  RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
			  RECV_ATTR_INT(TLS_MGR_ATTR_CACHABLE, cachable),
			  RECV_ATTR_INT(TLS_MGR_ATTR_SESSTOUT, timeout),
			  ATTR_TYPE_END) != 3)
	status = TLS_MGR_STAT_FAIL;
    return (status);
}
Example #2
0
int     tls_mgr_seed(VSTRING *buf, int len)
{
    int     status;

    /*
     * Create the tlsmgr client handle.
     */
    if (tls_mgr == 0)
	tls_mgr_open();

    /*
     * Request seed.
     */
    if (attr_clnt_request(tls_mgr,
			  ATTR_FLAG_NONE,	/* Request attributes */
			  SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_SEED),
			  SEND_ATTR_INT(TLS_MGR_ATTR_SIZE, len),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply attributes */
			  RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
			  RECV_ATTR_DATA(TLS_MGR_ATTR_SEED, buf),
			  ATTR_TYPE_END) != 2)
	status = TLS_MGR_STAT_FAIL;
    return (status);
}
Example #3
0
int     tls_mgr_delete(const char *cache_type, const char *cache_id)
{
    int     status;

    /*
     * Create the tlsmgr client handle.
     */
    if (tls_mgr == 0)
	tls_mgr_open();

    /*
     * Send the request and receive the reply.
     */
    if (attr_clnt_request(tls_mgr,
			  ATTR_FLAG_NONE,	/* Request */
			SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_DELETE),
			  SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
			  SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply */
			  RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
			  ATTR_TYPE_END) != 1)
	status = TLS_MGR_STAT_FAIL;
    return (status);
}
Example #4
0
int     tls_mgr_lookup(const char *cache_type, const char *cache_id,
		               VSTRING *buf)
{
    int     status;

    /*
     * Create the tlsmgr client handle.
     */
    if (tls_mgr == 0)
	tls_mgr_open();

    /*
     * Send the request and receive the reply.
     */
    if (attr_clnt_request(tls_mgr,
			  ATTR_FLAG_NONE,	/* Request */
			SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP),
			  SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type),
			  SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply */
			  RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
			  RECV_ATTR_DATA(TLS_MGR_ATTR_SESSION, buf),
			  ATTR_TYPE_END) != 2)
	status = TLS_MGR_STAT_FAIL;
    return (status);
}
Example #5
0
int     anvil_clnt_lookup(ANVIL_CLNT *anvil_clnt, const char *service,
			          const char *addr, int *count, int *rate,
		             int *msgs, int *rcpts, int *newtls, int *auths)
{
    char   *ident = ANVIL_IDENT(service, addr);
    int     status;

    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
			  ATTR_FLAG_NONE,	/* Query attributes. */
			  SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_LOOKUP),
			  SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply attributes. */
			  RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
			  RECV_ATTR_INT(ANVIL_ATTR_COUNT, count),
			  RECV_ATTR_INT(ANVIL_ATTR_RATE, rate),
			  RECV_ATTR_INT(ANVIL_ATTR_MAIL, msgs),
			  RECV_ATTR_INT(ANVIL_ATTR_RCPT, rcpts),
			  RECV_ATTR_INT(ANVIL_ATTR_NTLS, newtls),
			  RECV_ATTR_INT(ANVIL_ATTR_AUTH, auths),
			  ATTR_TYPE_END) != 7)
	status = ANVIL_STAT_FAIL;
    else if (status != ANVIL_STAT_OK)
	status = ANVIL_STAT_FAIL;
    myfree(ident);
    return (status);
}
Example #6
0
int     anvil_clnt_auth(ANVIL_CLNT *anvil_clnt, const char *service,
			        const char *addr, int *auths)
{
    char   *ident = ANVIL_IDENT(service, addr);
    int     status;

    if (attr_clnt_request((ATTR_CLNT *) anvil_clnt,
			  ATTR_FLAG_NONE,	/* Query attributes. */
			  SEND_ATTR_STR(ANVIL_ATTR_REQ, ANVIL_REQ_AUTH),
			  SEND_ATTR_STR(ANVIL_ATTR_IDENT, ident),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply attributes. */
			  RECV_ATTR_INT(ANVIL_ATTR_STATUS, &status),
			  RECV_ATTR_INT(ANVIL_ATTR_RATE, auths),
			  ATTR_TYPE_END) != 2)
	status = ANVIL_STAT_FAIL;
    else if (status != ANVIL_STAT_OK)
	status = ANVIL_STAT_FAIL;
    myfree(ident);
    return (status);
}
Example #7
0
static void verify_update_service(VSTREAM *client_stream)
{
    VSTRING *buf = vstring_alloc(10);
    VSTRING *addr = vstring_alloc(10);
    int     addr_status;
    VSTRING *text = vstring_alloc(10);
    const char *status_name;
    const char *raw_data;
    long    probed;
    long    updated;

    if (attr_scan(client_stream, ATTR_FLAG_STRICT,
		  RECV_ATTR_STR(MAIL_ATTR_ADDR, addr),
		  RECV_ATTR_INT(MAIL_ATTR_ADDR_STATUS, &addr_status),
		  RECV_ATTR_STR(MAIL_ATTR_WHY, text),
		  ATTR_TYPE_END) == 3) {
	/* FIX 200501 IPv6 patch did not neuter ":" in address literals. */
	translit(STR(addr), ":", "_");
	if ((status_name = verify_stat2name(addr_status)) == 0) {
	    msg_warn("bad recipient status %d for recipient %s",
		     addr_status, STR(addr));
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_BAD),
		       ATTR_TYPE_END);
	} else {

	    /*
	     * Robustness: don't allow a failed probe to clobber an OK
	     * address before it expires. The failed probe is ignored so that
	     * the address will be re-probed upon the next query. As long as
	     * some probes succeed the address will remain cached as OK.
	     */
	    if (addr_status == DEL_RCPT_STAT_OK
		|| (raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0
		|| STATUS_FROM_RAW_ENTRY(raw_data) != DEL_RCPT_STAT_OK) {
		probed = 0;
		updated = (long) time((time_t *) 0);
		verify_make_entry(buf, addr_status, probed, updated, STR(text));
		if (msg_verbose)
		    msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s",
			STR(addr), addr_status, probed, updated, STR(text));
		dict_cache_update(verify_map, STR(addr), STR(buf));
	    }
	    attr_print(client_stream, ATTR_FLAG_NONE,
		       SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK),
		       ATTR_TYPE_END);
	}
    }
    vstring_free(buf);
    vstring_free(addr);
    vstring_free(text);
}
Example #8
0
static TLS_TICKET_KEY *request_scache_key(unsigned char *keyname)
{
    TLS_TICKET_KEY tmp;
    static VSTRING *keybuf;
    char   *name;
    size_t  len;
    int     status;

    /*
     * Create the tlsmgr client handle.
     */
    if (tls_mgr == 0)
	tls_mgr_open();

    if (keybuf == 0)
	keybuf = vstring_alloc(sizeof(tmp));

    /* In tlsmgr requests we encode null key names as empty strings. */
    name = keyname ? (char *) keyname : "";
    len = keyname ? TLS_TICKET_NAMELEN : 0;

    /*
     * Send the request and receive the reply.
     */
    if (attr_clnt_request(tls_mgr,
			  ATTR_FLAG_NONE,	/* Request */
			SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_TKTKEY),
			  SEND_ATTR_DATA(TLS_MGR_ATTR_KEYNAME, len, name),
			  ATTR_TYPE_END,
			  ATTR_FLAG_MISSING,	/* Reply */
			  RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status),
			  RECV_ATTR_DATA(TLS_MGR_ATTR_KEYBUF, keybuf),
			  ATTR_TYPE_END) != 2
	|| status != TLS_MGR_STAT_OK
	|| LEN(keybuf) != sizeof(tmp))
	return (0);

    memcpy((void *) &tmp, STR(keybuf), sizeof(tmp));
    return (tls_scache_key_rotate(&tmp));
}
Example #9
0
static void psc_dnsbl_receive(int event, void *context)
{
    const char *myname = "psc_dnsbl_receive";
    VSTREAM *stream = (VSTREAM *) context;
    PSC_DNSBL_SCORE *score;
    PSC_DNSBL_HEAD *head;
    PSC_DNSBL_SITE *site;
    ARGV   *reply_argv;
    int     request_id;
    int     dnsbl_ttl;

    PSC_CLEAR_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, context);

    /*
     * Receive the DNSBL lookup result.
     * 
     * This is preliminary code to explore the field. Later, DNSBL lookup will
     * be handled by an UDP-based DNS client that is built directly into some
     * Postfix daemon.
     * 
     * Don't bother looking up the blocklist score when the client IP address is
     * not listed at the DNSBL.
     * 
     * Don't panic when the blocklist score no longer exists. It may be deleted
     * when the client triggers a "drop" action after pregreet, when the
     * client does not pregreet and the DNSBL reply arrives late, or when the
     * client triggers a "drop" action after hanging up.
     */
    if (event == EVENT_READ
	&& attr_scan(stream,
		     ATTR_FLAG_STRICT,
		     RECV_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, reply_dnsbl),
		     RECV_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, reply_client),
		     RECV_ATTR_INT(MAIL_ATTR_LABEL, &request_id),
		     RECV_ATTR_STR(MAIL_ATTR_RBL_ADDR, reply_addr),
		     RECV_ATTR_INT(MAIL_ATTR_TTL, &dnsbl_ttl),
		     ATTR_TYPE_END) == 5
	&& (score = (PSC_DNSBL_SCORE *)
	    htable_find(dnsbl_score_cache, STR(reply_client))) != 0
	&& score->request_id == request_id) {

	/*
	 * Run this response past all applicable DNSBL filters and update the
	 * blocklist score for this client IP address.
	 * 
	 * Don't panic when the DNSBL domain name is not found. The DNSBLOG
	 * server may be messed up.
	 */
	if (msg_verbose > 1)
	    msg_info("%s: client=\"%s\" score=%d domain=\"%s\" reply=\"%d %s\"",
		     myname, STR(reply_client), score->total,
		     STR(reply_dnsbl), dnsbl_ttl, STR(reply_addr));
	head = (PSC_DNSBL_HEAD *)
	    htable_find(dnsbl_site_cache, STR(reply_dnsbl));
	if (head == 0) {
	    /* Bogus domain. Do nothing. */
	} else if (*STR(reply_addr) != 0) {
	    /* DNS reputation record(s) found. */
	    reply_argv = 0;
	    for (site = head->first; site != 0; site = site->next) {
		if (site->byte_codes == 0
		    || psc_dnsbl_match(site->byte_codes, reply_argv ? reply_argv :
			 (reply_argv = argv_split(STR(reply_addr), " ")))) {
		    if (score->dnsbl_name == 0
			|| score->dnsbl_weight < site->weight) {
			score->dnsbl_name = head->safe_dnsbl;
			score->dnsbl_weight = site->weight;
		    }
		    score->total += site->weight;
		    if (msg_verbose > 1)
			msg_info("%s: filter=\"%s\" weight=%d score=%d",
			       myname, site->filter ? site->filter : "null",
				 site->weight, score->total);
		}
		/* As with dnsblog(8), a value < 0 means no reply TTL. */
		if (site->weight > 0) {
		    if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
			score->fail_ttl = dnsbl_ttl;
		} else {
		    if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
			score->pass_ttl = dnsbl_ttl;
		}
	    }
	    if (reply_argv != 0)
		argv_free(reply_argv);
	} else {
	    /* No DNS reputation record found. */
	    for (site = head->first; site != 0; site = site->next) {
		/* As with dnsblog(8), a value < 0 means no reply TTL. */
		if (site->weight > 0) {
		    if (score->pass_ttl < 0 || score->pass_ttl > dnsbl_ttl)
			score->pass_ttl = dnsbl_ttl;
		} else {
		    if (score->fail_ttl < 0 || score->fail_ttl > dnsbl_ttl)
			score->fail_ttl = dnsbl_ttl;
		}
	    }
	}

	/*
	 * Notify the requestor(s) that the result is ready to be picked up.
	 * If this call isn't made, clients have to sit out the entire
	 * pre-handshake delay.
	 */
	score->pending_lookups -= 1;
	if (score->pending_lookups == 0)
	    PSC_CALL_BACK_NOTIFY(score, PSC_NULL_EVENT);
    } else if (event == EVENT_TIME) {
	msg_warn("dnsblog reply timeout %ds for %s",
		 var_psc_dnsbl_tmout, (char *) vstream_context(stream));
    }
    /* Here, score may be a null pointer. */
    vstream_fclose(stream);
}
Example #10
0
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
{
    VSTRING *buf = vstring_alloc(100);
    CLEANUP_STATE *state;
    int     flags;
    int     type = 0;
    int     status;

    /*
     * Sanity check. This service takes no command-line arguments.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);

    /*
     * Open a queue file and initialize state.
     */
    state = cleanup_open(src);

    /*
     * Send the queue id to the client. Read client processing options. If we
     * can't read the client processing options we can pretty much forget
     * about the whole operation.
     */
    attr_print(src, ATTR_FLAG_NONE,
	       SEND_ATTR_STR(MAIL_ATTR_QUEUEID, state->queue_id),
	       ATTR_TYPE_END);
    if (attr_scan(src, ATTR_FLAG_STRICT,
		  RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
		  ATTR_TYPE_END) != 1) {
	state->errs |= CLEANUP_STAT_BAD;
	flags = 0;
    }
    cleanup_control(state, flags);

    /*
     * XXX Rely on the front-end programs to enforce record size limits.
     * 
     * First, copy the envelope records to the queue file. Then, copy the
     * message content (headers and body). Finally, attach any information
     * extracted from message headers.
     */
    while (CLEANUP_OUT_OK(state)) {
	if ((type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) < 0) {
	    state->errs |= CLEANUP_STAT_BAD;
	    break;
	}
	if (REC_GET_HIDDEN_TYPE(type)) {
	    msg_warn("%s: record type %d not allowed - discarding this message",
		     state->queue_id, type);
	    state->errs |= CLEANUP_STAT_BAD;
	    break;
	}
	CLEANUP_RECORD(state, type, vstring_str(buf), VSTRING_LEN(buf));
	if (type == REC_TYPE_END)
	    break;
    }

    /*
     * Keep reading in case of problems, until the sender is ready to receive
     * our status report.
     */
    if (CLEANUP_OUT_OK(state) == 0 && type > 0) {
	while (type != REC_TYPE_END
	       && (type = rec_get_raw(src, buf, 0, REC_FLAG_NONE)) > 0) {
	    if (type == REC_TYPE_MILT_COUNT) {
		int     milter_count = atoi(vstring_str(buf));

		/* Avoid deadlock. */
		if (milter_count >= 0)
		    cleanup_milter_receive(state, milter_count);
	    }
	}
    }

    /*
     * Log something to make timeout errors easier to debug.
     */
    if (vstream_ftimeout(src))
	msg_warn("%s: read timeout on %s",
		 state->queue_id, VSTREAM_PATH(src));

    /*
     * Finish this message, and report the result status to the client.
     */
    status = cleanup_flush(state);		/* in case state is modified */
    attr_print(src, ATTR_FLAG_NONE,
	       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
	       SEND_ATTR_STR(MAIL_ATTR_WHY,
			     (state->flags & CLEANUP_FLAG_SMTP_REPLY)
			     && state->smtp_reply ? state->smtp_reply :
			     state->reason ? state->reason : ""),
	       ATTR_TYPE_END);
    cleanup_free(state);

    /*
     * Cleanup.
     */
    vstring_free(buf);
}
Example #11
0
static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
			        DELIVER_ATTR attr, char *delivered)
{
    const char *myname = "forward_send";
    VSTRING *buffer = vstring_alloc(100);
    VSTRING *folded;
    int     status;
    int     rec_type = 0;

    /*
     * Start the message content segment. Prepend our Delivered-To: header to
     * the message data. Stop at the first error. XXX Rely on the front-end
     * services to enforce record size limits.
     */
    rec_fputs(info->cleanup, REC_TYPE_MESG, "");
    vstring_strcpy(buffer, delivered);
    rec_fprintf(info->cleanup, REC_TYPE_NORM, "Received: by %s (%s)",
		var_myhostname, var_mail_name);
    rec_fprintf(info->cleanup, REC_TYPE_NORM, "\tid %s; %s",
		info->queue_id, mail_date(info->posting_time.tv_sec));
    if (local_deliver_hdr_mask & DELIVER_HDR_FWD) {
	folded = vstring_alloc(100);
	rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
		    casefold(folded, (STR(buffer))));
	vstring_free(folded);
    }
    if ((status = vstream_ferror(info->cleanup)) == 0)
	if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
	    msg_fatal("%s: seek queue file %s: %m:",
		      myname, VSTREAM_PATH(attr.fp));
    while (status == 0 && (rec_type = rec_get(attr.fp, buffer, 0)) > 0) {
	if (rec_type != REC_TYPE_CONT && rec_type != REC_TYPE_NORM)
	    break;
	status = (REC_PUT_BUF(info->cleanup, rec_type, buffer) != rec_type);
    }
    if (status == 0 && rec_type != REC_TYPE_XTRA) {
	msg_warn("%s: bad record type: %d in message content",
		 info->queue_id, rec_type);
	status |= mark_corrupt(attr.fp);
    }

    /*
     * Send the end-of-data marker only when there were no errors.
     */
    if (status == 0) {
	rec_fputs(info->cleanup, REC_TYPE_XTRA, "");
	rec_fputs(info->cleanup, REC_TYPE_END, "");
    }

    /*
     * Retrieve the cleanup service completion status only if there are no
     * problems.
     */
    if (status == 0)
	if (vstream_fflush(info->cleanup)
	    || attr_scan(info->cleanup, ATTR_FLAG_MISSING,
			 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
			 ATTR_TYPE_END) != 1)
	    status = 1;

    /*
     * Log successful forwarding.
     * 
     * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
     * it again here.
     */
    if (status == 0) {
	attr.rcpt.dsn_notify =
	    (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
	     DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
	dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
		   "forwarded as %s", info->queue_id);
	status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
    }

    /*
     * Cleanup.
     */
    vstring_free(buffer);
    return (status);
}
Example #12
0
static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup,
		               PICKUP_INFO *info, VSTRING *buf)
{
    time_t  now = time((time_t *) 0);
    int     status;
    char   *name;

    /*
     * Protect against time-warped time stamps. Warn about mail that has been
     * queued for an excessive amount of time. Allow for some time drift with
     * network clients that mount the maildrop remotely - especially clients
     * that can't get their daylight savings offsets right.
     */
#define DAY_SECONDS 86400
#define HOUR_SECONDS 3600

    if (info->st.st_mtime > now + 2 * HOUR_SECONDS) {
	msg_warn("%s: message dated %ld seconds into the future",
		 info->id, (long) (info->st.st_mtime - now));
	info->st.st_mtime = now;
    } else if (info->st.st_mtime < now - DAY_SECONDS) {
	msg_warn("%s: message has been queued for %d days",
		 info->id, (int) ((now - info->st.st_mtime) / DAY_SECONDS));
    }

    /*
     * Add content inspection transport. See also postsuper(1).
     */
    if (*var_filter_xport)
	rec_fprintf(cleanup, REC_TYPE_FILT, "%s", var_filter_xport);

    /*
     * Copy the message envelope segment. Allow only those records that we
     * expect to see in the envelope section. The envelope segment must
     * contain an envelope sender address.
     */
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_ENVELOPE)) != 0)
	return (status);
    if (info->sender == 0) {
	msg_warn("%s: uid=%ld: no envelope sender",
		 info->id, (long) info->st.st_uid);
	return (REMOVE_MESSAGE_FILE);
    }

    /*
     * For messages belonging to $mail_owner also log the maildrop queue id.
     * This supports message tracking for mail requeued via "postsuper -r".
     */
#define MAIL_IS_REQUEUED(info) \
    ((info)->st.st_uid == var_owner_uid && ((info)->st.st_mode & S_IROTH) == 0)

    if (MAIL_IS_REQUEUED(info)) {
	msg_info("%s: uid=%d from=<%s> orig_id=%s", info->id,
		 (int) info->st.st_uid, info->sender,
		 ((name = strrchr(info->path, '/')) != 0 ?
		  name + 1 : info->path));
    } else {
	msg_info("%s: uid=%d from=<%s>", info->id,
		 (int) info->st.st_uid, info->sender);
    }

    /*
     * Message content segment. Send a dummy message length. Prepend a
     * Received: header to the message contents. For tracing purposes,
     * include the message file ownership, without revealing the login name.
     */
    rec_fputs(cleanup, REC_TYPE_MESG, "");
    rec_fprintf(cleanup, REC_TYPE_NORM, "Received: by %s (%s, from userid %ld)",
		var_myhostname, var_mail_name, (long) info->st.st_uid);
    rec_fprintf(cleanup, REC_TYPE_NORM, "\tid %s; %s", info->id,
		mail_date(info->st.st_mtime));

    /*
     * Copy the message content segment. Allow only those records that we
     * expect to see in the message content section.
     */
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_CONTENT)) != 0)
	return (status);

    /*
     * Send the segment with information extracted from message headers.
     * Permit a non-empty extracted segment, so that list manager software
     * can to output recipients after the message, and so that sysadmins can
     * re-inject messages after a change of configuration.
     */
    rec_fputs(cleanup, REC_TYPE_XTRA, "");
    if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_EXTRACT)) != 0)
	return (status);

    /*
     * There are no errors. Send the end-of-data marker, and get the cleanup
     * service completion status. XXX Since the pickup service is unable to
     * bounce, the cleanup service can report only soft errors here.
     */
    rec_fputs(cleanup, REC_TYPE_END, "");
    if (attr_scan(cleanup, ATTR_FLAG_MISSING,
		  RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
		  RECV_ATTR_STR(MAIL_ATTR_WHY, buf),
		  ATTR_TYPE_END) != 2)
	return (cleanup_service_error(info, CLEANUP_STAT_WRITE));

    /*
     * Depending on the cleanup service completion status, delete the message
     * file, or try again later. Bounces are dealt with by the cleanup
     * service itself. The master process wakes up the cleanup service every
     * now and then.
     */
    if (status) {
	return (cleanup_service_error_reason(info, status, vstring_str(buf)));
    } else {
	return (REMOVE_MESSAGE_FILE);
    }
}
Example #13
0
static void bounce_service(VSTREAM *client, char *service_name, char **argv)
{
    int     command;
    int     status;

    /*
     * Sanity check. This service takes no command-line arguments. The
     * service name should be usable as a subdirectory name.
     */
    if (argv[0])
	msg_fatal("unexpected command-line argument: %s", argv[0]);
    if (mail_queue_name_ok(service_name) == 0)
	msg_fatal("malformed service name: %s", service_name);

    /*
     * Read and validate the first parameter of the client request. Let the
     * request-specific protocol routines take care of the remainder.
     */
    if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
		  RECV_ATTR_INT(MAIL_ATTR_NREQ, &command), 0) != 1) {
	msg_warn("malformed request");
	status = -1;
    } else if (command == BOUNCE_CMD_VERP) {
	status = bounce_verp_proto(service_name, client);
    } else if (command == BOUNCE_CMD_FLUSH) {
	status = bounce_notify_proto(service_name, client,
				     bounce_notify_service);
    } else if (command == BOUNCE_CMD_WARN) {
	status = bounce_notify_proto(service_name, client,
				     bounce_warn_service);
    } else if (command == BOUNCE_CMD_TRACE) {
	status = bounce_notify_proto(service_name, client,
				     bounce_trace_service);
    } else if (command == BOUNCE_CMD_APPEND) {
	status = bounce_append_proto(service_name, client);
    } else if (command == BOUNCE_CMD_ONE) {
	status = bounce_one_proto(service_name, client);
    } else {
	msg_warn("unknown command: %d", command);
	status = -1;
    }

    /*
     * When the request has completed, send the completion status to the
     * client.
     */
    attr_print(client, ATTR_FLAG_NONE,
	       SEND_ATTR_INT(MAIL_ATTR_STATUS, status),
	       ATTR_TYPE_END);
    vstream_fflush(client);

    /*
     * When a cleanup trap was set, delete the log file in case of error.
     * This includes errors while sending the completion status to the
     * client.
     */
    if (bounce_cleanup_path) {
	if (status || vstream_ferror(client))
	    bounce_cleanup_log();
	bounce_cleanup_unregister();
    }
}
Example #14
0
static int bounce_one_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_one_proto";
    int     flags;
    int     smtputf8;
    int     dsn_ret;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
			    ATTR_TYPE_END) != 10) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
	msg_warn("wrong service name \"%s\" for one-recipient bouncing",
		 service_name);
	return (-1);
    }
    if (mail_queue_name_ok(STR(queue_name)) == 0) {
	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
	return (-1);
    }
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    VS_NEUTER(encoding);
    VS_NEUTER(sender);
    VS_NEUTER(dsn_envid);
    VS_NEUTER(rcpt_buf->address);
    VS_NEUTER(rcpt_buf->orig_addr);
    VS_NEUTER(rcpt_buf->dsn_orcpt);
    VS_NEUTER(dsn_buf->status);
    VS_NEUTER(dsn_buf->action);
    VS_NEUTER(dsn_buf->reason);
    VS_NEUTER(dsn_buf->dtype);
    VS_NEUTER(dsn_buf->dtext);
    VS_NEUTER(dsn_buf->mtype);
    VS_NEUTER(dsn_buf->mname);
    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    (void) DSN_FROM_DSN_BUF(dsn_buf);

    /*
     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
     * RECIPIENT_FROM_RCPT_BUF().
     */
    if (msg_verbose)
	msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
		 myname, flags, STR(queue_name), STR(queue_id),
		 STR(encoding), smtputf8, STR(sender), STR(dsn_envid),
		 dsn_ret, STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
		 STR(dsn_buf->action), STR(dsn_buf->reason));

    /*
     * Execute the request.
     */
    return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
			       STR(encoding), smtputf8, STR(sender),
			       STR(dsn_envid), dsn_ret, rcpt_buf,
			       dsn_buf, bounce_templates));
}
Example #15
0
static int bounce_verp_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_verp_proto";
    int     flags;
    int     smtputf8;
    int     dsn_ret;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
			    RECV_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims),
			    ATTR_TYPE_END) != 9) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (mail_queue_name_ok(STR(queue_name)) == 0) {
	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
	return (-1);
    }
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    VS_NEUTER(encoding);
    VS_NEUTER(sender);
    VS_NEUTER(dsn_envid);
    VS_NEUTER(verp_delims);
    if (strlen(STR(verp_delims)) != 2) {
	msg_warn("malformed verp delimiter string: %s", STR(verp_delims));
	return (-1);
    }
    if (msg_verbose)
	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x delim=%s",
		 myname, flags, service_name, STR(queue_name),
		 STR(queue_id), STR(encoding), smtputf8, STR(sender),
		 STR(dsn_envid), dsn_ret, STR(verp_delims));

    /*
     * On request by the client, set up a trap to delete the log file in case
     * of errors.
     */
    if (flags & BOUNCE_FLAG_CLEAN)
	bounce_cleanup_register(service_name, STR(queue_id));

    /*
     * Execute the request. Fall back to traditional notification if a bounce
     * was returned as undeliverable, because we don't want to VERPify those.
     */
    if (!*STR(sender) || !strcasecmp_utf8(STR(sender),
					  mail_addr_double_bounce())) {
	msg_warn("request to send VERP-style notification of bounced mail");
	return (bounce_notify_service(flags, service_name, STR(queue_name),
				      STR(queue_id), STR(encoding), smtputf8,
				      STR(sender), STR(dsn_envid), dsn_ret,
				      bounce_templates));
    } else
	return (bounce_notify_verp(flags, service_name, STR(queue_name),
				   STR(queue_id), STR(encoding), smtputf8,
				   STR(sender), STR(dsn_envid), dsn_ret,
				   STR(verp_delims), bounce_templates));
}
Example #16
0
static int bounce_notify_proto(char *service_name, VSTREAM *client,
			        int (*service) (int, char *, char *, char *,
				           char *, int, char *, char *, int,
						        BOUNCE_TEMPLATES *))
{
    const char *myname = "bounce_notify_proto";
    int     flags;
    int     smtputf8;
    int     dsn_ret;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			    RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding),
			    RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8),
			    RECV_ATTR_STR(MAIL_ATTR_SENDER, sender),
			    RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid),
			    RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret),
			    ATTR_TYPE_END) != 8) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (mail_queue_name_ok(STR(queue_name)) == 0) {
	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
	return (-1);
    }
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    VS_NEUTER(encoding);
    VS_NEUTER(sender);
    VS_NEUTER(dsn_envid);
    if (msg_verbose)
	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x",
		 myname, flags, service_name, STR(queue_name), STR(queue_id),
		 STR(encoding), smtputf8, STR(sender), STR(dsn_envid),
		 dsn_ret);

    /*
     * On request by the client, set up a trap to delete the log file in case
     * of errors.
     */
    if (flags & BOUNCE_FLAG_CLEAN)
	bounce_cleanup_register(service_name, STR(queue_id));

    /*
     * Execute the request.
     */
    return (service(flags, service_name, STR(queue_name),
		    STR(queue_id), STR(encoding), smtputf8,
		    STR(sender), STR(dsn_envid), dsn_ret,
		    bounce_templates));
}
Example #17
0
static int bounce_append_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_append_proto";
    int     flags;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags),
			    RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
			    RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf),
			    RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf),
			    ATTR_TYPE_END) != 4) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    VS_NEUTER(rcpt_buf->address);
    VS_NEUTER(rcpt_buf->orig_addr);
    VS_NEUTER(rcpt_buf->dsn_orcpt);
    VS_NEUTER(dsn_buf->status);
    VS_NEUTER(dsn_buf->action);
    VS_NEUTER(dsn_buf->reason);
    VS_NEUTER(dsn_buf->dtype);
    VS_NEUTER(dsn_buf->dtext);
    VS_NEUTER(dsn_buf->mtype);
    VS_NEUTER(dsn_buf->mname);
    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    (void) DSN_FROM_DSN_BUF(dsn_buf);

    /*
     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
     * RECIPIENT_FROM_RCPT_BUF().
     */
    if (msg_verbose)
	msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
		 myname, flags, service_name, STR(queue_id),
		 STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
		 STR(dsn_buf->action), STR(dsn_buf->reason));

    /*
     * On request by the client, set up a trap to delete the log file in case
     * of errors.
     */
    if (flags & BOUNCE_FLAG_CLEAN)
	bounce_cleanup_register(service_name, STR(queue_id));

    /*
     * Execute the request.
     */
    return (bounce_append_service(flags, service_name, STR(queue_id),
				  &rcpt_buf->rcpt, &dsn_buf->dsn));
}
Example #18
0
static void tlsp_get_request_event(int event, void *context)
{
    const char *myname = "tlsp_get_request_event";
    TLSP_STATE *state = (TLSP_STATE *) context;
    VSTREAM *plaintext_stream = state->plaintext_stream;
    int     plaintext_fd = vstream_fileno(plaintext_stream);
    static VSTRING *remote_endpt;
    static VSTRING *server_id;
    int     req_flags;
    int     timeout;
    int     ready;

    /*
     * One-time initialization.
     */
    if (remote_endpt == 0) {
        remote_endpt = vstring_alloc(10);
        server_id = vstring_alloc(10);
    }

    /*
     * At this point we still manually manage plaintext read/write/timeout
     * events. Turn off timer events. Below we disable read events on error,
     * and redefine read events on success.
     */
    if (event != EVENT_TIME)
        event_cancel_timer(tlsp_get_request_event, (void *) state);
    else
        errno = ETIMEDOUT;

    /*
     * We must send some data, after receiving the request attributes and
     * before receiving the remote file descriptor. We can't assume
     * UNIX-domain socket semantics here.
     */
    if (event != EVENT_READ
            || attr_scan(plaintext_stream, ATTR_FLAG_STRICT,
                         RECV_ATTR_STR(MAIL_ATTR_REMOTE_ENDPT, remote_endpt),
                         RECV_ATTR_INT(MAIL_ATTR_FLAGS, &req_flags),
                         RECV_ATTR_INT(MAIL_ATTR_TIMEOUT, &timeout),
                         RECV_ATTR_STR(MAIL_ATTR_SERVER_ID, server_id),
                         ATTR_TYPE_END) != 4) {
        msg_warn("%s: receive request attributes: %m", myname);
        event_disable_readwrite(plaintext_fd);
        tlsp_state_free(state);
        return;
    }

    /*
     * If the requested TLS engine is unavailable, hang up after making sure
     * that the plaintext peer has received our "sorry" indication.
     */
    ready = ((req_flags & TLS_PROXY_FLAG_ROLE_SERVER) != 0
             && tlsp_server_ctx != 0);
    if (attr_print(plaintext_stream, ATTR_FLAG_NONE,
                   SEND_ATTR_INT(MAIL_ATTR_STATUS, ready),
                   ATTR_TYPE_END) != 0
            || vstream_fflush(plaintext_stream) != 0
            || ready == 0) {
        read_wait(plaintext_fd, TLSP_INIT_TIMEOUT);	/* XXX */
        event_disable_readwrite(plaintext_fd);
        tlsp_state_free(state);
        return;
    }

    /*
     * XXX We use the same fixed timeout throughout the entire session for
     * both plaintext and ciphertext communication. This timeout is just a
     * safety feature; the real timeout will be enforced by our plaintext
     * peer.
     */
    else {
        state->remote_endpt = mystrdup(STR(remote_endpt));
        state->server_id = mystrdup(STR(server_id));
        msg_info("CONNECT %s %s",
                 (req_flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "from" :
                 (req_flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "to" :
                 "(bogus_direction)", state->remote_endpt);
        state->req_flags = req_flags;
        state->timeout = timeout + 10;		/* XXX */
        event_enable_read(plaintext_fd, tlsp_get_fd_event, (void *) state);
        event_request_timer(tlsp_get_fd_event, (void *) state,
                            TLSP_INIT_TIMEOUT);
        return;
    }
}
Example #19
0
SMTP_SESSION *smtp_session_activate(int fd, SMTP_ITERATOR *iter,
				            VSTRING *dest_prop,
				            VSTRING *endp_prop)
{
    const char *myname = "smtp_session_activate";
    VSTREAM *mp;
    SMTP_SESSION *session;
    int     endp_features;		/* server features */
    int     dest_features;		/* server features */
    long    expire_time;		/* session re-use expiration time */
    int     reuse_count;		/* # times reused */

#ifdef USE_TLS
    TLS_SESS_STATE *tls_context = 0;
    SMTP_TLS_POLICY *tls = iter->parent->tls;

#define TLS_PROXY_CONTEXT_FREE() do { \
    if (tls_context) \
	tls_proxy_context_free(tls_context); \
   } while (0)
#else
#define TLS_PROXY_CONTEXT_FREE()		/* nothing */
#endif

#define SMTP_SESSION_ACTIVATE_ERR_RETURN() do { \
	TLS_PROXY_CONTEXT_FREE(); \
	return (0); \
   } while (0)

    /*
     * Sanity check: if TLS is required, the cached properties must contain a
     * TLS context.
     */
    if ((mp = vstream_memopen(endp_prop, O_RDONLY)) == 0
	|| attr_scan_plain(mp, ATTR_FLAG_NONE,
#ifdef USE_TLS
			   RECV_ATTR_INT(SESS_ATTR_TLS_LEVEL,
					 &tls->level),
#endif
			   RECV_ATTR_INT(SESS_ATTR_REUSE_COUNT,
					 &reuse_count),
			   RECV_ATTR_INT(SESS_ATTR_ENDP_FEATURES,
					 &endp_features),
			   RECV_ATTR_LONG(SESS_ATTR_EXPIRE_TIME,
					  &expire_time),
			   ATTR_TYPE_END) != 4
#ifdef USE_TLS
	|| ((tls->level > TLS_LEV_MAY
	     || (tls->level == TLS_LEV_MAY && vstream_peek(mp) > 0))
	    && attr_scan_plain(mp, ATTR_FLAG_NONE,
			       RECV_ATTR_FUNC(tls_proxy_context_scan,
					      (void *) &tls_context),
			       ATTR_TYPE_END) != 1)
#endif
	|| vstream_fclose(mp) != 0) {
	msg_warn("smtp_session_activate: bad cached endp properties");
	SMTP_SESSION_ACTIVATE_ERR_RETURN();
    }

    /*
     * Clobber the iterator's current nexthop, host and address fields with
     * cached-connection information. This is done when a session is looked
     * up by delivery request nexthop instead of address and port. It is the
     * caller's responsibility to save and restore the delivery 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)) {
	if ((mp = vstream_memopen(dest_prop, O_RDONLY)) == 0
	    || attr_scan_plain(mp, ATTR_FLAG_NONE,
			       RECV_ATTR_STR(SESS_ATTR_DEST, iter->dest),
			       RECV_ATTR_STR(SESS_ATTR_HOST, iter->host),
			       RECV_ATTR_STR(SESS_ATTR_ADDR, iter->addr),
			       RECV_ATTR_INT(SESS_ATTR_DEST_FEATURES,
					     &dest_features),
			       ATTR_TYPE_END) != 4
	    || vstream_fclose(mp) != 0) {
	    msg_warn("smtp_session_passivate: bad cached dest properties");
	    SMTP_SESSION_ACTIVATE_ERR_RETURN();
	}
    } else {
	dest_features = 0;
    }
#ifdef USE_TLS
    if (msg_verbose)
	msg_info("%s: tls_level=%d", myname, tls->level);
#endif

    /*
     * 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 =
	(endp_features | dest_features | SMTP_FEATURE_FROM_CACHE);
#ifdef USE_TLS
    session->tls_context = tls_context;
#endif
    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),
		 endp_features | dest_features,
		 (long) (expire_time - time((time_t *) 0)),
		 reuse_count);

#if USE_TLS
    if (tls_context)
	tls_log_summary(TLS_ROLE_CLIENT, TLS_USAGE_USED,
			session->tls_context);
#endif

    return (session);
}