int pass_trigger(const char *service, const char *buf, ssize_t len, int timeout) { const char *myname = "pass_trigger"; int pass_fd[2]; struct pass_trigger *pp; int connect_fd; if (msg_verbose > 1) msg_info("%s: service %s", myname, service); /* * Connect... */ if ((connect_fd = LOCAL_CONNECT(service, BLOCKING, timeout)) < 0) { if (msg_verbose) msg_warn("%s: connect to %s: %m", myname, service); return (-1); } close_on_exec(connect_fd, CLOSE_ON_EXEC); /* * Create a pipe, and send one pipe end to the server. */ if (pipe(pass_fd) < 0) msg_fatal("%s: pipe: %m", myname); close_on_exec(pass_fd[0], CLOSE_ON_EXEC); close_on_exec(pass_fd[1], CLOSE_ON_EXEC); if (LOCAL_SEND_FD(connect_fd, pass_fd[0]) < 0) msg_fatal("%s: send file descriptor: %m", myname); /* * Stash away context. */ pp = (struct pass_trigger *) mymalloc(sizeof(*pp)); pp->connect_fd = connect_fd; pp->service = mystrdup(service); pp->pass_fd[0] = pass_fd[0]; pp->pass_fd[1] = pass_fd[1]; /* * Write the request... */ if (write_buf(pass_fd[1], buf, len, timeout) < 0 || write_buf(pass_fd[1], "", 1, timeout) < 0) if (msg_verbose) msg_warn("%s: write to %s: %m", myname, service); /* * Wakeup when the peer disconnects, or when we lose patience. */ if (timeout > 0) event_request_timer(pass_trigger_event, (char *) pp, timeout + 100); event_enable_read(connect_fd, pass_trigger_event, (char *) pp); return (0); }
VSTREAM *tls_proxy_open(const char *service, int flags, VSTREAM *peer_stream, const char *peer_addr, const char *peer_port, int timeout) { VSTREAM *tlsproxy_stream; int status; int fd; static VSTRING *tlsproxy_service = 0; static VSTRING *remote_endpt = 0; /* * Initialize. */ if (tlsproxy_service == 0) { tlsproxy_service = vstring_alloc(20); remote_endpt = vstring_alloc(20); } /* * Connect to the tlsproxy(8) daemon. */ vstring_sprintf(tlsproxy_service, "%s/%s", MAIL_CLASS_PRIVATE, service); if ((fd = LOCAL_CONNECT(STR(tlsproxy_service), BLOCKING, TLSPROXY_INIT_TIMEOUT)) < 0) { msg_warn("connect to %s service: %m", STR(tlsproxy_service)); return (0); } /* * Initial handshake. Send the data attributes now, and send the client * file descriptor in a later transaction. * * XXX The formatted endpoint should be a state member. Then, we can * simplify all the format strings throughout the program. */ tlsproxy_stream = vstream_fdopen(fd, O_RDWR); vstring_sprintf(remote_endpt, "[%s]:%s", peer_addr, peer_port); attr_print(tlsproxy_stream, ATTR_FLAG_NONE, ATTR_TYPE_STR, MAIL_ATTR_REMOTE_ENDPT, STR(remote_endpt), ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_INT, MAIL_ATTR_TIMEOUT, timeout, ATTR_TYPE_END); if (vstream_fflush(tlsproxy_stream) != 0) { msg_warn("error sending request to %s service: %m", STR(tlsproxy_service)); vstream_fclose(tlsproxy_stream); return (0); } /* * Receive the "TLS is available" indication. * * This may seem out of order, but we must have a read transaction between * sending the request attributes and sending the SMTP client file * descriptor. We can't assume UNIX-domain socket semantics here. */ if (attr_scan(tlsproxy_stream, ATTR_FLAG_STRICT, ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status, ATTR_TYPE_END) != 1 || status == 0) { /* * The TLS proxy reports that the TLS engine is not available (due to * configuration error, or other causes). */ msg_warn("%s service role \"%s\" is not available", STR(tlsproxy_service), (flags & TLS_PROXY_FLAG_ROLE_SERVER) ? "server" : (flags & TLS_PROXY_FLAG_ROLE_CLIENT) ? "client" : "bogus role"); vstream_fclose(tlsproxy_stream); return (0); } /* * Send the remote SMTP client file descriptor. */ if (LOCAL_SEND_FD(vstream_fileno(tlsproxy_stream), vstream_fileno(peer_stream)) < 0) { /* * Some error: drop the TLS proxy stream. */ msg_warn("sending file handle to %s service: %m", STR(tlsproxy_service)); vstream_fclose(tlsproxy_stream); return (0); } return (tlsproxy_stream); }
void psc_send_socket(PSC_STATE *state) { const char *myname = "psc_send_socket"; int server_fd; 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 = PASS_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; } if (LOCAL_SEND_FD(server_fd, vstream_fileno(state->smtp_client_stream)) < 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, (char *) state, PSC_SEND_SOCK_NOTIFY_TIMEOUT); return; } }