1*6dc81a07Seric /* $OpenBSD: mta.c,v 1.166 2013/10/27 17:47:53 eric Exp $ */ 21f3c2bcfSsobrado 33ef9cbf7Sgilles /* 43ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 565c4fdfbSgilles * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 6e5b07014Sgilles * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 7b04e6f27Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 83ef9cbf7Sgilles * 93ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any 103ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above 113ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies. 123ef9cbf7Sgilles * 133ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 143ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 153ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 163ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 173ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 183ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 193ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 203ef9cbf7Sgilles */ 213ef9cbf7Sgilles 223ef9cbf7Sgilles #include <sys/types.h> 233ef9cbf7Sgilles #include <sys/queue.h> 243ef9cbf7Sgilles #include <sys/tree.h> 253ef9cbf7Sgilles #include <sys/socket.h> 263ef9cbf7Sgilles 27b04e6f27Seric #include <ctype.h> 286d095224Schl #include <err.h> 293ef9cbf7Sgilles #include <errno.h> 303ef9cbf7Sgilles #include <event.h> 315eb8dddaSgilles #include <imsg.h> 32c50073caSchl #include <inttypes.h> 33eb143ecfSjacekm #include <netdb.h> 343ef9cbf7Sgilles #include <pwd.h> 359623339fSchl #include <signal.h> 363ef9cbf7Sgilles #include <stdio.h> 373ef9cbf7Sgilles #include <stdlib.h> 383ef9cbf7Sgilles #include <string.h> 39a44f0265Schl #include <time.h> 403ef9cbf7Sgilles #include <unistd.h> 413ef9cbf7Sgilles 423ef9cbf7Sgilles #include "smtpd.h" 435eb8dddaSgilles #include "log.h" 443ef9cbf7Sgilles 4565c4fdfbSgilles #define MAXERROR_PER_HOST 4 46ac5aa4b9Seric 47c5acbec8Seric #define DELAY_CHECK_SOURCE 1 48c5acbec8Seric #define DELAY_CHECK_SOURCE_SLOW 10 49c5acbec8Seric #define DELAY_CHECK_SOURCE_FAST 0 50c5acbec8Seric #define DELAY_CHECK_LIMIT 5 51ac5aa4b9Seric 52c5acbec8Seric #define DELAY_QUADRATIC 1 53c5acbec8Seric #define DELAY_ROUTE_BASE 200 54c5acbec8Seric #define DELAY_ROUTE_MAX (3600 * 4) 55ac5aa4b9Seric 56*6dc81a07Seric #define RELAY_ONHOLD 0x01 57*6dc81a07Seric 5865c4fdfbSgilles static void mta_imsg(struct mproc *, struct imsg *); 59be925435Sgilles static void mta_shutdown(void); 60be925435Sgilles static void mta_sig_handler(int, short, void *); 61ed1929b6Sjacekm 6265c4fdfbSgilles static void mta_query_mx(struct mta_relay *); 6365c4fdfbSgilles static void mta_query_secret(struct mta_relay *); 6465c4fdfbSgilles static void mta_query_preference(struct mta_relay *); 6565c4fdfbSgilles static void mta_query_source(struct mta_relay *); 6665c4fdfbSgilles static void mta_on_mx(void *, void *, void *); 67c5acbec8Seric static void mta_on_secret(struct mta_relay *, const char *); 68c5acbec8Seric static void mta_on_preference(struct mta_relay *, int, int); 6965c4fdfbSgilles static void mta_on_source(struct mta_relay *, struct mta_source *); 70c5acbec8Seric static void mta_on_timeout(struct runq *, void *); 7165c4fdfbSgilles static void mta_connect(struct mta_connector *); 72c5acbec8Seric static void mta_route_enable(struct mta_route *); 73c5acbec8Seric static void mta_route_disable(struct mta_route *, int, int); 7465c4fdfbSgilles static void mta_drain(struct mta_relay *); 7565c4fdfbSgilles static void mta_flush(struct mta_relay *, int, const char *); 76c5acbec8Seric static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*, 77c5acbec8Seric time_t*); 78399c053eSeric static void mta_log(const struct mta_envelope *, const char *, const char *, 79299c4efeSeric const char *, const char *); 80ac5aa4b9Seric 8165c4fdfbSgilles SPLAY_HEAD(mta_relay_tree, mta_relay); 8265c4fdfbSgilles static struct mta_relay *mta_relay(struct envelope *); 8365c4fdfbSgilles static void mta_relay_ref(struct mta_relay *); 8465c4fdfbSgilles static void mta_relay_unref(struct mta_relay *); 8565c4fdfbSgilles static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *); 8665c4fdfbSgilles SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp); 8765c4fdfbSgilles 8865c4fdfbSgilles SPLAY_HEAD(mta_host_tree, mta_host); 8965c4fdfbSgilles static struct mta_host *mta_host(const struct sockaddr *); 9065c4fdfbSgilles static void mta_host_ref(struct mta_host *); 9165c4fdfbSgilles static void mta_host_unref(struct mta_host *); 9265c4fdfbSgilles static int mta_host_cmp(const struct mta_host *, const struct mta_host *); 9365c4fdfbSgilles SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp); 9465c4fdfbSgilles 9565c4fdfbSgilles SPLAY_HEAD(mta_domain_tree, mta_domain); 9665c4fdfbSgilles static struct mta_domain *mta_domain(char *, int); 9765c4fdfbSgilles #if 0 9865c4fdfbSgilles static void mta_domain_ref(struct mta_domain *); 9965c4fdfbSgilles #endif 10065c4fdfbSgilles static void mta_domain_unref(struct mta_domain *); 10165c4fdfbSgilles static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *); 10265c4fdfbSgilles SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp); 10365c4fdfbSgilles 10465c4fdfbSgilles SPLAY_HEAD(mta_source_tree, mta_source); 10565c4fdfbSgilles static struct mta_source *mta_source(const struct sockaddr *); 10665c4fdfbSgilles static void mta_source_ref(struct mta_source *); 10765c4fdfbSgilles static void mta_source_unref(struct mta_source *); 10865c4fdfbSgilles static const char *mta_source_to_text(struct mta_source *); 10965c4fdfbSgilles static int mta_source_cmp(const struct mta_source *, const struct mta_source *); 11065c4fdfbSgilles SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp); 11165c4fdfbSgilles 11265c4fdfbSgilles static struct mta_connector *mta_connector(struct mta_relay *, 11365c4fdfbSgilles struct mta_source *); 11465c4fdfbSgilles static void mta_connector_free(struct mta_connector *); 11565c4fdfbSgilles static const char *mta_connector_to_text(struct mta_connector *); 11665c4fdfbSgilles 11765c4fdfbSgilles SPLAY_HEAD(mta_route_tree, mta_route); 11865c4fdfbSgilles static struct mta_route *mta_route(struct mta_source *, struct mta_host *); 11965c4fdfbSgilles static void mta_route_ref(struct mta_route *); 12065c4fdfbSgilles static void mta_route_unref(struct mta_route *); 12165c4fdfbSgilles static const char *mta_route_to_text(struct mta_route *); 12265c4fdfbSgilles static int mta_route_cmp(const struct mta_route *, const struct mta_route *); 123ac5aa4b9Seric SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp); 124ac5aa4b9Seric 12565c4fdfbSgilles static struct mta_relay_tree relays; 12665c4fdfbSgilles static struct mta_domain_tree domains; 12765c4fdfbSgilles static struct mta_host_tree hosts; 12865c4fdfbSgilles static struct mta_source_tree sources; 12965c4fdfbSgilles static struct mta_route_tree routes; 13065c4fdfbSgilles 13165c4fdfbSgilles static struct tree wait_mx; 13265c4fdfbSgilles static struct tree wait_preference; 13365c4fdfbSgilles static struct tree wait_secret; 13465c4fdfbSgilles static struct tree wait_source; 135ac5aa4b9Seric 136c5acbec8Seric static struct runq *runq_relay; 137c5acbec8Seric static struct runq *runq_connector; 138c5acbec8Seric static struct runq *runq_route; 139c5acbec8Seric static struct runq *runq_hoststat; 140c5acbec8Seric 141c5acbec8Seric static time_t max_seen_conndelay_route; 142c5acbec8Seric static time_t max_seen_discdelay_route; 143c5acbec8Seric 144c5acbec8Seric #define HOSTSTAT_EXPIRE_DELAY (4 * 3600) 145c5acbec8Seric struct hoststat { 146c5acbec8Seric char name[SMTPD_MAXHOSTNAMELEN]; 147c5acbec8Seric time_t tm; 148c5acbec8Seric char error[SMTPD_MAXLINESIZE]; 149c5acbec8Seric struct tree deferred; 150c5acbec8Seric }; 151c5acbec8Seric static struct dict hoststat; 152c5acbec8Seric 153c5acbec8Seric void mta_hoststat_update(const char *, const char *); 154c5acbec8Seric void mta_hoststat_cache(const char *, uint64_t); 155c5acbec8Seric void mta_hoststat_uncache(const char *, uint64_t); 156c5acbec8Seric void mta_hoststat_reschedule(const char *); 157c5acbec8Seric static void mta_hoststat_remove_entry(struct hoststat *); 158c5acbec8Seric 159c5acbec8Seric 160ac5aa4b9Seric void 16165c4fdfbSgilles mta_imsg(struct mproc *p, struct imsg *imsg) 1623ef9cbf7Sgilles { 16365c4fdfbSgilles struct mta_relay *relay; 164ac5aa4b9Seric struct mta_task *task; 16565c4fdfbSgilles struct mta_domain *domain; 166c5acbec8Seric struct mta_route *route; 16765c4fdfbSgilles struct mta_mx *mx, *imx; 168c5acbec8Seric struct hoststat *hs; 169399c053eSeric struct mta_envelope *e; 17065c4fdfbSgilles struct sockaddr_storage ss; 171399c053eSeric struct envelope evp; 17265c4fdfbSgilles struct msg m; 17365c4fdfbSgilles const char *secret; 174c5acbec8Seric const char *hostname; 17565c4fdfbSgilles uint64_t reqid; 176c5acbec8Seric time_t t; 177964ab7edSeric char buf[SMTPD_MAXLINESIZE]; 17865c4fdfbSgilles int dnserror, preference, v, status; 179c5acbec8Seric void *iter; 180c5acbec8Seric uint64_t u64; 181eb143ecfSjacekm 18265c4fdfbSgilles if (p->proc == PROC_QUEUE) { 183ed1929b6Sjacekm switch (imsg->hdr.type) { 184ac5aa4b9Seric 185c5acbec8Seric case IMSG_MTA_TRANSFER: 18665c4fdfbSgilles m_msg(&m, imsg); 187399c053eSeric m_get_envelope(&m, &evp); 18865c4fdfbSgilles m_end(&m); 189ac5aa4b9Seric 190399c053eSeric relay = mta_relay(&evp); 191*6dc81a07Seric /* ignore if we don't know the limits yet */ 192*6dc81a07Seric if (relay->limits && 193*6dc81a07Seric relay->ntask >= (size_t)relay->limits->task_hiwat) { 194*6dc81a07Seric if (!(relay->state & RELAY_ONHOLD)) { 195*6dc81a07Seric log_info("smtp-out: hiwat reached on %s: holding envelopes", 196*6dc81a07Seric mta_relay_to_text(relay)); 197*6dc81a07Seric relay->state |= RELAY_ONHOLD; 198*6dc81a07Seric } 199*6dc81a07Seric } 200*6dc81a07Seric 201*6dc81a07Seric /* 202*6dc81a07Seric * If the relay has too many pending tasks, tell the 203*6dc81a07Seric * scheduler to hold it until further notice 204*6dc81a07Seric */ 205*6dc81a07Seric if (relay->state & RELAY_ONHOLD) { 206*6dc81a07Seric m_create(p_queue, IMSG_DELIVERY_HOLD, 0, 0, -1); 207*6dc81a07Seric m_add_evpid(p_queue, evp.id); 208*6dc81a07Seric m_add_id(p_queue, relay->id); 209*6dc81a07Seric m_close(p_queue); 210*6dc81a07Seric mta_relay_unref(relay); 211*6dc81a07Seric return; 212*6dc81a07Seric } 21365c4fdfbSgilles 214c5acbec8Seric TAILQ_FOREACH(task, &relay->tasks, entry) 215c5acbec8Seric if (task->msgid == evpid_to_msgid(evp.id)) 216c5acbec8Seric break; 217c5acbec8Seric 218c5acbec8Seric if (task == NULL) { 219ac5aa4b9Seric task = xmalloc(sizeof *task, "mta_task"); 220ac5aa4b9Seric TAILQ_INIT(&task->envelopes); 22165c4fdfbSgilles task->relay = relay; 222c5acbec8Seric relay->ntask += 1; 223c5acbec8Seric TAILQ_INSERT_TAIL(&relay->tasks, task, entry); 224399c053eSeric task->msgid = evpid_to_msgid(evp.id); 225399c053eSeric if (evp.sender.user[0] || evp.sender.domain[0]) 226399c053eSeric snprintf(buf, sizeof buf, "%s@%s", 227399c053eSeric evp.sender.user, evp.sender.domain); 228399c053eSeric else 229399c053eSeric buf[0] = '\0'; 230399c053eSeric task->sender = xstrdup(buf, "mta_task:sender"); 231c5acbec8Seric stat_increment("mta.task", 1); 232c5acbec8Seric } 233ac5aa4b9Seric 234399c053eSeric e = xcalloc(1, sizeof *e, "mta_envelope"); 235399c053eSeric e->id = evp.id; 236399c053eSeric e->creation = evp.creation; 237399c053eSeric snprintf(buf, sizeof buf, "%s@%s", 238399c053eSeric evp.dest.user, evp.dest.domain); 239399c053eSeric e->dest = xstrdup(buf, "mta_envelope:dest"); 240399c053eSeric snprintf(buf, sizeof buf, "%s@%s", 241399c053eSeric evp.rcpt.user, evp.rcpt.domain); 242399c053eSeric if (strcmp(buf, e->dest)) 243399c053eSeric e->rcpt = xstrdup(buf, "mta_envelope:rcpt"); 244399c053eSeric e->task = task; 245c5acbec8Seric 246ac5aa4b9Seric TAILQ_INSERT_TAIL(&task->envelopes, e, entry); 24765c4fdfbSgilles log_debug("debug: mta: received evp:%016" PRIx64 248399c053eSeric " for <%s>", e->id, e->dest); 249ac5aa4b9Seric 250c5acbec8Seric stat_increment("mta.envelope", 1); 251c5acbec8Seric 25265c4fdfbSgilles mta_drain(relay); 253c5acbec8Seric mta_relay_unref(relay); /* from here */ 254ac5aa4b9Seric return; 255ac5aa4b9Seric 256e5b07014Sgilles case IMSG_QUEUE_MESSAGE_FD: 25765c4fdfbSgilles mta_session_imsg(p, imsg); 258b28a97afSjacekm return; 259df982ec9Sgilles } 2601eb29730Sjacekm } 2611eb29730Sjacekm 26265c4fdfbSgilles if (p->proc == PROC_LKA) { 263ed1929b6Sjacekm switch (imsg->hdr.type) { 26465c4fdfbSgilles 265ed1929b6Sjacekm case IMSG_LKA_SECRET: 26665c4fdfbSgilles m_msg(&m, imsg); 26765c4fdfbSgilles m_get_id(&m, &reqid); 26865c4fdfbSgilles m_get_string(&m, &secret); 26965c4fdfbSgilles m_end(&m); 27065c4fdfbSgilles relay = tree_xpop(&wait_secret, reqid); 271c5acbec8Seric mta_on_secret(relay, secret[0] ? secret : NULL); 27265c4fdfbSgilles return; 27365c4fdfbSgilles 27465c4fdfbSgilles case IMSG_LKA_SOURCE: 27565c4fdfbSgilles m_msg(&m, imsg); 27665c4fdfbSgilles m_get_id(&m, &reqid); 27765c4fdfbSgilles m_get_int(&m, &status); 278c5acbec8Seric if (status == LKA_OK) 27965c4fdfbSgilles m_get_sockaddr(&m, (struct sockaddr*)&ss); 28065c4fdfbSgilles m_end(&m); 28165c4fdfbSgilles 282c5acbec8Seric relay = tree_xpop(&wait_source, reqid); 283c5acbec8Seric mta_on_source(relay, (status == LKA_OK) ? 284c5acbec8Seric mta_source((struct sockaddr *)&ss) : NULL); 28565c4fdfbSgilles return; 28665c4fdfbSgilles 28765c4fdfbSgilles case IMSG_LKA_HELO: 28865c4fdfbSgilles mta_session_imsg(p, imsg); 28965c4fdfbSgilles return; 29065c4fdfbSgilles 29125080696Sgilles case IMSG_DNS_HOST: 29265c4fdfbSgilles m_msg(&m, imsg); 29365c4fdfbSgilles m_get_id(&m, &reqid); 29465c4fdfbSgilles m_get_sockaddr(&m, (struct sockaddr*)&ss); 29565c4fdfbSgilles m_get_int(&m, &preference); 29665c4fdfbSgilles m_end(&m); 29765c4fdfbSgilles domain = tree_xget(&wait_mx, reqid); 29865c4fdfbSgilles mx = xcalloc(1, sizeof *mx, "mta: mx"); 29965c4fdfbSgilles mx->host = mta_host((struct sockaddr*)&ss); 30065c4fdfbSgilles mx->preference = preference; 30165c4fdfbSgilles TAILQ_FOREACH(imx, &domain->mxs, entry) { 302e06daefdSeric if (imx->preference > mx->preference) { 30365c4fdfbSgilles TAILQ_INSERT_BEFORE(imx, mx, entry); 30465c4fdfbSgilles return; 30565c4fdfbSgilles } 30665c4fdfbSgilles } 30765c4fdfbSgilles TAILQ_INSERT_TAIL(&domain->mxs, mx, entry); 30865c4fdfbSgilles return; 30965c4fdfbSgilles 31025080696Sgilles case IMSG_DNS_HOST_END: 31165c4fdfbSgilles m_msg(&m, imsg); 31265c4fdfbSgilles m_get_id(&m, &reqid); 31365c4fdfbSgilles m_get_int(&m, &dnserror); 31465c4fdfbSgilles m_end(&m); 31565c4fdfbSgilles domain = tree_xpop(&wait_mx, reqid); 31665c4fdfbSgilles domain->mxstatus = dnserror; 31765c4fdfbSgilles if (domain->mxstatus == DNS_OK) { 31865c4fdfbSgilles log_debug("debug: MXs for domain %s:", 31965c4fdfbSgilles domain->name); 32065c4fdfbSgilles TAILQ_FOREACH(mx, &domain->mxs, entry) 321d7bcae4dSeric log_debug(" %s preference %d", 32265c4fdfbSgilles sa_to_text(mx->host->sa), 32365c4fdfbSgilles mx->preference); 32465c4fdfbSgilles } 32565c4fdfbSgilles else { 32665c4fdfbSgilles log_debug("debug: Failed MX query for %s:", 32765c4fdfbSgilles domain->name); 32865c4fdfbSgilles } 32965c4fdfbSgilles waitq_run(&domain->mxs, domain); 33065c4fdfbSgilles return; 33165c4fdfbSgilles 33265c4fdfbSgilles case IMSG_DNS_MX_PREFERENCE: 33365c4fdfbSgilles m_msg(&m, imsg); 33465c4fdfbSgilles m_get_id(&m, &reqid); 33565c4fdfbSgilles m_get_int(&m, &dnserror); 336c5acbec8Seric if (dnserror == 0) 337c5acbec8Seric m_get_int(&m, &preference); 33865c4fdfbSgilles m_end(&m); 339c5acbec8Seric 340c5acbec8Seric relay = tree_xpop(&wait_preference, reqid); 341c5acbec8Seric mta_on_preference(relay, dnserror, preference); 34265c4fdfbSgilles return; 34365c4fdfbSgilles 344ed1929b6Sjacekm case IMSG_DNS_PTR: 34565c4fdfbSgilles mta_session_imsg(p, imsg); 34665c4fdfbSgilles return; 34765c4fdfbSgilles 34865c4fdfbSgilles case IMSG_LKA_SSL_INIT: 34965c4fdfbSgilles mta_session_imsg(p, imsg); 35065c4fdfbSgilles return; 35165c4fdfbSgilles 35265c4fdfbSgilles case IMSG_LKA_SSL_VERIFY: 35365c4fdfbSgilles mta_session_imsg(p, imsg); 3543ef9cbf7Sgilles return; 3553ef9cbf7Sgilles } 3561eb29730Sjacekm } 3571eb29730Sjacekm 35865c4fdfbSgilles if (p->proc == PROC_PARENT) { 359ed1929b6Sjacekm switch (imsg->hdr.type) { 360ed1929b6Sjacekm case IMSG_CTL_VERBOSE: 36165c4fdfbSgilles m_msg(&m, imsg); 36265c4fdfbSgilles m_get_int(&m, &v); 36365c4fdfbSgilles m_end(&m); 36465c4fdfbSgilles log_verbose(v); 36565c4fdfbSgilles return; 36665c4fdfbSgilles 36765c4fdfbSgilles case IMSG_CTL_PROFILE: 36865c4fdfbSgilles m_msg(&m, imsg); 36965c4fdfbSgilles m_get_int(&m, &v); 37065c4fdfbSgilles m_end(&m); 37165c4fdfbSgilles profiling = v; 372ed1929b6Sjacekm return; 373ed1929b6Sjacekm } 374af8de4b6Sgilles } 375eb143ecfSjacekm 376c5acbec8Seric if (p->proc == PROC_CONTROL) { 377c5acbec8Seric switch (imsg->hdr.type) { 378c5acbec8Seric 379c5acbec8Seric case IMSG_CTL_RESUME_ROUTE: 380c5acbec8Seric u64 = *((uint64_t *)imsg->data); 381c5acbec8Seric if (u64) 382c5acbec8Seric log_debug("resuming route: %llu", 383c5acbec8Seric (unsigned long long)u64); 384c5acbec8Seric else 385c5acbec8Seric log_debug("resuming all routes"); 386c5acbec8Seric SPLAY_FOREACH(route, mta_route_tree, &routes) { 387c5acbec8Seric if (u64 && route->id != u64) 388c5acbec8Seric continue; 389c5acbec8Seric mta_route_enable(route); 390c5acbec8Seric if (u64) 391c5acbec8Seric break; 392c5acbec8Seric } 393c5acbec8Seric return; 394c5acbec8Seric 395c5acbec8Seric case IMSG_CTL_MTA_SHOW_ROUTES: 396c5acbec8Seric SPLAY_FOREACH(route, mta_route_tree, &routes) { 397c5acbec8Seric v = runq_pending(runq_route, NULL, route, &t); 398c5acbec8Seric snprintf(buf, sizeof(buf), 399d7bcae4dSeric "%llu. %s %c%c%c%c nconn=%zu penalty=%d timeout=%s", 400c5acbec8Seric (unsigned long long)route->id, 401c5acbec8Seric mta_route_to_text(route), 402c5acbec8Seric route->flags & ROUTE_NEW ? 'N' : '-', 403c5acbec8Seric route->flags & ROUTE_DISABLED ? 'D' : '-', 404c5acbec8Seric route->flags & ROUTE_RUNQ ? 'Q' : '-', 405c5acbec8Seric route->flags & ROUTE_KEEPALIVE ? 'K' : '-', 406c5acbec8Seric route->nconn, 407c5acbec8Seric route->penalty, 408c5acbec8Seric v ? duration_to_text(t - time(NULL)) : "-"); 409c5acbec8Seric m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, 410c5acbec8Seric imsg->hdr.peerid, 0, -1, 411c5acbec8Seric buf, strlen(buf) + 1); 412c5acbec8Seric } 413c5acbec8Seric m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid, 414c5acbec8Seric 0, -1, NULL, 0); 415c5acbec8Seric return; 416c5acbec8Seric case IMSG_CTL_MTA_SHOW_HOSTSTATS: 417c5acbec8Seric iter = NULL; 418c5acbec8Seric while (dict_iter(&hoststat, &iter, &hostname, 419c5acbec8Seric (void **)&hs)) { 420c5acbec8Seric snprintf(buf, sizeof(buf), 421c5acbec8Seric "%s|%llu|%s", 422c5acbec8Seric hostname, (unsigned long long) hs->tm, 423c5acbec8Seric hs->error); 424c5acbec8Seric m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, 425c5acbec8Seric imsg->hdr.peerid, 0, -1, 426c5acbec8Seric buf, strlen(buf) + 1); 427c5acbec8Seric } 428c5acbec8Seric m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, 429c5acbec8Seric imsg->hdr.peerid, 430c5acbec8Seric 0, -1, NULL, 0); 431c5acbec8Seric return; 432c5acbec8Seric } 433c5acbec8Seric } 434c5acbec8Seric 4356d095224Schl errx(1, "mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); 436ed1929b6Sjacekm } 437ed1929b6Sjacekm 438be925435Sgilles static void 439ed1929b6Sjacekm mta_sig_handler(int sig, short event, void *p) 440ed1929b6Sjacekm { 441ed1929b6Sjacekm switch (sig) { 442ed1929b6Sjacekm case SIGINT: 443ed1929b6Sjacekm case SIGTERM: 444ed1929b6Sjacekm mta_shutdown(); 445ed1929b6Sjacekm break; 446af8de4b6Sgilles default: 447ed1929b6Sjacekm fatalx("mta_sig_handler: unexpected signal"); 448af8de4b6Sgilles } 449af8de4b6Sgilles } 450af8de4b6Sgilles 451be925435Sgilles static void 4523ef9cbf7Sgilles mta_shutdown(void) 4533ef9cbf7Sgilles { 45482614934Seric log_info("info: mail transfer agent exiting"); 4553ef9cbf7Sgilles _exit(0); 4563ef9cbf7Sgilles } 4573ef9cbf7Sgilles 4583ef9cbf7Sgilles pid_t 459e4d36f12Seric mta(void) 4603ef9cbf7Sgilles { 4613ef9cbf7Sgilles pid_t pid; 4623ef9cbf7Sgilles struct passwd *pw; 4633ef9cbf7Sgilles struct event ev_sigint; 4643ef9cbf7Sgilles struct event ev_sigterm; 4653ef9cbf7Sgilles 4663ef9cbf7Sgilles switch (pid = fork()) { 4673ef9cbf7Sgilles case -1: 4683ef9cbf7Sgilles fatal("mta: cannot fork"); 4693ef9cbf7Sgilles case 0: 4705894db6eSeric post_fork(PROC_MTA); 4713ef9cbf7Sgilles break; 4723ef9cbf7Sgilles default: 4733ef9cbf7Sgilles return (pid); 4743ef9cbf7Sgilles } 4753ef9cbf7Sgilles 476e4d36f12Seric purge_config(PURGE_EVERYTHING); 4773ef9cbf7Sgilles 47811d04e02Seric if ((pw = getpwnam(SMTPD_USER)) == NULL) 47911d04e02Seric fatalx("unknown user " SMTPD_USER); 48011d04e02Seric 48111d04e02Seric if (chroot(PATH_CHROOT) == -1) 4823ef9cbf7Sgilles fatal("mta: chroot"); 4833ef9cbf7Sgilles if (chdir("/") == -1) 4843ef9cbf7Sgilles fatal("mta: chdir(\"/\")"); 4853ef9cbf7Sgilles 48665c4fdfbSgilles config_process(PROC_MTA); 4873ef9cbf7Sgilles 4883ef9cbf7Sgilles if (setgroups(1, &pw->pw_gid) || 4893ef9cbf7Sgilles setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 4903ef9cbf7Sgilles setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 4913ef9cbf7Sgilles fatal("mta: cannot drop privileges"); 4923ef9cbf7Sgilles 49365c4fdfbSgilles SPLAY_INIT(&relays); 49465c4fdfbSgilles SPLAY_INIT(&domains); 49565c4fdfbSgilles SPLAY_INIT(&hosts); 49665c4fdfbSgilles SPLAY_INIT(&sources); 49765c4fdfbSgilles SPLAY_INIT(&routes); 49865c4fdfbSgilles 49965c4fdfbSgilles tree_init(&wait_secret); 50065c4fdfbSgilles tree_init(&wait_mx); 50165c4fdfbSgilles tree_init(&wait_preference); 50265c4fdfbSgilles tree_init(&wait_source); 503c5acbec8Seric dict_init(&hoststat); 50465c4fdfbSgilles 505ed1929b6Sjacekm imsg_callback = mta_imsg; 5063ef9cbf7Sgilles event_init(); 5073ef9cbf7Sgilles 508c5acbec8Seric runq_init(&runq_relay, mta_on_timeout); 509c5acbec8Seric runq_init(&runq_connector, mta_on_timeout); 510c5acbec8Seric runq_init(&runq_route, mta_on_timeout); 511c5acbec8Seric runq_init(&runq_hoststat, mta_on_timeout); 512c5acbec8Seric 513e4d36f12Seric signal_set(&ev_sigint, SIGINT, mta_sig_handler, NULL); 514e4d36f12Seric signal_set(&ev_sigterm, SIGTERM, mta_sig_handler, NULL); 5153ef9cbf7Sgilles signal_add(&ev_sigint, NULL); 5163ef9cbf7Sgilles signal_add(&ev_sigterm, NULL); 5173ef9cbf7Sgilles signal(SIGPIPE, SIG_IGN); 5183ef9cbf7Sgilles signal(SIGHUP, SIG_IGN); 5193ef9cbf7Sgilles 52065c4fdfbSgilles config_peer(PROC_PARENT); 52165c4fdfbSgilles config_peer(PROC_QUEUE); 52265c4fdfbSgilles config_peer(PROC_LKA); 52365c4fdfbSgilles config_peer(PROC_CONTROL); 52465c4fdfbSgilles config_done(); 5253ef9cbf7Sgilles 5261c09daa9Schl if (event_dispatch() < 0) 5271c09daa9Schl fatal("event_dispatch"); 5283ef9cbf7Sgilles mta_shutdown(); 5293ef9cbf7Sgilles 5303ef9cbf7Sgilles return (0); 5313ef9cbf7Sgilles } 532ac5aa4b9Seric 53365c4fdfbSgilles /* 53465c4fdfbSgilles * Local error on the given source. 53565c4fdfbSgilles */ 53665c4fdfbSgilles void 53765c4fdfbSgilles mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e) 538ac5aa4b9Seric { 53965c4fdfbSgilles struct mta_connector *c; 540c5acbec8Seric 54165c4fdfbSgilles /* 54265c4fdfbSgilles * Remember the source as broken for this connector. 54365c4fdfbSgilles */ 54465c4fdfbSgilles c = mta_connector(relay, route->src); 545c5acbec8Seric if (!(c->flags & CONNECTOR_ERROR_SOURCE)) 546c5acbec8Seric log_info("smtp-out: Error on %s: %s", 547c5acbec8Seric mta_route_to_text(route), e); 548c5acbec8Seric c->flags |= CONNECTOR_ERROR_SOURCE; 549ac5aa4b9Seric } 550ac5aa4b9Seric 55165c4fdfbSgilles /* 55265c4fdfbSgilles * TODO: 55365c4fdfbSgilles * Currently all errors are reported on the host itself. Technically, 55465c4fdfbSgilles * it should depend on the error, and it would be probably better to report 55565c4fdfbSgilles * it at the connector level. But we would need to have persistent routes 55665c4fdfbSgilles * for that. Hosts are "naturally" persisted, as they are referenced from 55765c4fdfbSgilles * the MX list on the domain. 55865c4fdfbSgilles * Also, we need a timeout on that. 55965c4fdfbSgilles */ 56065c4fdfbSgilles void 5610cf935dfSgilles mta_route_error(struct mta_relay *relay, struct mta_route *route) 562ac5aa4b9Seric { 56365c4fdfbSgilles route->dst->nerror++; 564ac5aa4b9Seric 56565c4fdfbSgilles if (route->dst->flags & HOST_IGNORE) 56665c4fdfbSgilles return; 56782614934Seric 56865c4fdfbSgilles if (route->dst->nerror > MAXERROR_PER_HOST) { 56965c4fdfbSgilles log_info("smtp-out: Too many errors on host %s: ignoring this MX", 57065c4fdfbSgilles mta_host_to_text(route->dst)); 57165c4fdfbSgilles route->dst->flags |= HOST_IGNORE; 57265c4fdfbSgilles } 573ac5aa4b9Seric } 574ac5aa4b9Seric 575ac5aa4b9Seric void 57665c4fdfbSgilles mta_route_ok(struct mta_relay *relay, struct mta_route *route) 577ac5aa4b9Seric { 57865c4fdfbSgilles struct mta_connector *c; 57965c4fdfbSgilles 580c5acbec8Seric if (!(route->flags & ROUTE_NEW)) 581c5acbec8Seric return; 58265c4fdfbSgilles 583c5acbec8Seric log_debug("debug: mta-routing: route %s is now valid.", 584c5acbec8Seric mta_route_to_text(route)); 585c5acbec8Seric 586c5acbec8Seric route->flags &= ~ROUTE_NEW; 58765c4fdfbSgilles 58865c4fdfbSgilles c = mta_connector(relay, route->src); 589c5acbec8Seric mta_connect(c); 590c5acbec8Seric } 591c5acbec8Seric 592c5acbec8Seric void 593c5acbec8Seric mta_route_down(struct mta_relay *relay, struct mta_route *route) 594c5acbec8Seric { 595c5acbec8Seric mta_route_disable(route, 2, ROUTE_DISABLED_SMTP); 596ac5aa4b9Seric } 597ac5aa4b9Seric 598ac5aa4b9Seric void 59965c4fdfbSgilles mta_route_collect(struct mta_relay *relay, struct mta_route *route) 600ac5aa4b9Seric { 60165c4fdfbSgilles struct mta_connector *c; 60265c4fdfbSgilles 603c5acbec8Seric log_debug("debug: mta_route_collect(%s)", 604c5acbec8Seric mta_route_to_text(route)); 60565c4fdfbSgilles 60665c4fdfbSgilles relay->nconn -= 1; 607c5acbec8Seric relay->domain->nconn -= 1; 60865c4fdfbSgilles route->nconn -= 1; 60965c4fdfbSgilles route->src->nconn -= 1; 61065c4fdfbSgilles route->dst->nconn -= 1; 611c5acbec8Seric route->lastdisc = time(NULL); 612c5acbec8Seric 613c5acbec8Seric /* First connection failed */ 614c5acbec8Seric if (route->flags & ROUTE_NEW) 615c5acbec8Seric mta_route_disable(route, 2, ROUTE_DISABLED_NET); 61665c4fdfbSgilles 61765c4fdfbSgilles c = mta_connector(relay, route->src); 61865c4fdfbSgilles c->nconn -= 1; 619c5acbec8Seric mta_connect(c); 620c5acbec8Seric mta_route_unref(route); /* from mta_find_route() */ 62165c4fdfbSgilles mta_relay_unref(relay); /* from mta_connect() */ 62265c4fdfbSgilles } 62365c4fdfbSgilles 62465c4fdfbSgilles struct mta_task * 62565c4fdfbSgilles mta_route_next_task(struct mta_relay *relay, struct mta_route *route) 62665c4fdfbSgilles { 62765c4fdfbSgilles struct mta_task *task; 62865c4fdfbSgilles 62965c4fdfbSgilles if ((task = TAILQ_FIRST(&relay->tasks))) { 63065c4fdfbSgilles TAILQ_REMOVE(&relay->tasks, task, entry); 63165c4fdfbSgilles relay->ntask -= 1; 63265c4fdfbSgilles task->relay = NULL; 633*6dc81a07Seric 634*6dc81a07Seric /* When the number of tasks is down to lowat, query some evp */ 635*6dc81a07Seric if (relay->ntask == (size_t)relay->limits->task_lowat) { 636*6dc81a07Seric if (relay->state & RELAY_ONHOLD) { 637*6dc81a07Seric log_info("smtp-out: back to lowat on %s: releasing", 638*6dc81a07Seric mta_relay_to_text(relay)); 639*6dc81a07Seric relay->state &= ~RELAY_ONHOLD; 640*6dc81a07Seric } 641*6dc81a07Seric m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1); 642*6dc81a07Seric m_add_id(p_queue, relay->id); 643*6dc81a07Seric m_add_int(p_queue, relay->limits->task_release); 644*6dc81a07Seric m_close(p_queue); 645*6dc81a07Seric } 646*6dc81a07Seric else if (relay->ntask == 0) { 647*6dc81a07Seric m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1); 648*6dc81a07Seric m_add_id(p_queue, relay->id); 649*6dc81a07Seric m_add_int(p_queue, 0); 650*6dc81a07Seric m_close(p_queue); 651*6dc81a07Seric } 65265c4fdfbSgilles } 65365c4fdfbSgilles 65465c4fdfbSgilles return (task); 655ac5aa4b9Seric } 656ac5aa4b9Seric 657ac5aa4b9Seric void 658c5acbec8Seric mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay, 659299c4efeSeric int delivery, const char *status) 660ac5aa4b9Seric { 66165c4fdfbSgilles if (delivery == IMSG_DELIVERY_OK) { 662299c4efeSeric mta_log(e, "Ok", source, relay, status); 66365c4fdfbSgilles } 66465c4fdfbSgilles else if (delivery == IMSG_DELIVERY_TEMPFAIL) { 665299c4efeSeric mta_log(e, "TempFail", source, relay, status); 66665c4fdfbSgilles } 66765c4fdfbSgilles else if (delivery == IMSG_DELIVERY_PERMFAIL) { 668299c4efeSeric mta_log(e, "PermFail", source, relay, status); 66965c4fdfbSgilles } 67065c4fdfbSgilles else if (delivery == IMSG_DELIVERY_LOOP) { 671299c4efeSeric mta_log(e, "PermFail", source, relay, "Loop detected"); 672c5acbec8Seric } 673c5acbec8Seric else 674c5acbec8Seric errx(1, "bad delivery"); 675c5acbec8Seric } 676c5acbec8Seric 677c5acbec8Seric void 678c5acbec8Seric mta_delivery_notify(struct mta_envelope *e, int delivery, const char *status, 679c5acbec8Seric uint32_t penalty) 680c5acbec8Seric { 681c5acbec8Seric if (delivery == IMSG_DELIVERY_OK) { 682c5acbec8Seric queue_ok(e->id); 683c5acbec8Seric } 684c5acbec8Seric else if (delivery == IMSG_DELIVERY_TEMPFAIL) { 685c5acbec8Seric queue_tempfail(e->id, penalty, status); 686c5acbec8Seric } 687c5acbec8Seric else if (delivery == IMSG_DELIVERY_PERMFAIL) { 688c5acbec8Seric queue_permfail(e->id, status); 689c5acbec8Seric } 690c5acbec8Seric else if (delivery == IMSG_DELIVERY_LOOP) { 69165c4fdfbSgilles queue_loop(e->id); 69265c4fdfbSgilles } 69365c4fdfbSgilles else 69465c4fdfbSgilles errx(1, "bad delivery"); 69565c4fdfbSgilles } 696ac5aa4b9Seric 697c5acbec8Seric void 698c5acbec8Seric mta_delivery(struct mta_envelope *e, const char *source, const char *relay, 699c5acbec8Seric int delivery, const char *status, uint32_t penalty) 700c5acbec8Seric { 701c5acbec8Seric mta_delivery_log(e, source, relay, delivery, status); 702c5acbec8Seric mta_delivery_notify(e, delivery, status, penalty); 703c5acbec8Seric } 704c5acbec8Seric 70565c4fdfbSgilles static void 70665c4fdfbSgilles mta_query_mx(struct mta_relay *relay) 70765c4fdfbSgilles { 70865c4fdfbSgilles uint64_t id; 70965c4fdfbSgilles 71065c4fdfbSgilles if (relay->status & RELAY_WAIT_MX) 71165c4fdfbSgilles return; 71265c4fdfbSgilles 713c5acbec8Seric log_debug("debug: mta: querying MX for %s...", 714c5acbec8Seric mta_relay_to_text(relay)); 71565c4fdfbSgilles 71665c4fdfbSgilles if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) { 71765c4fdfbSgilles id = generate_uid(); 71865c4fdfbSgilles tree_xset(&wait_mx, id, relay->domain); 71965c4fdfbSgilles if (relay->domain->flags) 72065c4fdfbSgilles dns_query_host(id, relay->domain->name); 72165c4fdfbSgilles else 72265c4fdfbSgilles dns_query_mx(id, relay->domain->name); 72365c4fdfbSgilles relay->domain->lastmxquery = time(NULL); 72465c4fdfbSgilles } 72565c4fdfbSgilles relay->status |= RELAY_WAIT_MX; 72665c4fdfbSgilles mta_relay_ref(relay); 72765c4fdfbSgilles } 72865c4fdfbSgilles 72965c4fdfbSgilles static void 730c5acbec8Seric mta_query_limits(struct mta_relay *relay) 731c5acbec8Seric { 732c5acbec8Seric if (relay->status & RELAY_WAIT_LIMITS) 733c5acbec8Seric return; 734c5acbec8Seric 735c5acbec8Seric relay->limits = dict_get(env->sc_limits_dict, relay->domain->name); 736c5acbec8Seric if (relay->limits == NULL) 737c5acbec8Seric relay->limits = dict_get(env->sc_limits_dict, "default"); 738c5acbec8Seric 739c5acbec8Seric if (max_seen_conndelay_route < relay->limits->conndelay_route) 740c5acbec8Seric max_seen_conndelay_route = relay->limits->conndelay_route; 741c5acbec8Seric if (max_seen_discdelay_route < relay->limits->discdelay_route) 742c5acbec8Seric max_seen_discdelay_route = relay->limits->discdelay_route; 743c5acbec8Seric } 744c5acbec8Seric 745c5acbec8Seric static void 74665c4fdfbSgilles mta_query_secret(struct mta_relay *relay) 74765c4fdfbSgilles { 74865c4fdfbSgilles if (relay->status & RELAY_WAIT_SECRET) 74965c4fdfbSgilles return; 75065c4fdfbSgilles 751c5acbec8Seric log_debug("debug: mta: querying secret for %s...", 752c5acbec8Seric mta_relay_to_text(relay)); 75365c4fdfbSgilles 75465c4fdfbSgilles tree_xset(&wait_secret, relay->id, relay); 75565c4fdfbSgilles relay->status |= RELAY_WAIT_SECRET; 75665c4fdfbSgilles 757299c4efeSeric m_create(p_lka, IMSG_LKA_SECRET, 0, 0, -1); 75865c4fdfbSgilles m_add_id(p_lka, relay->id); 75965c4fdfbSgilles m_add_string(p_lka, relay->authtable); 76065c4fdfbSgilles m_add_string(p_lka, relay->authlabel); 76165c4fdfbSgilles m_close(p_lka); 76265c4fdfbSgilles 76365c4fdfbSgilles mta_relay_ref(relay); 76465c4fdfbSgilles } 76565c4fdfbSgilles 76665c4fdfbSgilles static void 76765c4fdfbSgilles mta_query_preference(struct mta_relay *relay) 76865c4fdfbSgilles { 76965c4fdfbSgilles if (relay->status & RELAY_WAIT_PREFERENCE) 77065c4fdfbSgilles return; 77165c4fdfbSgilles 772c5acbec8Seric log_debug("debug: mta: querying preference for %s...", 773c5acbec8Seric mta_relay_to_text(relay)); 77465c4fdfbSgilles 77565c4fdfbSgilles tree_xset(&wait_preference, relay->id, relay); 77665c4fdfbSgilles relay->status |= RELAY_WAIT_PREFERENCE; 77765c4fdfbSgilles dns_query_mx_preference(relay->id, relay->domain->name, 77865c4fdfbSgilles relay->backupname); 77965c4fdfbSgilles mta_relay_ref(relay); 78065c4fdfbSgilles } 78165c4fdfbSgilles 78265c4fdfbSgilles static void 78365c4fdfbSgilles mta_query_source(struct mta_relay *relay) 78465c4fdfbSgilles { 785c5acbec8Seric log_debug("debug: mta: querying source for %s...", 786c5acbec8Seric mta_relay_to_text(relay)); 787c5acbec8Seric 788c5acbec8Seric relay->sourceloop += 1; 789c5acbec8Seric 790c5acbec8Seric if (relay->sourcetable == NULL) { 791c5acbec8Seric /* 792c5acbec8Seric * This is a recursive call, but it only happens once, since 793c5acbec8Seric * another source will not be queried immediatly. 794c5acbec8Seric */ 795c5acbec8Seric mta_relay_ref(relay); 796c5acbec8Seric mta_on_source(relay, mta_source(NULL)); 797c5acbec8Seric return; 798c5acbec8Seric } 79965c4fdfbSgilles 800299c4efeSeric m_create(p_lka, IMSG_LKA_SOURCE, 0, 0, -1); 80165c4fdfbSgilles m_add_id(p_lka, relay->id); 80265c4fdfbSgilles m_add_string(p_lka, relay->sourcetable); 80365c4fdfbSgilles m_close(p_lka); 80465c4fdfbSgilles 80565c4fdfbSgilles tree_xset(&wait_source, relay->id, relay); 80665c4fdfbSgilles relay->status |= RELAY_WAIT_SOURCE; 80765c4fdfbSgilles mta_relay_ref(relay); 80865c4fdfbSgilles } 80965c4fdfbSgilles 81065c4fdfbSgilles static void 81165c4fdfbSgilles mta_on_mx(void *tag, void *arg, void *data) 81265c4fdfbSgilles { 81365c4fdfbSgilles struct mta_domain *domain = data; 81465c4fdfbSgilles struct mta_relay *relay = arg; 81565c4fdfbSgilles 816c5acbec8Seric log_debug("debug: mta: ... got mx (%p, %s, %s)", 81765c4fdfbSgilles tag, domain->name, mta_relay_to_text(relay)); 81865c4fdfbSgilles 81965c4fdfbSgilles switch (domain->mxstatus) { 82065c4fdfbSgilles case DNS_OK: 82165c4fdfbSgilles break; 82265c4fdfbSgilles case DNS_RETRY: 82365c4fdfbSgilles relay->fail = IMSG_DELIVERY_TEMPFAIL; 82465c4fdfbSgilles relay->failstr = "Temporary failure in MX lookup"; 82565c4fdfbSgilles break; 82665c4fdfbSgilles case DNS_EINVAL: 82765c4fdfbSgilles relay->fail = IMSG_DELIVERY_PERMFAIL; 82865c4fdfbSgilles relay->failstr = "Invalid domain name"; 82965c4fdfbSgilles break; 83065c4fdfbSgilles case DNS_ENONAME: 83165c4fdfbSgilles relay->fail = IMSG_DELIVERY_PERMFAIL; 83265c4fdfbSgilles relay->failstr = "Domain does not exist"; 83365c4fdfbSgilles break; 83465c4fdfbSgilles case DNS_ENOTFOUND: 83565c4fdfbSgilles relay->fail = IMSG_DELIVERY_TEMPFAIL; 83665c4fdfbSgilles relay->failstr = "No MX found for domain"; 83765c4fdfbSgilles break; 83865c4fdfbSgilles default: 83965c4fdfbSgilles fatalx("bad DNS lookup error code"); 84065c4fdfbSgilles break; 84165c4fdfbSgilles } 84265c4fdfbSgilles 84365c4fdfbSgilles if (domain->mxstatus) 84465c4fdfbSgilles log_info("smtp-out: Failed to resolve MX for %s: %s", 84565c4fdfbSgilles mta_relay_to_text(relay), relay->failstr); 84665c4fdfbSgilles 84765c4fdfbSgilles relay->status &= ~RELAY_WAIT_MX; 84865c4fdfbSgilles mta_drain(relay); 84965c4fdfbSgilles mta_relay_unref(relay); /* from mta_drain() */ 85065c4fdfbSgilles } 85165c4fdfbSgilles 85265c4fdfbSgilles static void 853c5acbec8Seric mta_on_secret(struct mta_relay *relay, const char *secret) 854c5acbec8Seric { 855c5acbec8Seric log_debug("debug: mta: ... got secret for %s: %s", 856c5acbec8Seric mta_relay_to_text(relay), secret); 857c5acbec8Seric 858c5acbec8Seric if (secret) 859c5acbec8Seric relay->secret = strdup(secret); 860c5acbec8Seric 861c5acbec8Seric if (relay->secret == NULL) { 862c5acbec8Seric log_warnx("warn: Failed to retrieve secret " 863c5acbec8Seric "for %s", mta_relay_to_text(relay)); 864c5acbec8Seric relay->fail = IMSG_DELIVERY_TEMPFAIL; 865c5acbec8Seric relay->failstr = "Could not retrieve credentials"; 866c5acbec8Seric } 867c5acbec8Seric 868c5acbec8Seric relay->status &= ~RELAY_WAIT_SECRET; 869c5acbec8Seric mta_drain(relay); 870c5acbec8Seric mta_relay_unref(relay); /* from mta_query_secret() */ 871c5acbec8Seric } 872c5acbec8Seric 873c5acbec8Seric static void 874c5acbec8Seric mta_on_preference(struct mta_relay *relay, int dnserror, int preference) 875c5acbec8Seric { 876c5acbec8Seric if (dnserror) { 877c5acbec8Seric log_warnx("warn: Couldn't find backup preference for %s", 878c5acbec8Seric mta_relay_to_text(relay)); 879c5acbec8Seric relay->backuppref = INT_MAX; 880c5acbec8Seric } 881c5acbec8Seric else { 882d7bcae4dSeric log_debug("debug: mta: ... got preference for %s: %d, %d", 883c5acbec8Seric mta_relay_to_text(relay), dnserror, preference); 884c5acbec8Seric relay->backuppref = preference; 885c5acbec8Seric } 886c5acbec8Seric 887c5acbec8Seric relay->status &= ~RELAY_WAIT_PREFERENCE; 888c5acbec8Seric mta_drain(relay); 889c5acbec8Seric mta_relay_unref(relay); /* from mta_query_preference() */ 890c5acbec8Seric } 891c5acbec8Seric 892c5acbec8Seric static void 89365c4fdfbSgilles mta_on_source(struct mta_relay *relay, struct mta_source *source) 89465c4fdfbSgilles { 895c5acbec8Seric struct mta_connector *c; 896c5acbec8Seric void *iter; 897c5acbec8Seric int delay, errmask; 898c5acbec8Seric 899c5acbec8Seric log_debug("debug: mta: ... got source for %s: %s", 900c5acbec8Seric mta_relay_to_text(relay), source ? mta_source_to_text(source) : "NULL"); 901c5acbec8Seric 902c5acbec8Seric relay->lastsource = time(NULL); 903c5acbec8Seric delay = DELAY_CHECK_SOURCE_SLOW; 904c5acbec8Seric 905c5acbec8Seric if (source) { 906c5acbec8Seric c = mta_connector(relay, source); 907c5acbec8Seric if (c->flags & CONNECTOR_NEW) { 908c5acbec8Seric c->flags &= ~CONNECTOR_NEW; 909c5acbec8Seric delay = DELAY_CHECK_SOURCE; 910c5acbec8Seric } 911c5acbec8Seric mta_connect(c); 912c5acbec8Seric if ((c->flags & CONNECTOR_ERROR) == 0) 913c5acbec8Seric relay->sourceloop = 0; 914c5acbec8Seric else 915c5acbec8Seric delay = DELAY_CHECK_SOURCE_FAST; 916c5acbec8Seric mta_source_unref(source); /* from constructor */ 917c5acbec8Seric } 918c5acbec8Seric else { 919c5acbec8Seric log_warnx("warn: Failed to get source address" 920c5acbec8Seric "for %s", mta_relay_to_text(relay)); 921c5acbec8Seric } 922c5acbec8Seric 923c5acbec8Seric if (tree_count(&relay->connectors) == 0) { 924c5acbec8Seric relay->fail = IMSG_DELIVERY_TEMPFAIL; 925c5acbec8Seric relay->failstr = "Could not retrieve source address"; 926c5acbec8Seric } 927c5acbec8Seric if (tree_count(&relay->connectors) < relay->sourceloop) { 928c5acbec8Seric relay->fail = IMSG_DELIVERY_TEMPFAIL; 929c5acbec8Seric relay->failstr = "No valid route to remote MX"; 930c5acbec8Seric 931c5acbec8Seric errmask = 0; 932c5acbec8Seric iter = NULL; 933c5acbec8Seric while (tree_iter(&relay->connectors, &iter, NULL, (void **)&c)) 934c5acbec8Seric errmask |= c->flags; 935c5acbec8Seric 936c5acbec8Seric if (errmask & CONNECTOR_ERROR_ROUTE_SMTP) 937c5acbec8Seric relay->failstr = "Destination seem to reject all mails"; 938c5acbec8Seric else if (errmask & CONNECTOR_ERROR_ROUTE_NET) 939c5acbec8Seric relay->failstr = "Network error on destination MXs"; 940c5acbec8Seric else if (errmask & CONNECTOR_ERROR_MX) 941c5acbec8Seric relay->failstr = "No MX found for destination"; 942c5acbec8Seric else if (errmask & CONNECTOR_ERROR_FAMILY) 943c5acbec8Seric relay->failstr = "Address family mismatch on destination MXs"; 944c5acbec8Seric else 945c5acbec8Seric relay->failstr = "No valid route to destination"; 946c5acbec8Seric } 947c5acbec8Seric 948c5acbec8Seric relay->nextsource = relay->lastsource + delay; 949c5acbec8Seric relay->status &= ~RELAY_WAIT_SOURCE; 950c5acbec8Seric mta_drain(relay); 951c5acbec8Seric mta_relay_unref(relay); /* from mta_query_source() */ 95265c4fdfbSgilles } 95365c4fdfbSgilles 95465c4fdfbSgilles static void 95565c4fdfbSgilles mta_connect(struct mta_connector *c) 95665c4fdfbSgilles { 95765c4fdfbSgilles struct mta_route *route; 958c5acbec8Seric struct mta_limits *l = c->relay->limits; 959c5acbec8Seric int limits; 960c5acbec8Seric time_t nextconn, now; 96165c4fdfbSgilles 962c5acbec8Seric again: 96365c4fdfbSgilles 964c5acbec8Seric log_debug("debug: mta: connecting with %s", mta_connector_to_text(c)); 965c5acbec8Seric 966c5acbec8Seric /* Do not connect if this connector has an error. */ 967c5acbec8Seric if (c->flags & CONNECTOR_ERROR) { 968c5acbec8Seric log_debug("debug: mta: connector error"); 96965c4fdfbSgilles return; 97065c4fdfbSgilles } 97165c4fdfbSgilles 972c5acbec8Seric if (c->flags & CONNECTOR_WAIT) { 973c5acbec8Seric log_debug("debug: mta: canceling connector timeout"); 974c5acbec8Seric runq_cancel(runq_connector, NULL, c); 975c5acbec8Seric } 976c5acbec8Seric 977c5acbec8Seric /* No job. */ 978c5acbec8Seric if (c->relay->ntask == 0) { 979c5acbec8Seric log_debug("debug: mta: no task for connector"); 980c5acbec8Seric return; 981c5acbec8Seric } 982c5acbec8Seric 983c5acbec8Seric /* Do not create more connections than necessary */ 984c5acbec8Seric if ((c->relay->nconn_ready >= c->relay->ntask) || 985c5acbec8Seric (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) { 986c5acbec8Seric log_debug("debug: mta: enough connections already"); 987c5acbec8Seric return; 988c5acbec8Seric } 989c5acbec8Seric 990c5acbec8Seric limits = 0; 991c5acbec8Seric nextconn = now = time(NULL); 992c5acbec8Seric 993c5acbec8Seric if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) { 994c5acbec8Seric log_debug("debug: mta: cannot use domain %s before %llus", 995c5acbec8Seric c->relay->domain->name, 996c5acbec8Seric (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now); 997c5acbec8Seric nextconn = c->relay->domain->lastconn + l->conndelay_domain; 998c5acbec8Seric } 999c5acbec8Seric if (c->relay->domain->nconn >= l->maxconn_per_domain) { 1000c5acbec8Seric log_debug("debug: mta: hit domain limit"); 1001c5acbec8Seric limits |= CONNECTOR_LIMIT_DOMAIN; 1002c5acbec8Seric } 1003c5acbec8Seric 1004c5acbec8Seric if (c->source->lastconn + l->conndelay_source > nextconn) { 1005c5acbec8Seric log_debug("debug: mta: cannot use source %s before %llus", 1006c5acbec8Seric mta_source_to_text(c->source), 1007c5acbec8Seric (unsigned long long) c->source->lastconn + l->conndelay_source - now); 1008c5acbec8Seric nextconn = c->source->lastconn + l->conndelay_source; 1009c5acbec8Seric } 1010c5acbec8Seric if (c->source->nconn >= l->maxconn_per_source) { 1011c5acbec8Seric log_debug("debug: mta: hit source limit"); 1012c5acbec8Seric limits |= CONNECTOR_LIMIT_SOURCE; 1013c5acbec8Seric } 1014c5acbec8Seric 1015c5acbec8Seric if (c->lastconn + l->conndelay_connector > nextconn) { 1016c5acbec8Seric log_debug("debug: mta: cannot use %s before %llus", 1017c5acbec8Seric mta_connector_to_text(c), 1018c5acbec8Seric (unsigned long long) c->lastconn + l->conndelay_connector - now); 1019c5acbec8Seric nextconn = c->lastconn + l->conndelay_connector; 1020c5acbec8Seric } 1021c5acbec8Seric if (c->nconn >= l->maxconn_per_connector) { 1022c5acbec8Seric log_debug("debug: mta: hit connector limit"); 1023c5acbec8Seric limits |= CONNECTOR_LIMIT_CONN; 1024c5acbec8Seric } 1025c5acbec8Seric 1026c5acbec8Seric if (c->relay->lastconn + l->conndelay_relay > nextconn) { 1027c5acbec8Seric log_debug("debug: mta: cannot use %s before %llus", 1028c5acbec8Seric mta_relay_to_text(c->relay), 1029c5acbec8Seric (unsigned long long) c->relay->lastconn + l->conndelay_relay - now); 1030c5acbec8Seric nextconn = c->relay->lastconn + l->conndelay_relay; 1031c5acbec8Seric } 1032c5acbec8Seric if (c->relay->nconn >= l->maxconn_per_relay) { 1033c5acbec8Seric log_debug("debug: mta: hit relay limit"); 1034c5acbec8Seric limits |= CONNECTOR_LIMIT_RELAY; 1035c5acbec8Seric } 1036c5acbec8Seric 1037c5acbec8Seric /* We can connect now, find a route */ 1038c5acbec8Seric if (!limits && nextconn <= now) 1039c5acbec8Seric route = mta_find_route(c, now, &limits, &nextconn); 1040c5acbec8Seric else 1041c5acbec8Seric route = NULL; 1042c5acbec8Seric 1043c5acbec8Seric /* No route */ 1044c5acbec8Seric if (route == NULL) { 1045c5acbec8Seric 1046c5acbec8Seric if (c->flags & CONNECTOR_ERROR) { 1047c5acbec8Seric /* XXX we might want to clear this flag later */ 1048c5acbec8Seric log_debug("debug: mta-routing: no route available for %s: errors on connector", 1049c5acbec8Seric mta_connector_to_text(c)); 1050c5acbec8Seric return; 1051c5acbec8Seric } 1052c5acbec8Seric else if (limits) { 1053c5acbec8Seric log_debug("debug: mta-routing: no route available for %s: limits reached", 1054c5acbec8Seric mta_connector_to_text(c)); 1055c5acbec8Seric nextconn = now + DELAY_CHECK_LIMIT; 1056c5acbec8Seric } 1057c5acbec8Seric else { 1058c5acbec8Seric log_debug("debug: mta-routing: no route available for %s: must wait a bit", 1059c5acbec8Seric mta_connector_to_text(c)); 1060c5acbec8Seric } 1061c5acbec8Seric log_debug("debug: mta: retrying to connect on %s in %llus...", 1062c5acbec8Seric mta_connector_to_text(c), 1063c5acbec8Seric (unsigned long long) nextconn - time(NULL)); 1064c5acbec8Seric c->flags |= CONNECTOR_WAIT; 1065c5acbec8Seric runq_schedule(runq_connector, nextconn, NULL, c); 1066c5acbec8Seric return; 1067c5acbec8Seric } 1068c5acbec8Seric 1069c5acbec8Seric log_debug("debug: mta-routing: spawning new connection on %s", 1070c5acbec8Seric mta_route_to_text(route)); 1071c5acbec8Seric 107265c4fdfbSgilles c->nconn += 1; 107365c4fdfbSgilles c->lastconn = time(NULL); 107465c4fdfbSgilles 107565c4fdfbSgilles c->relay->nconn += 1; 107665c4fdfbSgilles c->relay->lastconn = c->lastconn; 1077c5acbec8Seric c->relay->domain->nconn += 1; 1078c5acbec8Seric c->relay->domain->lastconn = c->lastconn; 107965c4fdfbSgilles route->nconn += 1; 108065c4fdfbSgilles route->lastconn = c->lastconn; 108165c4fdfbSgilles route->src->nconn += 1; 108265c4fdfbSgilles route->src->lastconn = c->lastconn; 108365c4fdfbSgilles route->dst->nconn += 1; 108465c4fdfbSgilles route->dst->lastconn = c->lastconn; 108565c4fdfbSgilles 108665c4fdfbSgilles mta_session(c->relay, route); /* this never fails synchronously */ 1087c5acbec8Seric mta_relay_ref(c->relay); 1088c5acbec8Seric 1089c5acbec8Seric goto again; 109065c4fdfbSgilles } 109165c4fdfbSgilles 109265c4fdfbSgilles static void 1093c5acbec8Seric mta_on_timeout(struct runq *runq, void *arg) 109465c4fdfbSgilles { 1095c5acbec8Seric struct mta_connector *connector = arg; 1096c5acbec8Seric struct mta_relay *relay = arg; 1097c5acbec8Seric struct mta_route *route = arg; 1098c5acbec8Seric struct hoststat *hs = arg; 109965c4fdfbSgilles 1100c5acbec8Seric if (runq == runq_relay) { 1101c5acbec8Seric log_debug("debug: mta: ... timeout for %s", 1102c5acbec8Seric mta_relay_to_text(relay)); 1103c5acbec8Seric relay->status &= ~RELAY_WAIT_CONNECTOR; 1104c5acbec8Seric mta_drain(relay); 1105c5acbec8Seric mta_relay_unref(relay); /* from mta_drain() */ 110665c4fdfbSgilles } 1107c5acbec8Seric else if (runq == runq_connector) { 1108c5acbec8Seric log_debug("debug: mta: ... timeout for %s", 1109c5acbec8Seric mta_connector_to_text(connector)); 1110c5acbec8Seric connector->flags &= ~CONNECTOR_WAIT; 1111c5acbec8Seric mta_connect(connector); 111265c4fdfbSgilles } 1113c5acbec8Seric else if (runq == runq_route) { 1114c5acbec8Seric route->flags &= ~ROUTE_RUNQ; 1115c5acbec8Seric mta_route_enable(route); 1116c5acbec8Seric mta_route_unref(route); 111765c4fdfbSgilles } 1118c5acbec8Seric else if (runq == runq_hoststat) { 1119c5acbec8Seric log_debug("debug: mta: ... timeout for hoststat %s", 1120c5acbec8Seric hs->name); 1121c5acbec8Seric mta_hoststat_remove_entry(hs); 1122c5acbec8Seric free(hs); 112365c4fdfbSgilles } 112465c4fdfbSgilles } 112565c4fdfbSgilles 112665c4fdfbSgilles static void 1127c5acbec8Seric mta_route_disable(struct mta_route *route, int penalty, int reason) 112865c4fdfbSgilles { 1129c5acbec8Seric unsigned long long delay; 113065c4fdfbSgilles 1131c5acbec8Seric route->penalty += penalty; 1132c5acbec8Seric route->lastpenalty = time(NULL); 1133c5acbec8Seric delay = (unsigned long long)DELAY_ROUTE_BASE * route->penalty * route->penalty; 1134c5acbec8Seric if (delay > DELAY_ROUTE_MAX) 1135c5acbec8Seric delay = DELAY_ROUTE_MAX; 1136c5acbec8Seric #if 0 1137c5acbec8Seric delay = 60; 1138c5acbec8Seric #endif 113965c4fdfbSgilles 1140c5acbec8Seric log_info("smtp-out: Disabling route %s for %llus", 1141c5acbec8Seric mta_route_to_text(route), delay); 114265c4fdfbSgilles 1143c5acbec8Seric if (route->flags & ROUTE_DISABLED) { 1144c5acbec8Seric runq_cancel(runq_route, NULL, route); 1145c5acbec8Seric mta_route_unref(route); /* from last call to here */ 114665c4fdfbSgilles } 1147c5acbec8Seric route->flags |= reason & ROUTE_DISABLED; 1148c5acbec8Seric runq_schedule(runq_route, time(NULL) + delay, NULL, route); 1149c5acbec8Seric mta_route_ref(route); 115065c4fdfbSgilles } 115165c4fdfbSgilles 115265c4fdfbSgilles static void 1153c5acbec8Seric mta_route_enable(struct mta_route *route) 115465c4fdfbSgilles { 1155c5acbec8Seric if (route->flags & ROUTE_DISABLED) { 1156c5acbec8Seric log_info("smtp-out: Enabling route %s", 1157c5acbec8Seric mta_route_to_text(route)); 1158c5acbec8Seric route->flags &= ~ROUTE_DISABLED; 1159c5acbec8Seric route->flags |= ROUTE_NEW; 1160c5acbec8Seric } 116165c4fdfbSgilles 1162c5acbec8Seric if (route->penalty) { 1163c5acbec8Seric #if DELAY_QUADRATIC 1164c5acbec8Seric route->penalty -= 1; 1165c5acbec8Seric route->lastpenalty = time(NULL); 1166c5acbec8Seric #else 1167c5acbec8Seric route->penalty = 0; 1168c5acbec8Seric #endif 1169c5acbec8Seric } 117065c4fdfbSgilles } 117165c4fdfbSgilles 117265c4fdfbSgilles static void 117365c4fdfbSgilles mta_drain(struct mta_relay *r) 117465c4fdfbSgilles { 117565c4fdfbSgilles char buf[64]; 117665c4fdfbSgilles 117765c4fdfbSgilles log_debug("debug: mta: draining %s " 1178d7bcae4dSeric "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu", 117965c4fdfbSgilles mta_relay_to_text(r), 1180c5acbec8Seric r->refcount, r->ntask, tree_count(&r->connectors), r->nconn); 118165c4fdfbSgilles 118265c4fdfbSgilles /* 118365c4fdfbSgilles * All done. 118465c4fdfbSgilles */ 118565c4fdfbSgilles if (r->ntask == 0) { 118665c4fdfbSgilles log_debug("debug: mta: all done for %s", mta_relay_to_text(r)); 118765c4fdfbSgilles return; 118865c4fdfbSgilles } 118965c4fdfbSgilles 119065c4fdfbSgilles /* 119165c4fdfbSgilles * If we know that this relay is failing flush the tasks. 119265c4fdfbSgilles */ 119365c4fdfbSgilles if (r->fail) { 119465c4fdfbSgilles mta_flush(r, r->fail, r->failstr); 119565c4fdfbSgilles return; 119665c4fdfbSgilles } 119765c4fdfbSgilles 119865c4fdfbSgilles /* Query secret if needed. */ 119965c4fdfbSgilles if (r->flags & RELAY_AUTH && r->secret == NULL) 120065c4fdfbSgilles mta_query_secret(r); 120165c4fdfbSgilles 120265c4fdfbSgilles /* Query our preference if needed. */ 120365c4fdfbSgilles if (r->backupname && r->backuppref == -1) 120465c4fdfbSgilles mta_query_preference(r); 120565c4fdfbSgilles 120665c4fdfbSgilles /* Query the domain MXs if needed. */ 120765c4fdfbSgilles if (r->domain->lastmxquery == 0) 120865c4fdfbSgilles mta_query_mx(r); 120965c4fdfbSgilles 1210c5acbec8Seric /* Query the limits if needed. */ 1211c5acbec8Seric if (r->limits == NULL) 1212c5acbec8Seric mta_query_limits(r); 1213c5acbec8Seric 121465c4fdfbSgilles /* Wait until we are ready to proceed. */ 121565c4fdfbSgilles if (r->status & RELAY_WAITMASK) { 121665c4fdfbSgilles buf[0] = '\0'; 121765c4fdfbSgilles if (r->status & RELAY_WAIT_MX) 121865c4fdfbSgilles strlcat(buf, " MX", sizeof buf); 121965c4fdfbSgilles if (r->status & RELAY_WAIT_PREFERENCE) 122065c4fdfbSgilles strlcat(buf, " preference", sizeof buf); 122165c4fdfbSgilles if (r->status & RELAY_WAIT_SECRET) 122265c4fdfbSgilles strlcat(buf, " secret", sizeof buf); 122365c4fdfbSgilles if (r->status & RELAY_WAIT_SOURCE) 122465c4fdfbSgilles strlcat(buf, " source", sizeof buf); 1225c5acbec8Seric if (r->status & RELAY_WAIT_CONNECTOR) 1226c5acbec8Seric strlcat(buf, " connector", sizeof buf); 122765c4fdfbSgilles log_debug("debug: mta: %s waiting for%s", 122865c4fdfbSgilles mta_relay_to_text(r), buf); 122965c4fdfbSgilles return; 123065c4fdfbSgilles } 123165c4fdfbSgilles 123265c4fdfbSgilles /* 1233c5acbec8Seric * We have pending task, and it's maybe time too try a new source. 123465c4fdfbSgilles */ 1235c5acbec8Seric if (r->nextsource <= time(NULL)) 123665c4fdfbSgilles mta_query_source(r); 1237c5acbec8Seric else { 1238c5acbec8Seric log_debug("debug: mta: scheduling relay %s in %llus...", 1239c5acbec8Seric mta_relay_to_text(r), 1240c5acbec8Seric (unsigned long long) r->nextsource - time(NULL)); 1241c5acbec8Seric runq_schedule(runq_relay, r->nextsource, NULL, r); 1242c5acbec8Seric r->status |= RELAY_WAIT_CONNECTOR; 1243c5acbec8Seric mta_relay_ref(r); 124465c4fdfbSgilles } 124565c4fdfbSgilles } 124665c4fdfbSgilles 124765c4fdfbSgilles static void 124865c4fdfbSgilles mta_flush(struct mta_relay *relay, int fail, const char *error) 124965c4fdfbSgilles { 1250399c053eSeric struct mta_envelope *e; 125165c4fdfbSgilles struct mta_task *task; 1252c5acbec8Seric const char *domain; 1253c5acbec8Seric void *iter; 1254c5acbec8Seric struct mta_connector *c; 125565c4fdfbSgilles size_t n; 1256c5acbec8Seric size_t r; 125765c4fdfbSgilles 1258d7bcae4dSeric log_debug("debug: mta_flush(%s, %d, \"%s\")", 125965c4fdfbSgilles mta_relay_to_text(relay), fail, error); 126065c4fdfbSgilles 1261299c4efeSeric if (fail != IMSG_DELIVERY_TEMPFAIL && fail != IMSG_DELIVERY_PERMFAIL) 1262d7bcae4dSeric errx(1, "unexpected delivery status %d", fail); 126365c4fdfbSgilles 126465c4fdfbSgilles n = 0; 126565c4fdfbSgilles while ((task = TAILQ_FIRST(&relay->tasks))) { 126665c4fdfbSgilles TAILQ_REMOVE(&relay->tasks, task, entry); 126765c4fdfbSgilles while ((e = TAILQ_FIRST(&task->envelopes))) { 126865c4fdfbSgilles TAILQ_REMOVE(&task->envelopes, e, entry); 1269c5acbec8Seric mta_delivery(e, NULL, relay->domain->name, fail, error, 0); 1270c5acbec8Seric 1271c5acbec8Seric /* 1272c5acbec8Seric * host was suspended, cache envelope id in hoststat tree 1273c5acbec8Seric * so that it can be retried when a delivery succeeds for 1274c5acbec8Seric * that domain. 1275c5acbec8Seric */ 1276c5acbec8Seric domain = strchr(e->dest, '@'); 1277c5acbec8Seric if (fail == IMSG_DELIVERY_TEMPFAIL && domain) { 1278c5acbec8Seric r = 0; 1279c5acbec8Seric iter = NULL; 1280c5acbec8Seric while (tree_iter(&relay->connectors, &iter, 1281c5acbec8Seric NULL, (void **)&c)) { 1282c5acbec8Seric if (c->flags & CONNECTOR_ERROR_ROUTE) 1283c5acbec8Seric r++; 1284c5acbec8Seric } 1285c5acbec8Seric if (tree_count(&relay->connectors) == r) 1286c5acbec8Seric mta_hoststat_cache(domain+1, e->id); 1287c5acbec8Seric } 1288c5acbec8Seric 1289399c053eSeric free(e->dest); 1290399c053eSeric free(e->rcpt); 129165c4fdfbSgilles free(e); 129265c4fdfbSgilles n++; 129365c4fdfbSgilles } 1294399c053eSeric free(task->sender); 129565c4fdfbSgilles free(task); 129665c4fdfbSgilles } 129765c4fdfbSgilles 129865c4fdfbSgilles stat_decrement("mta.task", relay->ntask); 129965c4fdfbSgilles stat_decrement("mta.envelope", n); 130065c4fdfbSgilles relay->ntask = 0; 1301*6dc81a07Seric 1302*6dc81a07Seric /* release all waiting envelopes for the relay */ 1303*6dc81a07Seric m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1); 1304*6dc81a07Seric m_add_id(p_queue, relay->id); 1305*6dc81a07Seric m_add_int(p_queue, 0); 1306*6dc81a07Seric m_close(p_queue); 130765c4fdfbSgilles } 130865c4fdfbSgilles 130965c4fdfbSgilles /* 131065c4fdfbSgilles * Find a route to use for this connector 131165c4fdfbSgilles */ 131265c4fdfbSgilles static struct mta_route * 1313c5acbec8Seric mta_find_route(struct mta_connector *c, time_t now, int *limits, 1314c5acbec8Seric time_t *nextconn) 131565c4fdfbSgilles { 131665c4fdfbSgilles struct mta_route *route, *best; 1317c5acbec8Seric struct mta_limits *l = c->relay->limits; 131865c4fdfbSgilles struct mta_mx *mx; 131965c4fdfbSgilles int level, limit_host, limit_route; 1320c5acbec8Seric int family_mismatch, seen, suspended_route; 1321c5acbec8Seric time_t tm; 132265c4fdfbSgilles 1323c5acbec8Seric log_debug("debug: mta-routing: searching new route for %s...", 1324c5acbec8Seric mta_connector_to_text(c)); 1325c5acbec8Seric 1326c5acbec8Seric tm = 0; 132765c4fdfbSgilles limit_host = 0; 132865c4fdfbSgilles limit_route = 0; 1329c5acbec8Seric suspended_route = 0; 133065c4fdfbSgilles family_mismatch = 0; 133165c4fdfbSgilles level = -1; 133265c4fdfbSgilles best = NULL; 133365c4fdfbSgilles seen = 0; 133465c4fdfbSgilles 133565c4fdfbSgilles TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry) { 133665c4fdfbSgilles /* 133765c4fdfbSgilles * New preference level 133865c4fdfbSgilles */ 133965c4fdfbSgilles if (mx->preference > level) { 134065c4fdfbSgilles #ifndef IGNORE_MX_PREFERENCE 134165c4fdfbSgilles /* 134265c4fdfbSgilles * Use the current best MX if found. 134365c4fdfbSgilles */ 134465c4fdfbSgilles if (best) 134565c4fdfbSgilles break; 134665c4fdfbSgilles 134765c4fdfbSgilles /* 134865c4fdfbSgilles * No candidate found. There are valid MXs at this 1349c5acbec8Seric * preference level but they reached their limit, or 1350c5acbec8Seric * we can't connect yet. 135165c4fdfbSgilles */ 1352c5acbec8Seric if (limit_host || limit_route || tm) 135365c4fdfbSgilles break; 135465c4fdfbSgilles 135565c4fdfbSgilles /* 135665c4fdfbSgilles * If we are a backup MX, do not relay to MXs with 135765c4fdfbSgilles * a greater preference value. 135865c4fdfbSgilles */ 135965c4fdfbSgilles if (c->relay->backuppref >= 0 && 136065c4fdfbSgilles mx->preference >= c->relay->backuppref) 136165c4fdfbSgilles break; 136265c4fdfbSgilles 136365c4fdfbSgilles /* 136465c4fdfbSgilles * Start looking at MXs on this preference level. 136565c4fdfbSgilles */ 136665c4fdfbSgilles #endif 136765c4fdfbSgilles level = mx->preference; 136865c4fdfbSgilles } 136965c4fdfbSgilles 137065c4fdfbSgilles if (mx->host->flags & HOST_IGNORE) 137165c4fdfbSgilles continue; 137265c4fdfbSgilles 137365c4fdfbSgilles /* Found a possibly valid mx */ 137465c4fdfbSgilles seen++; 137565c4fdfbSgilles 1376c5acbec8Seric if ((c->source->sa && 1377c5acbec8Seric c->source->sa->sa_family != mx->host->sa->sa_family) || 1378c5acbec8Seric (l->family && l->family != mx->host->sa->sa_family)) { 1379c5acbec8Seric log_debug("debug: mta-routing: skipping host %s: AF mismatch", 1380c5acbec8Seric mta_host_to_text(mx->host)); 138165c4fdfbSgilles family_mismatch = 1; 138265c4fdfbSgilles continue; 138365c4fdfbSgilles } 138465c4fdfbSgilles 1385c5acbec8Seric if (mx->host->nconn >= l->maxconn_per_host) { 1386c5acbec8Seric log_debug("debug: mta-routing: skipping host %s: too many connections", 1387c5acbec8Seric mta_host_to_text(mx->host)); 138865c4fdfbSgilles limit_host = 1; 138965c4fdfbSgilles continue; 139065c4fdfbSgilles } 139165c4fdfbSgilles 1392c5acbec8Seric if (mx->host->lastconn + l->conndelay_host > now) { 1393c5acbec8Seric log_debug("debug: mta-routing: skipping host %s: cannot use before %llus", 1394c5acbec8Seric mta_host_to_text(mx->host), 1395c5acbec8Seric (unsigned long long) mx->host->lastconn + l->conndelay_host - now); 1396c5acbec8Seric if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm) 1397c5acbec8Seric tm = mx->host->lastconn + l->conndelay_host; 1398c5acbec8Seric continue; 1399c5acbec8Seric } 1400c5acbec8Seric 140165c4fdfbSgilles route = mta_route(c->source, mx->host); 140265c4fdfbSgilles 1403c5acbec8Seric if (route->flags & ROUTE_DISABLED) { 1404c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: suspend", 1405c5acbec8Seric mta_route_to_text(route)); 1406c5acbec8Seric suspended_route |= route->flags & ROUTE_DISABLED; 1407c5acbec8Seric mta_route_unref(route); /* from here */ 1408c5acbec8Seric continue; 1409c5acbec8Seric } 1410c5acbec8Seric 1411c5acbec8Seric if (route->nconn && (route->flags & ROUTE_NEW)) { 1412c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: not validated yet", 1413c5acbec8Seric mta_route_to_text(route)); 141465c4fdfbSgilles limit_route = 1; 141565c4fdfbSgilles mta_route_unref(route); /* from here */ 141665c4fdfbSgilles continue; 141765c4fdfbSgilles } 141865c4fdfbSgilles 1419c5acbec8Seric if (route->nconn >= l->maxconn_per_route) { 1420c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: too many connections", 1421c5acbec8Seric mta_route_to_text(route)); 1422c5acbec8Seric limit_route = 1; 1423c5acbec8Seric mta_route_unref(route); /* from here */ 1424c5acbec8Seric continue; 1425c5acbec8Seric } 1426c5acbec8Seric 1427c5acbec8Seric if (route->lastconn + l->conndelay_route > now) { 1428c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)", 1429c5acbec8Seric mta_route_to_text(route), 1430c5acbec8Seric (unsigned long long) route->lastconn + l->conndelay_route - now); 1431c5acbec8Seric if (tm == 0 || route->lastconn + l->conndelay_route < tm) 1432c5acbec8Seric tm = route->lastconn + l->conndelay_route; 1433c5acbec8Seric mta_route_unref(route); /* from here */ 1434c5acbec8Seric continue; 1435c5acbec8Seric } 1436c5acbec8Seric 1437c5acbec8Seric if (route->lastdisc + l->discdelay_route > now) { 1438c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)", 1439c5acbec8Seric mta_route_to_text(route), 1440c5acbec8Seric (unsigned long long) route->lastdisc + l->discdelay_route - now); 1441c5acbec8Seric if (tm == 0 || route->lastdisc + l->discdelay_route < tm) 1442c5acbec8Seric tm = route->lastdisc + l->discdelay_route; 1443c5acbec8Seric mta_route_unref(route); /* from here */ 1444c5acbec8Seric continue; 1445c5acbec8Seric } 1446c5acbec8Seric 144765c4fdfbSgilles /* Use the route with the lowest number of connections. */ 144865c4fdfbSgilles if (best && route->nconn >= best->nconn) { 1449c5acbec8Seric log_debug("debug: mta-routing: skipping route %s: current one is better", 1450c5acbec8Seric mta_route_to_text(route)); 145165c4fdfbSgilles mta_route_unref(route); /* from here */ 145265c4fdfbSgilles continue; 145365c4fdfbSgilles } 145465c4fdfbSgilles 145565c4fdfbSgilles if (best) 145665c4fdfbSgilles mta_route_unref(best); /* from here */ 145765c4fdfbSgilles best = route; 1458c5acbec8Seric log_debug("debug: mta-routing: selecting candidate route %s", 1459c5acbec8Seric mta_route_to_text(route)); 146065c4fdfbSgilles } 146165c4fdfbSgilles 146265c4fdfbSgilles if (best) 146365c4fdfbSgilles return (best); 146465c4fdfbSgilles 1465c5acbec8Seric /* Order is important */ 146665c4fdfbSgilles if (seen == 0) { 1467c5acbec8Seric log_info("smtp-out: No MX found for %s", 146865c4fdfbSgilles mta_connector_to_text(c)); 1469c5acbec8Seric c->flags |= CONNECTOR_ERROR_MX; 147065c4fdfbSgilles } 147165c4fdfbSgilles else if (limit_route) { 1472c5acbec8Seric log_debug("debug: mta: hit route limit"); 1473c5acbec8Seric *limits |= CONNECTOR_LIMIT_ROUTE; 147465c4fdfbSgilles } 147565c4fdfbSgilles else if (limit_host) { 1476c5acbec8Seric log_debug("debug: mta: hit host limit"); 1477c5acbec8Seric *limits |= CONNECTOR_LIMIT_HOST; 1478c5acbec8Seric } 1479c5acbec8Seric else if (tm) { 1480c5acbec8Seric if (tm > *nextconn) 1481c5acbec8Seric *nextconn = tm; 148265c4fdfbSgilles } 1483299c4efeSeric else if (family_mismatch) { 1484c5acbec8Seric log_info("smtp-out: Address family mismatch on %s", 1485299c4efeSeric mta_connector_to_text(c)); 1486c5acbec8Seric c->flags |= CONNECTOR_ERROR_FAMILY; 1487c5acbec8Seric } 1488c5acbec8Seric else if (suspended_route) { 1489c5acbec8Seric log_info("smtp-out: No valid route for %s", 1490c5acbec8Seric mta_connector_to_text(c)); 1491c5acbec8Seric if (suspended_route & ROUTE_DISABLED_NET) 1492c5acbec8Seric c->flags |= CONNECTOR_ERROR_ROUTE_NET; 1493c5acbec8Seric if (suspended_route & ROUTE_DISABLED_SMTP) 1494c5acbec8Seric c->flags |= CONNECTOR_ERROR_ROUTE_SMTP; 1495299c4efeSeric } 149665c4fdfbSgilles 149765c4fdfbSgilles return (NULL); 149865c4fdfbSgilles } 149965c4fdfbSgilles 150065c4fdfbSgilles static void 1501299c4efeSeric mta_log(const struct mta_envelope *evp, const char *prefix, const char *source, 1502299c4efeSeric const char *relay, const char *status) 150365c4fdfbSgilles { 1504c5acbec8Seric log_info("relay: %s for %016" PRIx64 ": session=%016"PRIx64", " 1505c5acbec8Seric "from=<%s>, to=<%s>, rcpt=<%s>, source=%s, " 1506c5acbec8Seric "relay=%s, delay=%s, stat=%s", 150765c4fdfbSgilles prefix, 1508399c053eSeric evp->id, 1509c5acbec8Seric evp->session, 1510399c053eSeric evp->task->sender, 1511399c053eSeric evp->dest, 1512c5acbec8Seric evp->rcpt ? evp->rcpt : "-", 1513c5acbec8Seric source ? source : "-", 151465c4fdfbSgilles relay, 151565c4fdfbSgilles duration_to_text(time(NULL) - evp->creation), 151665c4fdfbSgilles status); 151765c4fdfbSgilles } 151865c4fdfbSgilles 151965c4fdfbSgilles static struct mta_relay * 152065c4fdfbSgilles mta_relay(struct envelope *e) 152165c4fdfbSgilles { 152265c4fdfbSgilles struct mta_relay key, *r; 152365c4fdfbSgilles 152465c4fdfbSgilles bzero(&key, sizeof key); 152565c4fdfbSgilles 152665c4fdfbSgilles if (e->agent.mta.relay.flags & RELAY_BACKUP) { 152765c4fdfbSgilles key.domain = mta_domain(e->dest.domain, 0); 152865c4fdfbSgilles key.backupname = e->agent.mta.relay.hostname; 152965c4fdfbSgilles } else if (e->agent.mta.relay.hostname[0]) { 153065c4fdfbSgilles key.domain = mta_domain(e->agent.mta.relay.hostname, 1); 153165c4fdfbSgilles key.flags |= RELAY_MX; 153265c4fdfbSgilles } else { 153365c4fdfbSgilles key.domain = mta_domain(e->dest.domain, 0); 153465c4fdfbSgilles } 153565c4fdfbSgilles 153665c4fdfbSgilles key.flags = e->agent.mta.relay.flags; 153765c4fdfbSgilles key.port = e->agent.mta.relay.port; 153865c4fdfbSgilles key.cert = e->agent.mta.relay.cert; 153965c4fdfbSgilles if (!key.cert[0]) 154065c4fdfbSgilles key.cert = NULL; 154165c4fdfbSgilles key.authtable = e->agent.mta.relay.authtable; 154265c4fdfbSgilles if (!key.authtable[0]) 154365c4fdfbSgilles key.authtable = NULL; 154465c4fdfbSgilles key.authlabel = e->agent.mta.relay.authlabel; 154565c4fdfbSgilles if (!key.authlabel[0]) 154665c4fdfbSgilles key.authlabel = NULL; 154765c4fdfbSgilles key.sourcetable = e->agent.mta.relay.sourcetable; 154865c4fdfbSgilles if (!key.sourcetable[0]) 154965c4fdfbSgilles key.sourcetable = NULL; 155065c4fdfbSgilles key.helotable = e->agent.mta.relay.helotable; 155165c4fdfbSgilles if (!key.helotable[0]) 155265c4fdfbSgilles key.helotable = NULL; 155365c4fdfbSgilles 155465c4fdfbSgilles if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)) == NULL) { 155565c4fdfbSgilles r = xcalloc(1, sizeof *r, "mta_relay"); 155665c4fdfbSgilles TAILQ_INIT(&r->tasks); 155765c4fdfbSgilles r->id = generate_uid(); 155865c4fdfbSgilles r->flags = key.flags; 155965c4fdfbSgilles r->domain = key.domain; 156065c4fdfbSgilles r->backupname = key.backupname ? 156165c4fdfbSgilles xstrdup(key.backupname, "mta: backupname") : NULL; 156265c4fdfbSgilles r->backuppref = -1; 156365c4fdfbSgilles r->port = key.port; 156465c4fdfbSgilles r->cert = key.cert ? xstrdup(key.cert, "mta: cert") : NULL; 156565c4fdfbSgilles if (key.authtable) 156665c4fdfbSgilles r->authtable = xstrdup(key.authtable, "mta: authtable"); 156765c4fdfbSgilles if (key.authlabel) 156865c4fdfbSgilles r->authlabel = xstrdup(key.authlabel, "mta: authlabel"); 156965c4fdfbSgilles if (key.sourcetable) 157065c4fdfbSgilles r->sourcetable = xstrdup(key.sourcetable, 157165c4fdfbSgilles "mta: sourcetable"); 157265c4fdfbSgilles if (key.helotable) 157365c4fdfbSgilles r->helotable = xstrdup(key.helotable, 157465c4fdfbSgilles "mta: helotable"); 157565c4fdfbSgilles SPLAY_INSERT(mta_relay_tree, &relays, r); 157665c4fdfbSgilles stat_increment("mta.relay", 1); 157765c4fdfbSgilles } else { 157865c4fdfbSgilles mta_domain_unref(key.domain); /* from here */ 157965c4fdfbSgilles } 158065c4fdfbSgilles 158165c4fdfbSgilles r->refcount++; 158265c4fdfbSgilles return (r); 158365c4fdfbSgilles } 158465c4fdfbSgilles 158565c4fdfbSgilles static void 158665c4fdfbSgilles mta_relay_ref(struct mta_relay *r) 158765c4fdfbSgilles { 158865c4fdfbSgilles r->refcount++; 158965c4fdfbSgilles } 159065c4fdfbSgilles 159165c4fdfbSgilles static void 159265c4fdfbSgilles mta_relay_unref(struct mta_relay *relay) 159365c4fdfbSgilles { 159465c4fdfbSgilles struct mta_connector *c; 159565c4fdfbSgilles 159665c4fdfbSgilles if (--relay->refcount) 159765c4fdfbSgilles return; 159865c4fdfbSgilles 1599*6dc81a07Seric /* Make sure they are no envelopes held for this relay */ 1600*6dc81a07Seric m_create(p_queue, IMSG_DELIVERY_RELEASE, 0, 0, -1); 1601*6dc81a07Seric m_add_id(p_queue, relay->id); 1602*6dc81a07Seric m_add_int(p_queue, 0); 1603*6dc81a07Seric m_close(p_queue); 1604*6dc81a07Seric 160565c4fdfbSgilles log_debug("debug: mta: freeing %s", mta_relay_to_text(relay)); 160665c4fdfbSgilles SPLAY_REMOVE(mta_relay_tree, &relays, relay); 1607edae97a5Seric 16087d590283Seric while ((tree_poproot(&relay->connectors, NULL, (void**)&c))) 16097d590283Seric mta_connector_free(c); 16107d590283Seric 161165c4fdfbSgilles free(relay->authlabel); 1612edae97a5Seric free(relay->authtable); 1613edae97a5Seric free(relay->backupname); 1614edae97a5Seric free(relay->cert); 1615edae97a5Seric free(relay->helotable); 1616edae97a5Seric free(relay->secret); 1617edae97a5Seric free(relay->sourcetable); 161865c4fdfbSgilles 161965c4fdfbSgilles mta_domain_unref(relay->domain); /* from constructor */ 162065c4fdfbSgilles free(relay); 162165c4fdfbSgilles stat_decrement("mta.relay", 1); 1622ac5aa4b9Seric } 1623ac5aa4b9Seric 1624ac5aa4b9Seric const char * 162565c4fdfbSgilles mta_relay_to_text(struct mta_relay *relay) 1626ac5aa4b9Seric { 1627ac5aa4b9Seric static char buf[1024]; 1628e009e7d3Seric char tmp[32]; 162965c4fdfbSgilles const char *sep = ","; 1630ac5aa4b9Seric 163165c4fdfbSgilles snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name); 1632ac5aa4b9Seric 163365c4fdfbSgilles if (relay->port) { 163465c4fdfbSgilles strlcat(buf, sep, sizeof buf); 1635d7bcae4dSeric snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port); 1636e009e7d3Seric strlcat(buf, tmp, sizeof buf); 1637e009e7d3Seric } 1638e009e7d3Seric 163965c4fdfbSgilles if (relay->flags & RELAY_STARTTLS) { 1640e009e7d3Seric strlcat(buf, sep, sizeof buf); 1641ac5aa4b9Seric strlcat(buf, "starttls", sizeof buf); 1642ac5aa4b9Seric } 1643ac5aa4b9Seric 164465c4fdfbSgilles if (relay->flags & RELAY_SMTPS) { 1645a2389673Seric strlcat(buf, sep, sizeof buf); 1646ac5aa4b9Seric strlcat(buf, "smtps", sizeof buf); 1647ac5aa4b9Seric } 1648ac5aa4b9Seric 164965c4fdfbSgilles if (relay->flags & RELAY_AUTH) { 1650a2389673Seric strlcat(buf, sep, sizeof buf); 1651ac5aa4b9Seric strlcat(buf, "auth=", sizeof buf); 165265c4fdfbSgilles strlcat(buf, relay->authtable, sizeof buf); 165365c4fdfbSgilles strlcat(buf, ":", sizeof buf); 165465c4fdfbSgilles strlcat(buf, relay->authlabel, sizeof buf); 1655ac5aa4b9Seric } 1656ac5aa4b9Seric 165765c4fdfbSgilles if (relay->cert) { 1658a2389673Seric strlcat(buf, sep, sizeof buf); 1659ac5aa4b9Seric strlcat(buf, "cert=", sizeof buf); 166065c4fdfbSgilles strlcat(buf, relay->cert, sizeof buf); 1661ac5aa4b9Seric } 1662ac5aa4b9Seric 166365c4fdfbSgilles if (relay->flags & RELAY_MX) { 1664a2389673Seric strlcat(buf, sep, sizeof buf); 1665ac5aa4b9Seric strlcat(buf, "mx", sizeof buf); 1666ac5aa4b9Seric } 1667a2389673Seric 166865c4fdfbSgilles if (relay->flags & RELAY_BACKUP) { 1669a2389673Seric strlcat(buf, sep, sizeof buf); 1670a2389673Seric strlcat(buf, "backup=", sizeof buf); 167165c4fdfbSgilles strlcat(buf, relay->backupname, sizeof buf); 167265c4fdfbSgilles } 167365c4fdfbSgilles 167465c4fdfbSgilles if (relay->sourcetable) { 167565c4fdfbSgilles strlcat(buf, sep, sizeof buf); 167665c4fdfbSgilles strlcat(buf, "sourcetable=", sizeof buf); 167765c4fdfbSgilles strlcat(buf, relay->sourcetable, sizeof buf); 1678a2389673Seric } 1679a2389673Seric 1680ac5aa4b9Seric strlcat(buf, "]", sizeof buf); 1681ac5aa4b9Seric 1682ac5aa4b9Seric return (buf); 1683ac5aa4b9Seric } 1684ac5aa4b9Seric 1685ac5aa4b9Seric static int 168665c4fdfbSgilles mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b) 1687ac5aa4b9Seric { 1688ac5aa4b9Seric int r; 1689ac5aa4b9Seric 169065c4fdfbSgilles if (a->domain < b->domain) 169165c4fdfbSgilles return (-1); 169265c4fdfbSgilles if (a->domain > b->domain) 169365c4fdfbSgilles return (1); 169465c4fdfbSgilles 1695ac5aa4b9Seric if (a->flags < b->flags) 1696ac5aa4b9Seric return (-1); 1697ac5aa4b9Seric if (a->flags > b->flags) 1698ac5aa4b9Seric return (1); 1699ac5aa4b9Seric 1700ac5aa4b9Seric if (a->port < b->port) 1701ac5aa4b9Seric return (-1); 1702ac5aa4b9Seric if (a->port > b->port) 1703ac5aa4b9Seric return (1); 1704ac5aa4b9Seric 170565c4fdfbSgilles if (a->authtable == NULL && b->authtable) 1706ac5aa4b9Seric return (-1); 170765c4fdfbSgilles if (a->authtable && b->authtable == NULL) 1708ac5aa4b9Seric return (1); 170965c4fdfbSgilles if (a->authtable && ((r = strcmp(a->authtable, b->authtable)))) 171065c4fdfbSgilles return (r); 171165c4fdfbSgilles if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel)))) 171265c4fdfbSgilles return (r); 171365c4fdfbSgilles if (a->sourcetable == NULL && b->sourcetable) 171465c4fdfbSgilles return (-1); 171565c4fdfbSgilles if (a->sourcetable && b->sourcetable == NULL) 171665c4fdfbSgilles return (1); 171765c4fdfbSgilles if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable)))) 1718ac5aa4b9Seric return (r); 1719ac5aa4b9Seric 1720ac5aa4b9Seric if (a->cert == NULL && b->cert) 1721ac5aa4b9Seric return (-1); 1722ac5aa4b9Seric if (a->cert && b->cert == NULL) 1723ac5aa4b9Seric return (1); 1724ac5aa4b9Seric if (a->cert && ((r = strcmp(a->cert, b->cert)))) 1725ac5aa4b9Seric return (r); 1726ac5aa4b9Seric 17276916573aSeric if (a->backupname && ((r = strcmp(a->backupname, b->backupname)))) 17286916573aSeric return (r); 17296916573aSeric 173065c4fdfbSgilles return (0); 173165c4fdfbSgilles } 173265c4fdfbSgilles 173365c4fdfbSgilles SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp); 173465c4fdfbSgilles 173565c4fdfbSgilles static struct mta_host * 173665c4fdfbSgilles mta_host(const struct sockaddr *sa) 173765c4fdfbSgilles { 173865c4fdfbSgilles struct mta_host key, *h; 173965c4fdfbSgilles struct sockaddr_storage ss; 174065c4fdfbSgilles 174165c4fdfbSgilles memmove(&ss, sa, sa->sa_len); 174265c4fdfbSgilles key.sa = (struct sockaddr*)&ss; 174365c4fdfbSgilles h = SPLAY_FIND(mta_host_tree, &hosts, &key); 174465c4fdfbSgilles 174565c4fdfbSgilles if (h == NULL) { 174665c4fdfbSgilles h = xcalloc(1, sizeof(*h), "mta_host"); 174765c4fdfbSgilles h->sa = xmemdup(sa, sa->sa_len, "mta_host"); 174865c4fdfbSgilles SPLAY_INSERT(mta_host_tree, &hosts, h); 174965c4fdfbSgilles stat_increment("mta.host", 1); 175065c4fdfbSgilles } 175165c4fdfbSgilles 175265c4fdfbSgilles h->refcount++; 175365c4fdfbSgilles return (h); 175465c4fdfbSgilles } 175565c4fdfbSgilles 175665c4fdfbSgilles static void 175765c4fdfbSgilles mta_host_ref(struct mta_host *h) 175865c4fdfbSgilles { 175965c4fdfbSgilles h->refcount++; 176065c4fdfbSgilles } 176165c4fdfbSgilles 176265c4fdfbSgilles static void 176365c4fdfbSgilles mta_host_unref(struct mta_host *h) 176465c4fdfbSgilles { 176565c4fdfbSgilles if (--h->refcount) 176665c4fdfbSgilles return; 176765c4fdfbSgilles 176865c4fdfbSgilles SPLAY_REMOVE(mta_host_tree, &hosts, h); 176965c4fdfbSgilles free(h->sa); 177065c4fdfbSgilles free(h->ptrname); 1771edae97a5Seric free(h); 177265c4fdfbSgilles stat_decrement("mta.host", 1); 177365c4fdfbSgilles } 177465c4fdfbSgilles 177565c4fdfbSgilles const char * 177665c4fdfbSgilles mta_host_to_text(struct mta_host *h) 177765c4fdfbSgilles { 177865c4fdfbSgilles static char buf[1024]; 177965c4fdfbSgilles 178065c4fdfbSgilles if (h->ptrname) 178165c4fdfbSgilles snprintf(buf, sizeof buf, "%s (%s)", 178265c4fdfbSgilles sa_to_text(h->sa), h->ptrname); 178365c4fdfbSgilles else 178465c4fdfbSgilles snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa)); 178565c4fdfbSgilles 178665c4fdfbSgilles return (buf); 178765c4fdfbSgilles } 178865c4fdfbSgilles 178965c4fdfbSgilles static int 179065c4fdfbSgilles mta_host_cmp(const struct mta_host *a, const struct mta_host *b) 179165c4fdfbSgilles { 179265c4fdfbSgilles if (a->sa->sa_len < b->sa->sa_len) 179365c4fdfbSgilles return (-1); 179465c4fdfbSgilles if (a->sa->sa_len > b->sa->sa_len) 179565c4fdfbSgilles return (1); 179665c4fdfbSgilles return (memcmp(a->sa, b->sa, a->sa->sa_len)); 179765c4fdfbSgilles } 179865c4fdfbSgilles 179965c4fdfbSgilles SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp); 180065c4fdfbSgilles 180165c4fdfbSgilles static struct mta_domain * 180265c4fdfbSgilles mta_domain(char *name, int flags) 180365c4fdfbSgilles { 180465c4fdfbSgilles struct mta_domain key, *d; 180565c4fdfbSgilles 180665c4fdfbSgilles key.name = name; 180765c4fdfbSgilles key.flags = flags; 180865c4fdfbSgilles d = SPLAY_FIND(mta_domain_tree, &domains, &key); 180965c4fdfbSgilles 181065c4fdfbSgilles if (d == NULL) { 181165c4fdfbSgilles d = xcalloc(1, sizeof(*d), "mta_domain"); 181265c4fdfbSgilles d->name = xstrdup(name, "mta_domain"); 181365c4fdfbSgilles d->flags = flags; 181465c4fdfbSgilles TAILQ_INIT(&d->mxs); 181565c4fdfbSgilles SPLAY_INSERT(mta_domain_tree, &domains, d); 181665c4fdfbSgilles stat_increment("mta.domain", 1); 181765c4fdfbSgilles } 181865c4fdfbSgilles 181965c4fdfbSgilles d->refcount++; 182065c4fdfbSgilles return (d); 182165c4fdfbSgilles } 182265c4fdfbSgilles 182365c4fdfbSgilles #if 0 182465c4fdfbSgilles static void 182565c4fdfbSgilles mta_domain_ref(struct mta_domain *d) 182665c4fdfbSgilles { 182765c4fdfbSgilles d->refcount++; 182865c4fdfbSgilles } 182965c4fdfbSgilles #endif 183065c4fdfbSgilles 183165c4fdfbSgilles static void 183265c4fdfbSgilles mta_domain_unref(struct mta_domain *d) 183365c4fdfbSgilles { 183465c4fdfbSgilles struct mta_mx *mx; 183565c4fdfbSgilles 183665c4fdfbSgilles if (--d->refcount) 183765c4fdfbSgilles return; 183865c4fdfbSgilles 183965c4fdfbSgilles while ((mx = TAILQ_FIRST(&d->mxs))) { 184065c4fdfbSgilles TAILQ_REMOVE(&d->mxs, mx, entry); 184165c4fdfbSgilles mta_host_unref(mx->host); /* from IMSG_DNS_HOST */ 184265c4fdfbSgilles free(mx); 184365c4fdfbSgilles } 184465c4fdfbSgilles 184565c4fdfbSgilles SPLAY_REMOVE(mta_domain_tree, &domains, d); 184665c4fdfbSgilles free(d->name); 1847edae97a5Seric free(d); 184865c4fdfbSgilles stat_decrement("mta.domain", 1); 184965c4fdfbSgilles } 185065c4fdfbSgilles 185165c4fdfbSgilles static int 185265c4fdfbSgilles mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b) 185365c4fdfbSgilles { 185465c4fdfbSgilles if (a->flags < b->flags) 185565c4fdfbSgilles return (-1); 185665c4fdfbSgilles if (a->flags > b->flags) 185765c4fdfbSgilles return (1); 185865c4fdfbSgilles return (strcasecmp(a->name, b->name)); 185965c4fdfbSgilles } 186065c4fdfbSgilles 186165c4fdfbSgilles SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp); 186265c4fdfbSgilles 186365c4fdfbSgilles static struct mta_source * 186465c4fdfbSgilles mta_source(const struct sockaddr *sa) 186565c4fdfbSgilles { 186665c4fdfbSgilles struct mta_source key, *s; 186765c4fdfbSgilles struct sockaddr_storage ss; 186865c4fdfbSgilles 186965c4fdfbSgilles if (sa) { 187065c4fdfbSgilles memmove(&ss, sa, sa->sa_len); 187165c4fdfbSgilles key.sa = (struct sockaddr*)&ss; 187265c4fdfbSgilles } else 187365c4fdfbSgilles key.sa = NULL; 187465c4fdfbSgilles s = SPLAY_FIND(mta_source_tree, &sources, &key); 187565c4fdfbSgilles 187665c4fdfbSgilles if (s == NULL) { 187765c4fdfbSgilles s = xcalloc(1, sizeof(*s), "mta_source"); 187865c4fdfbSgilles if (sa) 187965c4fdfbSgilles s->sa = xmemdup(sa, sa->sa_len, "mta_source"); 188065c4fdfbSgilles SPLAY_INSERT(mta_source_tree, &sources, s); 188165c4fdfbSgilles stat_increment("mta.source", 1); 188265c4fdfbSgilles } 188365c4fdfbSgilles 188465c4fdfbSgilles s->refcount++; 188565c4fdfbSgilles return (s); 188665c4fdfbSgilles } 188765c4fdfbSgilles 188865c4fdfbSgilles static void 188965c4fdfbSgilles mta_source_ref(struct mta_source *s) 189065c4fdfbSgilles { 189165c4fdfbSgilles s->refcount++; 189265c4fdfbSgilles } 189365c4fdfbSgilles 189465c4fdfbSgilles static void 189565c4fdfbSgilles mta_source_unref(struct mta_source *s) 189665c4fdfbSgilles { 189765c4fdfbSgilles if (--s->refcount) 189865c4fdfbSgilles return; 189965c4fdfbSgilles 190065c4fdfbSgilles SPLAY_REMOVE(mta_source_tree, &sources, s); 190165c4fdfbSgilles free(s->sa); 1902edae97a5Seric free(s); 190365c4fdfbSgilles stat_decrement("mta.source", 1); 190465c4fdfbSgilles } 190565c4fdfbSgilles 190665c4fdfbSgilles static const char * 190765c4fdfbSgilles mta_source_to_text(struct mta_source *s) 190865c4fdfbSgilles { 190965c4fdfbSgilles static char buf[1024]; 191065c4fdfbSgilles 191165c4fdfbSgilles if (s->sa == NULL) 191265c4fdfbSgilles return "[]"; 191365c4fdfbSgilles snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa)); 191465c4fdfbSgilles return (buf); 191565c4fdfbSgilles } 191665c4fdfbSgilles 191765c4fdfbSgilles static int 191865c4fdfbSgilles mta_source_cmp(const struct mta_source *a, const struct mta_source *b) 191965c4fdfbSgilles { 192065c4fdfbSgilles if (a->sa == NULL) 192165c4fdfbSgilles return ((b->sa == NULL) ? 0 : -1); 192265c4fdfbSgilles if (b->sa == NULL) 192365c4fdfbSgilles return (1); 192465c4fdfbSgilles if (a->sa->sa_len < b->sa->sa_len) 192565c4fdfbSgilles return (-1); 192665c4fdfbSgilles if (a->sa->sa_len > b->sa->sa_len) 192765c4fdfbSgilles return (1); 192865c4fdfbSgilles return (memcmp(a->sa, b->sa, a->sa->sa_len)); 192965c4fdfbSgilles } 193065c4fdfbSgilles 193165c4fdfbSgilles SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp); 193265c4fdfbSgilles 193365c4fdfbSgilles static struct mta_connector * 193465c4fdfbSgilles mta_connector(struct mta_relay *relay, struct mta_source *source) 193565c4fdfbSgilles { 193665c4fdfbSgilles struct mta_connector *c; 193765c4fdfbSgilles 193865c4fdfbSgilles c = tree_get(&relay->connectors, (uintptr_t)(source)); 193965c4fdfbSgilles if (c == NULL) { 194065c4fdfbSgilles c = xcalloc(1, sizeof(*c), "mta_connector"); 194165c4fdfbSgilles c->relay = relay; 194265c4fdfbSgilles c->source = source; 1943c5acbec8Seric c->flags |= CONNECTOR_NEW; 194465c4fdfbSgilles mta_source_ref(source); 194565c4fdfbSgilles tree_xset(&relay->connectors, (uintptr_t)(source), c); 194665c4fdfbSgilles stat_increment("mta.connector", 1); 1947c5acbec8Seric log_debug("debug: mta: new %s", mta_connector_to_text(c)); 194865c4fdfbSgilles } 194965c4fdfbSgilles 195065c4fdfbSgilles return (c); 195165c4fdfbSgilles } 195265c4fdfbSgilles 195365c4fdfbSgilles static void 195465c4fdfbSgilles mta_connector_free(struct mta_connector *c) 195565c4fdfbSgilles { 1956c5acbec8Seric log_debug("debug: mta: freeing %s", 1957c5acbec8Seric mta_connector_to_text(c)); 1958c5acbec8Seric 1959c5acbec8Seric if (c->flags & CONNECTOR_WAIT) { 1960c5acbec8Seric log_debug("debug: mta: canceling timeout for %s", 1961c5acbec8Seric mta_connector_to_text(c)); 1962c5acbec8Seric runq_cancel(runq_connector, NULL, c); 1963c5acbec8Seric } 1964c5acbec8Seric mta_source_unref(c->source); /* from constructor */ 1965edae97a5Seric free(c); 1966c5acbec8Seric 196765c4fdfbSgilles stat_decrement("mta.connector", 1); 196865c4fdfbSgilles } 196965c4fdfbSgilles 197065c4fdfbSgilles static const char * 197165c4fdfbSgilles mta_connector_to_text(struct mta_connector *c) 197265c4fdfbSgilles { 197365c4fdfbSgilles static char buf[1024]; 197465c4fdfbSgilles 1975c5acbec8Seric snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]", 197665c4fdfbSgilles mta_source_to_text(c->source), 1977c5acbec8Seric mta_relay_to_text(c->relay), 1978c5acbec8Seric c->flags); 197965c4fdfbSgilles return (buf); 198065c4fdfbSgilles } 198165c4fdfbSgilles 198265c4fdfbSgilles static struct mta_route * 198365c4fdfbSgilles mta_route(struct mta_source *src, struct mta_host *dst) 198465c4fdfbSgilles { 198565c4fdfbSgilles struct mta_route key, *r; 1986c5acbec8Seric static uint64_t rid = 0; 198765c4fdfbSgilles 198865c4fdfbSgilles key.src = src; 198965c4fdfbSgilles key.dst = dst; 199065c4fdfbSgilles r = SPLAY_FIND(mta_route_tree, &routes, &key); 199165c4fdfbSgilles 199265c4fdfbSgilles if (r == NULL) { 199365c4fdfbSgilles r = xcalloc(1, sizeof(*r), "mta_route"); 199465c4fdfbSgilles r->src = src; 199565c4fdfbSgilles r->dst = dst; 1996c5acbec8Seric r->flags |= ROUTE_NEW; 1997c5acbec8Seric r->id = ++rid; 199865c4fdfbSgilles SPLAY_INSERT(mta_route_tree, &routes, r); 199965c4fdfbSgilles mta_source_ref(src); 200065c4fdfbSgilles mta_host_ref(dst); 200165c4fdfbSgilles stat_increment("mta.route", 1); 200265c4fdfbSgilles } 2003c5acbec8Seric else if (r->flags & ROUTE_RUNQ) { 2004c5acbec8Seric log_debug("debug: mta: mta_route_ref(): canceling runq for route %s", 2005c5acbec8Seric mta_route_to_text(r)); 2006c5acbec8Seric r->flags &= ~(ROUTE_RUNQ | ROUTE_KEEPALIVE); 2007c5acbec8Seric runq_cancel(runq_route, NULL, r); 2008c5acbec8Seric r->refcount--; /* from mta_route_unref() */ 2009c5acbec8Seric } 201065c4fdfbSgilles 201165c4fdfbSgilles r->refcount++; 2012ac5aa4b9Seric return (r); 201365c4fdfbSgilles } 201465c4fdfbSgilles 201565c4fdfbSgilles static void 201665c4fdfbSgilles mta_route_ref(struct mta_route *r) 201765c4fdfbSgilles { 201865c4fdfbSgilles r->refcount++; 201965c4fdfbSgilles } 202065c4fdfbSgilles 202165c4fdfbSgilles static void 202265c4fdfbSgilles mta_route_unref(struct mta_route *r) 202365c4fdfbSgilles { 2024c5acbec8Seric time_t sched, now; 2025c5acbec8Seric int delay; 2026c5acbec8Seric 202765c4fdfbSgilles if (--r->refcount) 202865c4fdfbSgilles return; 202965c4fdfbSgilles 2030c5acbec8Seric /* 2031c5acbec8Seric * Nothing references this route, but we might want to keep it alive 2032c5acbec8Seric * for a while. 2033c5acbec8Seric */ 2034c5acbec8Seric now = time(NULL); 2035c5acbec8Seric sched = 0; 2036c5acbec8Seric 2037c5acbec8Seric if (r->penalty) { 2038c5acbec8Seric #if DELAY_QUADRATIC 2039c5acbec8Seric delay = DELAY_ROUTE_BASE * r->penalty * r->penalty; 2040c5acbec8Seric #else 2041c5acbec8Seric delay = 15 * 60; 2042c5acbec8Seric #endif 2043c5acbec8Seric if (delay > DELAY_ROUTE_MAX) 2044c5acbec8Seric delay = DELAY_ROUTE_MAX; 2045c5acbec8Seric sched = r->lastpenalty + delay; 2046d7bcae4dSeric log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)", 2047c5acbec8Seric mta_route_to_text(r), (unsigned long long) sched - now, r->penalty); 2048c5acbec8Seric } else if (!(r->flags & ROUTE_KEEPALIVE)) { 2049c5acbec8Seric if (r->lastconn + max_seen_conndelay_route > now) 2050c5acbec8Seric sched = r->lastconn + max_seen_conndelay_route; 2051c5acbec8Seric if (r->lastdisc + max_seen_discdelay_route > now && 2052c5acbec8Seric r->lastdisc + max_seen_discdelay_route < sched) 2053c5acbec8Seric sched = r->lastdisc + max_seen_discdelay_route; 2054c5acbec8Seric 2055c5acbec8Seric if (sched > now) 2056c5acbec8Seric log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)", 2057c5acbec8Seric mta_route_to_text(r), (unsigned long long) sched - now); 2058c5acbec8Seric } 2059c5acbec8Seric 2060c5acbec8Seric if (sched > now) { 2061c5acbec8Seric r->flags |= ROUTE_RUNQ; 2062c5acbec8Seric runq_schedule(runq_route, sched, NULL, r); 2063c5acbec8Seric r->refcount++; 2064c5acbec8Seric return; 2065c5acbec8Seric } 2066c5acbec8Seric 2067c5acbec8Seric log_debug("debug: mta: ma_route_unref(): really discarding route %s", 2068c5acbec8Seric mta_route_to_text(r)); 2069c5acbec8Seric 207065c4fdfbSgilles SPLAY_REMOVE(mta_route_tree, &routes, r); 207165c4fdfbSgilles mta_source_unref(r->src); /* from constructor */ 207265c4fdfbSgilles mta_host_unref(r->dst); /* from constructor */ 2073edae97a5Seric free(r); 207465c4fdfbSgilles stat_decrement("mta.route", 1); 207565c4fdfbSgilles } 207665c4fdfbSgilles 207765c4fdfbSgilles static const char * 207865c4fdfbSgilles mta_route_to_text(struct mta_route *r) 207965c4fdfbSgilles { 208065c4fdfbSgilles static char buf[1024]; 208165c4fdfbSgilles 208265c4fdfbSgilles snprintf(buf, sizeof buf, "%s <-> %s", 208365c4fdfbSgilles mta_source_to_text(r->src), 208465c4fdfbSgilles mta_host_to_text(r->dst)); 208565c4fdfbSgilles 208665c4fdfbSgilles return (buf); 208765c4fdfbSgilles } 208865c4fdfbSgilles 208965c4fdfbSgilles static int 209065c4fdfbSgilles mta_route_cmp(const struct mta_route *a, const struct mta_route *b) 209165c4fdfbSgilles { 209265c4fdfbSgilles if (a->src < b->src) 209365c4fdfbSgilles return (-1); 209465c4fdfbSgilles if (a->src > b->src) 209565c4fdfbSgilles return (1); 209665c4fdfbSgilles 209765c4fdfbSgilles if (a->dst < b->dst) 209865c4fdfbSgilles return (-1); 209965c4fdfbSgilles if (a->dst > b->dst) 210065c4fdfbSgilles return (1); 2101ac5aa4b9Seric 2102ac5aa4b9Seric return (0); 2103ac5aa4b9Seric } 2104ac5aa4b9Seric 2105ac5aa4b9Seric SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp); 2106c5acbec8Seric 2107c5acbec8Seric 2108c5acbec8Seric /* hoststat errors are not critical, we do best effort */ 2109c5acbec8Seric void 2110c5acbec8Seric mta_hoststat_update(const char *host, const char *error) 2111c5acbec8Seric { 2112c5acbec8Seric struct hoststat *hs = NULL; 2113c5acbec8Seric char buf[SMTPD_MAXHOSTNAMELEN]; 2114c5acbec8Seric time_t tm; 2115c5acbec8Seric 2116c5acbec8Seric if (! lowercase(buf, host, sizeof buf)) 2117c5acbec8Seric return; 2118c5acbec8Seric 2119c5acbec8Seric tm = time(NULL); 2120c5acbec8Seric hs = dict_get(&hoststat, buf); 2121c5acbec8Seric if (hs == NULL) { 2122c5acbec8Seric hs = calloc(1, sizeof *hs); 2123c5acbec8Seric if (hs == NULL) 2124c5acbec8Seric return; 2125c5acbec8Seric tree_init(&hs->deferred); 2126c5acbec8Seric runq_schedule(runq_hoststat, tm+HOSTSTAT_EXPIRE_DELAY, NULL, hs); 2127c5acbec8Seric } 2128c5acbec8Seric strlcpy(hs->name, buf, sizeof hs->name); 2129c5acbec8Seric strlcpy(hs->error, error, sizeof hs->error); 2130c5acbec8Seric hs->tm = time(NULL); 2131c5acbec8Seric dict_set(&hoststat, buf, hs); 2132c5acbec8Seric 2133c5acbec8Seric runq_cancel(runq_hoststat, NULL, hs); 2134c5acbec8Seric runq_schedule(runq_hoststat, tm+HOSTSTAT_EXPIRE_DELAY, NULL, hs); 2135c5acbec8Seric } 2136c5acbec8Seric 2137c5acbec8Seric void 2138c5acbec8Seric mta_hoststat_cache(const char *host, uint64_t evpid) 2139c5acbec8Seric { 2140c5acbec8Seric struct hoststat *hs = NULL; 2141c5acbec8Seric char buf[SMTPD_MAXHOSTNAMELEN]; 2142c5acbec8Seric 2143c5acbec8Seric if (! lowercase(buf, host, sizeof buf)) 2144c5acbec8Seric return; 2145c5acbec8Seric 2146c5acbec8Seric hs = dict_get(&hoststat, buf); 2147c5acbec8Seric if (hs == NULL) 2148c5acbec8Seric return; 2149c5acbec8Seric 2150c5acbec8Seric tree_set(&hs->deferred, evpid, NULL); 2151c5acbec8Seric } 2152c5acbec8Seric 2153c5acbec8Seric void 2154c5acbec8Seric mta_hoststat_uncache(const char *host, uint64_t evpid) 2155c5acbec8Seric { 2156c5acbec8Seric struct hoststat *hs = NULL; 2157c5acbec8Seric char buf[SMTPD_MAXHOSTNAMELEN]; 2158c5acbec8Seric 2159c5acbec8Seric if (! lowercase(buf, host, sizeof buf)) 2160c5acbec8Seric return; 2161c5acbec8Seric 2162c5acbec8Seric hs = dict_get(&hoststat, buf); 2163c5acbec8Seric if (hs == NULL) 2164c5acbec8Seric return; 2165c5acbec8Seric 2166c5acbec8Seric tree_pop(&hs->deferred, evpid); 2167c5acbec8Seric } 2168c5acbec8Seric 2169c5acbec8Seric void 2170c5acbec8Seric mta_hoststat_reschedule(const char *host) 2171c5acbec8Seric { 2172c5acbec8Seric struct hoststat *hs = NULL; 2173c5acbec8Seric char buf[SMTPD_MAXHOSTNAMELEN]; 2174c5acbec8Seric uint64_t evpid; 2175c5acbec8Seric 2176c5acbec8Seric if (! lowercase(buf, host, sizeof buf)) 2177c5acbec8Seric return; 2178c5acbec8Seric 2179c5acbec8Seric hs = dict_get(&hoststat, buf); 2180c5acbec8Seric if (hs == NULL) 2181c5acbec8Seric return; 2182c5acbec8Seric 2183c5acbec8Seric while (tree_poproot(&hs->deferred, &evpid, NULL)) { 2184c5acbec8Seric m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1, 2185c5acbec8Seric &evpid, sizeof evpid); 2186c5acbec8Seric } 2187c5acbec8Seric } 2188c5acbec8Seric 2189c5acbec8Seric static void 2190c5acbec8Seric mta_hoststat_remove_entry(struct hoststat *hs) 2191c5acbec8Seric { 2192c5acbec8Seric while (tree_poproot(&hs->deferred, NULL, NULL)) 2193c5acbec8Seric ; 2194c5acbec8Seric dict_pop(&hoststat, hs->name); 2195c5acbec8Seric runq_cancel(runq_hoststat, NULL, hs); 2196c5acbec8Seric } 2197