static void single_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; /* * If the accept() succeeds, be sure to disable non-blocking I/O, because * the application is supposed to be single-threaded. Notice the master * of our (un)availability to service connection requests. Commit suicide * when the master process disconnected from us. Don't drop the already * accepted client request after "postfix reload"; that would be rude. */ if (msg_verbose) msg_info("connection established"); non_blocking(fd, BLOCKING); close_on_exec(fd, CLOSE_ON_EXEC); stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(single_server_name, " socket", (char *) 0); vstream_control(stream, CA_VSTREAM_CTL_PATH(tmp), CA_VSTREAM_CTL_CONTEXT((void *) attr), CA_VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); if (master_notify(var_pid, single_server_generation, MASTER_STAT_TAKEN) < 0) /* void */ ; if (single_server_in_flow_delay && mail_flow_get(1) < 0) doze(var_in_flow_delay * 1000000); single_server_service(stream, single_server_name, single_server_argv); (void) vstream_fclose(stream); if (master_notify(var_pid, single_server_generation, MASTER_STAT_AVAIL) < 0) single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); if (msg_verbose) msg_info("connection closed"); /* Avoid integer wrap-around in a persistent process. */ if (use_count < INT_MAX) use_count++; if (var_idle_limit > 0) event_request_timer(single_server_timeout, (void *) 0, var_idle_limit); if (attr) htable_free(attr, myfree); }
static void multi_server_wakeup(int fd, HTABLE *attr) { VSTREAM *stream; char *tmp; #if defined(F_DUPFD) && (EVENTS_STYLE != EVENTS_STYLE_SELECT) #ifndef THRESHOLD_FD_WORKAROUND #define THRESHOLD_FD_WORKAROUND 128 #endif int new_fd; /* * Leave some handles < FD_SETSIZE for DBMS libraries, in the unlikely * case of a multi-server with a thousand clients. */ if (fd < THRESHOLD_FD_WORKAROUND) { if ((new_fd = fcntl(fd, F_DUPFD, THRESHOLD_FD_WORKAROUND)) < 0) msg_fatal("fcntl F_DUPFD: %m"); (void) close(fd); fd = new_fd; } #endif if (msg_verbose) msg_info("connection established fd %d", fd); non_blocking(fd, BLOCKING); close_on_exec(fd, CLOSE_ON_EXEC); client_count++; stream = vstream_fdopen(fd, O_RDWR); tmp = concatenate(multi_server_name, " socket", (char *) 0); vstream_control(stream, CA_VSTREAM_CTL_PATH(tmp), CA_VSTREAM_CTL_CONTEXT((void *) attr), CA_VSTREAM_CTL_END); myfree(tmp); timed_ipc_setup(stream); multi_server_saved_flags = vstream_flags(stream); if (multi_server_in_flow_delay && mail_flow_get(1) < 0) event_request_timer(multi_server_enable_read, (void *) stream, var_in_flow_delay); else multi_server_enable_read(0, (void *) 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)); }