int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, VSTRING *endp_prop) { 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. */ vstring_sprintf(dest_prop, "%u", 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%s\n%s\n%s\n%u\n%u\n%lu", session->reuse_count, session->dest, session->host, session->addr, session->port, 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); }
int recv_pass_attr(int fd, HTABLE **attr, int timeout, ssize_t bufsize) { VSTREAM *fp; int stream_err; /* * Set up a temporary VSTREAM to receive the attributes. * * XXX We use one-character reads to simplify the implementation. */ fp = vstream_fdopen(fd, O_RDWR); vstream_control(fp, VSTREAM_CTL_BUFSIZE, bufsize, VSTREAM_CTL_TIMEOUT, timeout, VSTREAM_CTL_START_DEADLINE, VSTREAM_CTL_END); (void) attr_scan(fp, ATTR_FLAG_NONE, ATTR_TYPE_HASH, *attr = htable_create(1), ATTR_TYPE_END); stream_err = (vstream_feof(fp) || vstream_ferror(fp)); vstream_fdclose(fp); /* * Error reporting and recovery. */ if (stream_err) { htable_free(*attr, myfree); *attr = 0; return (-1); } else { if ((*attr)->used == 0) { htable_free(*attr, myfree); *attr = 0; } return (0); } }
SMTP_SESSION *smtp_session_activate(int fd, 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 port; 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 ((dest = mystrtok(&endp_props, "\n")) == 0) { msg_warn("%s: missing cached session destination property", myname); return (0); } if ((host = mystrtok(&endp_props, "\n")) == 0) { msg_warn("%s: missing cached session hostname property", myname); return (0); } if ((addr = mystrtok(&endp_props, "\n")) == 0) { msg_warn("%s: missing cached session address property", myname); return (0); } if ((prop = mystrtok(&endp_props, "\n")) == 0 || !alldig(prop)) { msg_warn("%s: bad cached session port property", myname); return (0); } port = 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 if (dest_prop && VSTRING_LEN(dest_prop)) { dest_props = STR(dest_prop); if ((prop = mystrtok(&dest_props, "\n")) == 0 || !alldig(prop)) { msg_warn("%s: bad cached destination features property", myname); return (0); } features |= atoi(prop); } /* * Allright, bundle up what we have sofar. */ #define NO_FLAGS 0 session = smtp_session_alloc(vstream_fdopen(fd, O_RDWR), dest, host, addr, port, (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, dest, host, addr, ntohs(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); }
int smtp_session_passivate(SMTP_SESSION *session, VSTRING *dest_prop, VSTRING *endp_prop) { SMTP_ITERATOR *iter = session->iterator; VSTREAM *mp; int fd; /* * Encode the delivery request next-hop to endpoint binding properties: * whether or not this server is best MX host for the delivery request * next-hop or fall-back logical destination (this information is needed * for loop handling in smtp_proto()). * * TODO: save SASL username and password information so that we can * correctly save a reused authenticated connection. * * These memory writes should never fail. */ if ((mp = vstream_memopen(dest_prop, O_WRONLY)) == 0 || attr_print_plain(mp, ATTR_FLAG_NONE, SEND_ATTR_STR(SESS_ATTR_DEST, STR(iter->dest)), SEND_ATTR_STR(SESS_ATTR_HOST, STR(iter->host)), SEND_ATTR_STR(SESS_ATTR_ADDR, STR(iter->addr)), SEND_ATTR_INT(SESS_ATTR_DEST_FEATURES, session->features & SMTP_FEATURE_DESTINATION_MASK), ATTR_TYPE_END) != 0 || vstream_fclose(mp) != 0) msg_fatal("smtp_session_passivate: can't save dest properties: %m"); /* * Encode the physical endpoint properties: all the session properties * except for "session from cache", "best MX", or "RSET failure". Plus * the TLS level, reuse count, and connection expiration time. * * XXX Should also record 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. * * TODO: passivate SASL username and password information so that we can * correctly save a reused authenticated connection. * * These memory writes should never fail. */ if ((mp = vstream_memopen(endp_prop, O_WRONLY)) == 0 || attr_print_plain(mp, ATTR_FLAG_NONE, #ifdef USE_TLS SEND_ATTR_INT(SESS_ATTR_TLS_LEVEL, session->state->tls->level), #endif SEND_ATTR_INT(SESS_ATTR_REUSE_COUNT, session->reuse_count), SEND_ATTR_INT(SESS_ATTR_ENDP_FEATURES, session->features & SMTP_FEATURE_ENDPOINT_MASK), SEND_ATTR_LONG(SESS_ATTR_EXPIRE_TIME, (long) session->expire_time), ATTR_TYPE_END) != 0 /* * Append the passivated TLS context. These memory writes should never * fail. */ #ifdef USE_TLS || (session->tls_context && attr_print_plain(mp, ATTR_FLAG_NONE, SEND_ATTR_FUNC(tls_proxy_context_print, (void *) session->tls_context), ATTR_TYPE_END) != 0) #endif || vstream_fclose(mp) != 0) msg_fatal("smtp_session_passivate: cannot save TLS context: %m"); /* * 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); }
void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; int pass_err; VSTREAM *fp; if (msg_verbose > 1) msg_info("%s: sq=%d cq=%d send socket %d from [%s]:%s", myname, psc_post_queue_length, psc_check_queue_length, vstream_fileno(state->smtp_client_stream), state->smtp_client_addr, state->smtp_client_port); /* * Connect to the real SMTP service over a local IPC channel, send the * file descriptor, and close the file descriptor to save resources. * Experience has shown that some systems will discard information when * we close a channel immediately after writing. Thus, we waste resources * waiting for the remote side to close the local IPC channel first. The * good side of waiting is that we learn when the real SMTP server is * falling behind. * * This is where we would forward the connection to an SMTP server that * provides an appropriate level of service for this client class. For * example, a server that is more forgiving, or one that is more * suspicious. Alternatively, we could send attributes along with the * socket with client reputation information, making everything even more * Postfix-specific. */ if ((server_fd = LOCAL_CONNECT(psc_smtpd_service_name, NON_BLOCKING, PSC_SEND_SOCK_CONNECT_TIMEOUT)) < 0) { msg_warn("cannot connect to service %s: %m", psc_smtpd_service_name); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 All server ports are busy\r\n"); psc_free_session_state(state); } return; } /* XXX Note: no dummy read between LOCAL_SEND_FD() and attr_print(). */ fp = vstream_fdopen(server_fd, O_RDWR); pass_err = (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 0 || (attr_print(fp, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, state->smtp_client_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_PORT, state->smtp_client_port), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_ADDR, state->smtp_server_addr), SEND_ATTR_STR(MAIL_ATTR_ACT_SERVER_PORT, state->smtp_server_port), ATTR_TYPE_END) || vstream_fflush(fp))); /* XXX Note: no read between attr_print() and vstream_fdclose(). */ (void) vstream_fdclose(fp); if (pass_err != 0) { msg_warn("cannot pass connection to service %s: %m", psc_smtpd_service_name); (void) close(server_fd); if (state->flags & PSC_STATE_FLAG_PREGR_TODO) { PSC_SMTPD_X21(state, "421 4.3.2 No system resources\r\n"); } else { PSC_SEND_REPLY(state, "421 4.3.2 No system resources\r\n"); psc_free_session_state(state); } return; } else { /* * Closing the smtp_client_fd here triggers a FreeBSD 7.1 kernel bug * where smtp-source sometimes sees the connection being closed after * it has already received the real SMTP server's 220 greeting! */ #if 0 PSC_DEL_CLIENT_STATE(state); #endif PSC_ADD_SERVER_STATE(state, server_fd); PSC_READ_EVENT_REQUEST(state->smtp_server_fd, psc_send_socket_close_event, (void *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; } }