static void multi_server_execute(int unused_event, char *context) { VSTREAM *stream = (VSTREAM *) context; HTABLE *attr = (vstream_flags(stream) == multi_server_saved_flags ? (HTABLE *) vstream_context(stream) : 0); if (multi_server_lock != 0 && myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) msg_fatal("select unlock: %m"); /* * Do not bother the application when the client disconnected. Don't drop * the already accepted client request after "postfix reload"; that would * be rude. */ if (peekfd(vstream_fileno(stream)) > 0) { if (master_notify(var_pid, multi_server_generation, MASTER_STAT_TAKEN) < 0) /* void */ ; multi_server_service(stream, multi_server_name, multi_server_argv); if (master_notify(var_pid, multi_server_generation, MASTER_STAT_AVAIL) < 0) multi_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT); } else { multi_server_disconnect(stream); } if (attr) htable_free(attr, myfree); }
static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv) { int status = -1; #ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT static time_t last; time_t now; const char *table; #endif /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Client connections are long-lived. Be sure to refesh timely. */ #ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT if (server_flags == 0 && (now = event_time()) - last > 10) { if ((table = dict_changed_name()) != 0) { msg_info("table %s has changed -- restarting", table); if (multi_server_drain() == 0) server_flags = 1; } last = now; } #endif /* * This routine runs whenever a client connects to the UNIX-domain socket * dedicated to address rewriting. All connection-management stuff is * handled by the common code in multi_server.c. */ if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, ATTR_TYPE_STR, MAIL_ATTR_REQ, command, ATTR_TYPE_END) == 1) { if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) { status = rewrite_proto(stream); } else if (strcmp(vstring_str(command), RESOLVE_REGULAR) == 0) { status = resolve_proto(&resolve_regular, stream); } else if (strcmp(vstring_str(command), RESOLVE_VERIFY) == 0) { status = resolve_proto(&resolve_verify, stream); } else { msg_warn("bad command %.30s", printable(vstring_str(command), '?')); } } if (status < 0) multi_server_disconnect(stream); }
static void anvil_service(VSTREAM *client_stream, char *unused_service, char **argv) { static VSTRING *request; static VSTRING *ident; static const ANVIL_REQ_TABLE request_table[] = { ANVIL_REQ_CONN, anvil_remote_connect, ANVIL_REQ_MAIL, anvil_remote_mail, ANVIL_REQ_RCPT, anvil_remote_rcpt, ANVIL_REQ_NTLS, anvil_remote_newtls, ANVIL_REQ_DISC, anvil_remote_disconnect, ANVIL_REQ_NTLS_STAT, anvil_remote_newtls_stat, ANVIL_REQ_LOOKUP, anvil_remote_lookup, 0, 0, }; const ANVIL_REQ_TABLE *rp; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Initialize. */ if (request == 0) { request = vstring_alloc(10); ident = vstring_alloc(10); } /* * This routine runs whenever a client connects to the socket dedicated * to the client connection rate management service. All * connection-management stuff is handled by the common code in * multi_server.c. */ if (msg_verbose) msg_info("--- start request ---"); if (attr_scan_plain(client_stream, ATTR_FLAG_MISSING | ATTR_FLAG_STRICT, ATTR_TYPE_STR, ANVIL_ATTR_REQ, request, ATTR_TYPE_STR, ANVIL_ATTR_IDENT, ident, ATTR_TYPE_END) == 2) { for (rp = request_table; /* see below */ ; rp++) { if (rp->name == 0) { msg_warn("unrecognized request: \"%s\", ignored", STR(request)); attr_print_plain(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, ANVIL_ATTR_STATUS, ANVIL_STAT_FAIL, ATTR_TYPE_END); break; } if (STREQ(rp->name, STR(request))) { rp->action(client_stream, STR(ident)); break; } } vstream_fflush(client_stream); } else { /* Note: invokes anvil_service_done() */ multi_server_disconnect(client_stream); } if (msg_verbose) msg_info("--- end request ---"); }
static void tlsmgr_service(VSTREAM *client_stream, char *unused_service, char **argv) { static VSTRING *request = 0; static VSTRING *cache_type = 0; static VSTRING *cache_id = 0; static VSTRING *buffer = 0; int len; static char wakeup[] = { /* master wakeup request */ TRIGGER_REQ_WAKEUP, 0, }; TLSMGR_SCACHE *ent; int status = TLS_MGR_STAT_FAIL; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * Initialize. We're select threaded, so we can use static buffers. */ if (request == 0) { request = vstring_alloc(10); cache_type = vstring_alloc(10); cache_id = vstring_alloc(10); buffer = vstring_alloc(10); } /* * This routine runs whenever a client connects to the socket dedicated * to the tlsmgr service (including wake up events sent by the master). * All connection-management stuff is handled by the common code in * multi_server.c. */ if (tlsmgr_request_receive(client_stream, request) == 0) { /* * Load session from cache. */ if (STREQ(STR(request), TLS_MGR_REQ_LOOKUP)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_END) == 2) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { msg_warn("bogus cache type \"%s\" in \"%s\" request", STR(cache_type), TLS_MGR_REQ_LOOKUP); VSTRING_RESET(buffer); } else if (ent->cache_info == 0) { /* * Cache type valid, but not enabled */ VSTRING_RESET(buffer); } else { status = tls_scache_lookup(ent->cache_info, STR(cache_id), buffer) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, LEN(buffer), STR(buffer), ATTR_TYPE_END); } /* * Save session to cache. */ else if (STREQ(STR(request), TLS_MGR_REQ_UPDATE)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer, ATTR_TYPE_END) == 3) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { msg_warn("bogus cache type \"%s\" in \"%s\" request", STR(cache_type), TLS_MGR_REQ_UPDATE); } else if (ent->cache_info != 0) { status = tls_scache_update(ent->cache_info, STR(cache_id), STR(buffer), LEN(buffer)) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_END); } /* * Delete session from cache. */ else if (STREQ(STR(request), TLS_MGR_REQ_DELETE)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id, ATTR_TYPE_END) == 2) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { msg_warn("bogus cache type \"%s\" in \"%s\" request", STR(cache_type), TLS_MGR_REQ_DELETE); } else if (ent->cache_info != 0) { status = tls_scache_delete(ent->cache_info, STR(cache_id)) ? TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_END); } /* * RFC 5077 TLS session ticket keys */ else if (STREQ(STR(request), TLS_MGR_REQ_TKTKEY)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYNAME, buffer, ATTR_TYPE_END) == 1) { if (LEN(buffer) != 0 && LEN(buffer) != TLS_TICKET_NAMELEN) { msg_warn("invalid session ticket key name length: %ld", (long) LEN(buffer)); VSTRING_RESET(buffer); } else if (*smtpd_cache.cache_timeout <= 0) { status = TLS_MGR_STAT_ERR; VSTRING_RESET(buffer); } else { status = tlsmgr_key(buffer, *smtpd_cache.cache_timeout); } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_DATA, TLS_MGR_ATTR_KEYBUF, LEN(buffer), STR(buffer), ATTR_TYPE_END); } /* * Entropy request. */ else if (STREQ(STR(request), TLS_MGR_REQ_SEED)) { if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_INT, TLS_MGR_ATTR_SIZE, &len, ATTR_TYPE_END) == 1) { VSTRING_RESET(buffer); if (len <= 0 || len > 255) { msg_warn("bogus seed length \"%d\" in \"%s\" request", len, TLS_MGR_REQ_SEED); } else { VSTRING_SPACE(buffer, len); RAND_bytes((unsigned char *) STR(buffer), len); VSTRING_AT_OFFSET(buffer, len); /* XXX not part of the * official interface */ status = TLS_MGR_STAT_OK; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_DATA, TLS_MGR_ATTR_SEED, LEN(buffer), STR(buffer), ATTR_TYPE_END); } /* * Caching policy request. */ else if (STREQ(STR(request), TLS_MGR_REQ_POLICY)) { int cachable = 0; int timeout = 0; if (attr_scan(client_stream, ATTR_FLAG_STRICT, ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_TYPE, cache_type, ATTR_TYPE_END) == 1) { for (ent = cache_table; ent->cache_label; ++ent) if (strcmp(ent->cache_label, STR(cache_type)) == 0) break; if (ent->cache_label == 0) { msg_warn("bogus cache type \"%s\" in \"%s\" request", STR(cache_type), TLS_MGR_REQ_POLICY); } else { cachable = (ent->cache_info != 0) ? 1 : 0; timeout = *ent->cache_timeout; status = TLS_MGR_STAT_OK; } } attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, ATTR_TYPE_INT, TLS_MGR_ATTR_CACHABLE, cachable, ATTR_TYPE_INT, TLS_MGR_ATTR_SESSTOUT, timeout, ATTR_TYPE_END); } /* * Master trigger. Normally, these triggers arrive only after some * other process requested the tlsmgr's service. The purpose is to * restart the tlsmgr after it aborted due to a fatal run-time error, * so that it can continue its housekeeping even while nothing is * using TLS. * * XXX Which begs the question, if TLS isn't used often, do we need a * tlsmgr background process? It could terminate when the session * caches are empty. */ else if (STREQ(STR(request), wakeup)) { if (msg_verbose) msg_info("received master trigger"); multi_server_disconnect(client_stream); return; /* NOT: vstream_fflush */ } } /* * Protocol error. */ else { attr_print(client_stream, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, TLS_MGR_STAT_FAIL, ATTR_TYPE_END); } vstream_fflush(client_stream); }