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); }
int psc_dnsbl_request(const char *client_addr, void (*callback) (int, void *), void *context) { const char *myname = "psc_dnsbl_request"; int fd; VSTREAM *stream; HTABLE_INFO **ht; PSC_DNSBL_SCORE *score; HTABLE_INFO *hash_node; static int request_count; /* * Some spambots make several connections at nearly the same time, * causing their pregreet delays to overlap. Such connections can share * the efforts of DNSBL lookup. * * We store a reference-counted DNSBL score under its client IP address. We * increment the reference count with each score request, and decrement * the reference count with each score retrieval. * * Do not notify the requestor NOW when the DNS replies are already in. * Reason: we must not make a backwards call while we are still in the * middle of executing the corresponding forward call. Instead we create * a zero-delay timer request and call the notification function from * there. * * psc_dnsbl_request() could instead return a result value to indicate that * the DNSBL score is already available, but that would complicate the * caller with two different notification code paths: one asynchronous * code path via the callback invocation, and one synchronous code path * via the psc_dnsbl_request() result value. That would be a source of * future bugs. */ if ((hash_node = htable_locate(dnsbl_score_cache, client_addr)) != 0) { score = (PSC_DNSBL_SCORE *) hash_node->value; score->refcount += 1; PSC_CALL_BACK_EXTEND(hash_node, score); PSC_CALL_BACK_ENTER(score, callback, context); if (msg_verbose > 1) msg_info("%s: reuse blocklist score for %s refcount=%d pending=%d", myname, client_addr, score->refcount, score->pending_lookups); if (score->pending_lookups == 0) event_request_timer(callback, context, EVENT_NULL_DELAY); return (PSC_CALL_BACK_INDEX_OF_LAST(score)); } if (msg_verbose > 1) msg_info("%s: create blocklist score for %s", myname, client_addr); score = (PSC_DNSBL_SCORE *) mymalloc(sizeof(*score)); score->request_id = request_count++; score->dnsbl_name = 0; score->dnsbl_weight = 0; /* As with dnsblog(8), a value < 0 means no reply TTL. */ score->pass_ttl = -1; score->fail_ttl = -1; score->total = 0; score->refcount = 1; score->pending_lookups = 0; PSC_CALL_BACK_INIT(score); PSC_CALL_BACK_ENTER(score, callback, context); (void) htable_enter(dnsbl_score_cache, client_addr, (void *) score); /* * Send a query to all DNSBL servers. Later, DNSBL lookup will be done * with an UDP-based DNS client that is built directly into Postfix code. * We therefore do not optimize the maximum out of this temporary * implementation. */ for (ht = dnsbl_site_list; *ht; ht++) { if ((fd = LOCAL_CONNECT(psc_dnsbl_service, NON_BLOCKING, 1)) < 0) { msg_warn("%s: connect to %s service: %m", myname, psc_dnsbl_service); continue; } stream = vstream_fdopen(fd, O_RDWR); vstream_control(stream, CA_VSTREAM_CTL_CONTEXT(ht[0]->key), CA_VSTREAM_CTL_END); attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, ht[0]->key), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, client_addr), SEND_ATTR_INT(MAIL_ATTR_LABEL, score->request_id), ATTR_TYPE_END); if (vstream_fflush(stream) != 0) { msg_warn("%s: error sending to %s service: %m", myname, psc_dnsbl_service); vstream_fclose(stream); continue; } PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, (void *) stream, var_psc_dnsbl_tmout); score->pending_lookups += 1; } return (PSC_CALL_BACK_INDEX_OF_LAST(score)); }
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; } }