| File: | src/usr.sbin/smtpd/smtpd/../mta.c |
| Warning: | line 1378, column 3 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: mta.c,v 1.240 2021/06/14 17:58:15 eric Exp $ */ | |||
| 2 | ||||
| 3 | /* | |||
| 4 | * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> | |||
| 5 | * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> | |||
| 6 | * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> | |||
| 7 | * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> | |||
| 8 | * | |||
| 9 | * Permission to use, copy, modify, and distribute this software for any | |||
| 10 | * purpose with or without fee is hereby granted, provided that the above | |||
| 11 | * copyright notice and this permission notice appear in all copies. | |||
| 12 | * | |||
| 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
| 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
| 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
| 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
| 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
| 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
| 19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| 20 | */ | |||
| 21 | ||||
| 22 | #include <inttypes.h> | |||
| 23 | #include <stdlib.h> | |||
| 24 | #include <string.h> | |||
| 25 | #include <tls.h> | |||
| 26 | ||||
| 27 | #include "smtpd.h" | |||
| 28 | #include "log.h" | |||
| 29 | #include "ssl.h" | |||
| 30 | ||||
| 31 | #define MAXERROR_PER_ROUTE4 4 | |||
| 32 | ||||
| 33 | #define DELAY_CHECK_SOURCE1 1 | |||
| 34 | #define DELAY_CHECK_SOURCE_SLOW10 10 | |||
| 35 | #define DELAY_CHECK_SOURCE_FAST0 0 | |||
| 36 | #define DELAY_CHECK_LIMIT5 5 | |||
| 37 | ||||
| 38 | #define DELAY_QUADRATIC1 1 | |||
| 39 | #define DELAY_ROUTE_BASE15 15 | |||
| 40 | #define DELAY_ROUTE_MAX3600 3600 | |||
| 41 | ||||
| 42 | #define RELAY_ONHOLD0x01 0x01 | |||
| 43 | #define RELAY_HOLDQ0x02 0x02 | |||
| 44 | ||||
| 45 | static void mta_setup_dispatcher(struct dispatcher *); | |||
| 46 | static void mta_handle_envelope(struct envelope *, const char *); | |||
| 47 | static void mta_query_smarthost(struct envelope *); | |||
| 48 | static void mta_on_smarthost(struct envelope *, const char *); | |||
| 49 | static void mta_query_mx(struct mta_relay *); | |||
| 50 | static void mta_query_secret(struct mta_relay *); | |||
| 51 | static void mta_query_preference(struct mta_relay *); | |||
| 52 | static void mta_query_source(struct mta_relay *); | |||
| 53 | static void mta_on_mx(void *, void *, void *); | |||
| 54 | static void mta_on_secret(struct mta_relay *, const char *); | |||
| 55 | static void mta_on_preference(struct mta_relay *, int); | |||
| 56 | static void mta_on_source(struct mta_relay *, struct mta_source *); | |||
| 57 | static void mta_on_timeout(struct runq *, void *); | |||
| 58 | static void mta_connect(struct mta_connector *); | |||
| 59 | static void mta_route_enable(struct mta_route *); | |||
| 60 | static void mta_route_disable(struct mta_route *, int, int); | |||
| 61 | static void mta_drain(struct mta_relay *); | |||
| 62 | static void mta_delivery_flush_event(int, short, void *); | |||
| 63 | static void mta_flush(struct mta_relay *, int, const char *); | |||
| 64 | static struct mta_route *mta_find_route(struct mta_connector *, time_t, int*, | |||
| 65 | time_t*, struct mta_mx **); | |||
| 66 | static void mta_log(const struct mta_envelope *, const char *, const char *, | |||
| 67 | const char *, const char *); | |||
| 68 | ||||
| 69 | SPLAY_HEAD(mta_relay_tree, mta_relay)struct mta_relay_tree { struct mta_relay *sph_root; }; | |||
| 70 | static struct mta_relay *mta_relay(struct envelope *, struct relayhost *); | |||
| 71 | static void mta_relay_ref(struct mta_relay *); | |||
| 72 | static void mta_relay_unref(struct mta_relay *); | |||
| 73 | static void mta_relay_show(struct mta_relay *, struct mproc *, uint32_t, time_t); | |||
| 74 | static int mta_relay_cmp(const struct mta_relay *, const struct mta_relay *); | |||
| 75 | SPLAY_PROTOTYPE(mta_relay_tree, mta_relay, entry, mta_relay_cmp)void mta_relay_tree_SPLAY(struct mta_relay_tree *, struct mta_relay *); void mta_relay_tree_SPLAY_MINMAX(struct mta_relay_tree * , int); struct mta_relay *mta_relay_tree_SPLAY_INSERT(struct mta_relay_tree *, struct mta_relay *); struct mta_relay *mta_relay_tree_SPLAY_REMOVE (struct mta_relay_tree *, struct mta_relay *); static __attribute__ ((__unused__)) __inline struct mta_relay * mta_relay_tree_SPLAY_FIND (struct mta_relay_tree *head, struct mta_relay *elm) { if ((( head)->sph_root == ((void*)0))) return(((void*)0)); mta_relay_tree_SPLAY (head, elm); if ((mta_relay_cmp)(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void*)0)); } static __attribute__((__unused__)) __inline struct mta_relay * mta_relay_tree_SPLAY_NEXT (struct mta_relay_tree *head, struct mta_relay *elm) { mta_relay_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_relay * mta_relay_tree_SPLAY_MIN_MAX(struct mta_relay_tree *head, int val) { mta_relay_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | |||
| 76 | ||||
| 77 | SPLAY_HEAD(mta_host_tree, mta_host)struct mta_host_tree { struct mta_host *sph_root; }; | |||
| 78 | static struct mta_host *mta_host(const struct sockaddr *); | |||
| 79 | static void mta_host_ref(struct mta_host *); | |||
| 80 | static void mta_host_unref(struct mta_host *); | |||
| 81 | static int mta_host_cmp(const struct mta_host *, const struct mta_host *); | |||
| 82 | SPLAY_PROTOTYPE(mta_host_tree, mta_host, entry, mta_host_cmp)void mta_host_tree_SPLAY(struct mta_host_tree *, struct mta_host *); void mta_host_tree_SPLAY_MINMAX(struct mta_host_tree *, int ); struct mta_host *mta_host_tree_SPLAY_INSERT(struct mta_host_tree *, struct mta_host *); struct mta_host *mta_host_tree_SPLAY_REMOVE (struct mta_host_tree *, struct mta_host *); static __attribute__ ((__unused__)) __inline struct mta_host * mta_host_tree_SPLAY_FIND (struct mta_host_tree *head, struct mta_host *elm) { if (((head )->sph_root == ((void*)0))) return(((void*)0)); mta_host_tree_SPLAY (head, elm); if ((mta_host_cmp)(elm, (head)->sph_root) == 0 ) return (head->sph_root); return (((void*)0)); } static __attribute__ ((__unused__)) __inline struct mta_host * mta_host_tree_SPLAY_NEXT (struct mta_host_tree *head, struct mta_host *elm) { mta_host_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_host * mta_host_tree_SPLAY_MIN_MAX(struct mta_host_tree *head, int val) { mta_host_tree_SPLAY_MINMAX(head , val); return ((head)->sph_root); }; | |||
| 83 | ||||
| 84 | SPLAY_HEAD(mta_domain_tree, mta_domain)struct mta_domain_tree { struct mta_domain *sph_root; }; | |||
| 85 | static struct mta_domain *mta_domain(char *, int); | |||
| 86 | #if 0 | |||
| 87 | static void mta_domain_ref(struct mta_domain *); | |||
| 88 | #endif | |||
| 89 | static void mta_domain_unref(struct mta_domain *); | |||
| 90 | static int mta_domain_cmp(const struct mta_domain *, const struct mta_domain *); | |||
| 91 | SPLAY_PROTOTYPE(mta_domain_tree, mta_domain, entry, mta_domain_cmp)void mta_domain_tree_SPLAY(struct mta_domain_tree *, struct mta_domain *); void mta_domain_tree_SPLAY_MINMAX(struct mta_domain_tree *, int); struct mta_domain *mta_domain_tree_SPLAY_INSERT(struct mta_domain_tree *, struct mta_domain *); struct mta_domain * mta_domain_tree_SPLAY_REMOVE(struct mta_domain_tree *, struct mta_domain *); static __attribute__((__unused__)) __inline struct mta_domain * mta_domain_tree_SPLAY_FIND(struct mta_domain_tree *head, struct mta_domain *elm) { if (((head)->sph_root == ((void*)0))) return(((void*)0)); mta_domain_tree_SPLAY(head, elm); if ((mta_domain_cmp)(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void*)0)); } static __attribute__ ((__unused__)) __inline struct mta_domain * mta_domain_tree_SPLAY_NEXT (struct mta_domain_tree *head, struct mta_domain *elm) { mta_domain_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_domain * mta_domain_tree_SPLAY_MIN_MAX (struct mta_domain_tree *head, int val) { mta_domain_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | |||
| 92 | ||||
| 93 | SPLAY_HEAD(mta_source_tree, mta_source)struct mta_source_tree { struct mta_source *sph_root; }; | |||
| 94 | static struct mta_source *mta_source(const struct sockaddr *); | |||
| 95 | static void mta_source_ref(struct mta_source *); | |||
| 96 | static void mta_source_unref(struct mta_source *); | |||
| 97 | static const char *mta_source_to_text(struct mta_source *); | |||
| 98 | static int mta_source_cmp(const struct mta_source *, const struct mta_source *); | |||
| 99 | SPLAY_PROTOTYPE(mta_source_tree, mta_source, entry, mta_source_cmp)void mta_source_tree_SPLAY(struct mta_source_tree *, struct mta_source *); void mta_source_tree_SPLAY_MINMAX(struct mta_source_tree *, int); struct mta_source *mta_source_tree_SPLAY_INSERT(struct mta_source_tree *, struct mta_source *); struct mta_source * mta_source_tree_SPLAY_REMOVE(struct mta_source_tree *, struct mta_source *); static __attribute__((__unused__)) __inline struct mta_source * mta_source_tree_SPLAY_FIND(struct mta_source_tree *head, struct mta_source *elm) { if (((head)->sph_root == ((void*)0))) return(((void*)0)); mta_source_tree_SPLAY(head, elm); if ((mta_source_cmp)(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void*)0)); } static __attribute__ ((__unused__)) __inline struct mta_source * mta_source_tree_SPLAY_NEXT (struct mta_source_tree *head, struct mta_source *elm) { mta_source_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_source * mta_source_tree_SPLAY_MIN_MAX (struct mta_source_tree *head, int val) { mta_source_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | |||
| 100 | ||||
| 101 | static struct mta_connector *mta_connector(struct mta_relay *, | |||
| 102 | struct mta_source *); | |||
| 103 | static void mta_connector_free(struct mta_connector *); | |||
| 104 | static const char *mta_connector_to_text(struct mta_connector *); | |||
| 105 | ||||
| 106 | SPLAY_HEAD(mta_route_tree, mta_route)struct mta_route_tree { struct mta_route *sph_root; }; | |||
| 107 | static struct mta_route *mta_route(struct mta_source *, struct mta_host *); | |||
| 108 | static void mta_route_ref(struct mta_route *); | |||
| 109 | static void mta_route_unref(struct mta_route *); | |||
| 110 | static const char *mta_route_to_text(struct mta_route *); | |||
| 111 | static int mta_route_cmp(const struct mta_route *, const struct mta_route *); | |||
| 112 | SPLAY_PROTOTYPE(mta_route_tree, mta_route, entry, mta_route_cmp)void mta_route_tree_SPLAY(struct mta_route_tree *, struct mta_route *); void mta_route_tree_SPLAY_MINMAX(struct mta_route_tree * , int); struct mta_route *mta_route_tree_SPLAY_INSERT(struct mta_route_tree *, struct mta_route *); struct mta_route *mta_route_tree_SPLAY_REMOVE (struct mta_route_tree *, struct mta_route *); static __attribute__ ((__unused__)) __inline struct mta_route * mta_route_tree_SPLAY_FIND (struct mta_route_tree *head, struct mta_route *elm) { if ((( head)->sph_root == ((void*)0))) return(((void*)0)); mta_route_tree_SPLAY (head, elm); if ((mta_route_cmp)(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void*)0)); } static __attribute__((__unused__)) __inline struct mta_route * mta_route_tree_SPLAY_NEXT (struct mta_route_tree *head, struct mta_route *elm) { mta_route_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_route * mta_route_tree_SPLAY_MIN_MAX(struct mta_route_tree *head, int val) { mta_route_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | |||
| 113 | ||||
| 114 | struct mta_block { | |||
| 115 | SPLAY_ENTRY(mta_block)struct { struct mta_block *spe_left; struct mta_block *spe_right ; } entry; | |||
| 116 | struct mta_source *source; | |||
| 117 | char *domain; | |||
| 118 | }; | |||
| 119 | ||||
| 120 | SPLAY_HEAD(mta_block_tree, mta_block)struct mta_block_tree { struct mta_block *sph_root; }; | |||
| 121 | void mta_block(struct mta_source *, char *); | |||
| 122 | void mta_unblock(struct mta_source *, char *); | |||
| 123 | int mta_is_blocked(struct mta_source *, char *); | |||
| 124 | static int mta_block_cmp(const struct mta_block *, const struct mta_block *); | |||
| 125 | SPLAY_PROTOTYPE(mta_block_tree, mta_block, entry, mta_block_cmp)void mta_block_tree_SPLAY(struct mta_block_tree *, struct mta_block *); void mta_block_tree_SPLAY_MINMAX(struct mta_block_tree * , int); struct mta_block *mta_block_tree_SPLAY_INSERT(struct mta_block_tree *, struct mta_block *); struct mta_block *mta_block_tree_SPLAY_REMOVE (struct mta_block_tree *, struct mta_block *); static __attribute__ ((__unused__)) __inline struct mta_block * mta_block_tree_SPLAY_FIND (struct mta_block_tree *head, struct mta_block *elm) { if ((( head)->sph_root == ((void*)0))) return(((void*)0)); mta_block_tree_SPLAY (head, elm); if ((mta_block_cmp)(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void*)0)); } static __attribute__((__unused__)) __inline struct mta_block * mta_block_tree_SPLAY_NEXT (struct mta_block_tree *head, struct mta_block *elm) { mta_block_tree_SPLAY (head, elm); if ((elm)->entry.spe_right != ((void*)0)) { elm = (elm)->entry.spe_right; while ((elm)->entry.spe_left != ((void*)0)) { elm = (elm)->entry.spe_left; } } else elm = ((void*)0); return (elm); } static __attribute__((__unused__ )) __inline struct mta_block * mta_block_tree_SPLAY_MIN_MAX(struct mta_block_tree *head, int val) { mta_block_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | |||
| 126 | ||||
| 127 | /* | |||
| 128 | * This function is not publicy exported because it is a hack until libtls | |||
| 129 | * has a proper privsep setup | |||
| 130 | */ | |||
| 131 | void tls_config_use_fake_private_key(struct tls_config *config); | |||
| 132 | ||||
| 133 | static struct mta_relay_tree relays; | |||
| 134 | static struct mta_domain_tree domains; | |||
| 135 | static struct mta_host_tree hosts; | |||
| 136 | static struct mta_source_tree sources; | |||
| 137 | static struct mta_route_tree routes; | |||
| 138 | static struct mta_block_tree blocks; | |||
| 139 | ||||
| 140 | static struct tree wait_mx; | |||
| 141 | static struct tree wait_preference; | |||
| 142 | static struct tree wait_secret; | |||
| 143 | static struct tree wait_smarthost; | |||
| 144 | static struct tree wait_source; | |||
| 145 | static struct tree flush_evp; | |||
| 146 | static struct event ev_flush_evp; | |||
| 147 | ||||
| 148 | static struct runq *runq_relay; | |||
| 149 | static struct runq *runq_connector; | |||
| 150 | static struct runq *runq_route; | |||
| 151 | static struct runq *runq_hoststat; | |||
| 152 | ||||
| 153 | static time_t max_seen_conndelay_route; | |||
| 154 | static time_t max_seen_discdelay_route; | |||
| 155 | ||||
| 156 | #define HOSTSTAT_EXPIRE_DELAY(4 * 3600) (4 * 3600) | |||
| 157 | struct hoststat { | |||
| 158 | char name[HOST_NAME_MAX255+1]; | |||
| 159 | time_t tm; | |||
| 160 | char error[LINE_MAX2048]; | |||
| 161 | struct tree deferred; | |||
| 162 | }; | |||
| 163 | static struct dict hoststat; | |||
| 164 | ||||
| 165 | void mta_hoststat_update(const char *, const char *); | |||
| 166 | void mta_hoststat_cache(const char *, uint64_t); | |||
| 167 | void mta_hoststat_uncache(const char *, uint64_t); | |||
| 168 | void mta_hoststat_reschedule(const char *); | |||
| 169 | static void mta_hoststat_remove_entry(struct hoststat *); | |||
| 170 | ||||
| 171 | void | |||
| 172 | mta_imsg(struct mproc *p, struct imsg *imsg) | |||
| 173 | { | |||
| 174 | struct mta_relay *relay; | |||
| 175 | struct mta_domain *domain; | |||
| 176 | struct mta_host *host; | |||
| 177 | struct mta_route *route; | |||
| 178 | struct mta_block *block; | |||
| 179 | struct mta_mx *mx, *imx; | |||
| 180 | struct mta_source *source; | |||
| 181 | struct hoststat *hs; | |||
| 182 | struct sockaddr_storage ss; | |||
| 183 | struct envelope evp, *e; | |||
| 184 | struct msg m; | |||
| 185 | const char *secret; | |||
| 186 | const char *hostname; | |||
| 187 | const char *dom; | |||
| 188 | const char *smarthost; | |||
| 189 | uint64_t reqid; | |||
| 190 | time_t t; | |||
| 191 | char buf[LINE_MAX2048]; | |||
| 192 | int dnserror, preference, v, status; | |||
| 193 | void *iter; | |||
| 194 | uint64_t u64; | |||
| 195 | ||||
| 196 | switch (imsg->hdr.type) { | |||
| 197 | case IMSG_QUEUE_TRANSFER: | |||
| 198 | m_msg(&m, imsg); | |||
| 199 | m_get_envelope(&m, &evp); | |||
| 200 | m_end(&m); | |||
| 201 | mta_handle_envelope(&evp, NULL((void*)0)); | |||
| 202 | return; | |||
| 203 | ||||
| 204 | case IMSG_MTA_OPEN_MESSAGE: | |||
| 205 | mta_session_imsg(p, imsg); | |||
| 206 | return; | |||
| 207 | ||||
| 208 | case IMSG_MTA_LOOKUP_CREDENTIALS: | |||
| 209 | m_msg(&m, imsg); | |||
| 210 | m_get_id(&m, &reqid); | |||
| 211 | m_get_string(&m, &secret); | |||
| 212 | m_end(&m); | |||
| 213 | relay = tree_xpop(&wait_secret, reqid); | |||
| 214 | mta_on_secret(relay, secret[0] ? secret : NULL((void*)0)); | |||
| 215 | return; | |||
| 216 | ||||
| 217 | case IMSG_MTA_LOOKUP_SOURCE: | |||
| 218 | m_msg(&m, imsg); | |||
| 219 | m_get_id(&m, &reqid); | |||
| 220 | m_get_int(&m, &status); | |||
| 221 | if (status == LKA_OK) | |||
| 222 | m_get_sockaddr(&m, (struct sockaddr*)&ss); | |||
| 223 | m_end(&m); | |||
| 224 | ||||
| 225 | relay = tree_xpop(&wait_source, reqid); | |||
| 226 | mta_on_source(relay, (status == LKA_OK) ? | |||
| 227 | mta_source((struct sockaddr *)&ss) : NULL((void*)0)); | |||
| 228 | return; | |||
| 229 | ||||
| 230 | case IMSG_MTA_LOOKUP_SMARTHOST: | |||
| 231 | m_msg(&m, imsg); | |||
| 232 | m_get_id(&m, &reqid); | |||
| 233 | m_get_int(&m, &status); | |||
| 234 | smarthost = NULL((void*)0); | |||
| 235 | if (status == LKA_OK) | |||
| 236 | m_get_string(&m, &smarthost); | |||
| 237 | m_end(&m); | |||
| 238 | ||||
| 239 | e = tree_xpop(&wait_smarthost, reqid); | |||
| 240 | mta_on_smarthost(e, smarthost); | |||
| 241 | return; | |||
| 242 | ||||
| 243 | case IMSG_MTA_LOOKUP_HELO: | |||
| 244 | mta_session_imsg(p, imsg); | |||
| 245 | return; | |||
| 246 | ||||
| 247 | case IMSG_MTA_DNS_HOST: | |||
| 248 | m_msg(&m, imsg); | |||
| 249 | m_get_id(&m, &reqid); | |||
| 250 | m_get_string(&m, &hostname); | |||
| 251 | m_get_sockaddr(&m, (struct sockaddr*)&ss); | |||
| 252 | m_get_int(&m, &preference); | |||
| 253 | m_end(&m); | |||
| 254 | domain = tree_xget(&wait_mx, reqid); | |||
| 255 | mx = xcalloc(1, sizeof *mx); | |||
| 256 | mx->mxname = xstrdup(hostname); | |||
| 257 | mx->host = mta_host((struct sockaddr*)&ss); | |||
| 258 | mx->preference = preference; | |||
| 259 | TAILQ_FOREACH(imx, &domain->mxs, entry)for((imx) = ((&domain->mxs)->tqh_first); (imx) != ( (void*)0); (imx) = ((imx)->entry.tqe_next)) { | |||
| 260 | if (imx->preference > mx->preference) { | |||
| 261 | TAILQ_INSERT_BEFORE(imx, mx, entry)do { (mx)->entry.tqe_prev = (imx)->entry.tqe_prev; (mx) ->entry.tqe_next = (imx); *(imx)->entry.tqe_prev = (mx) ; (imx)->entry.tqe_prev = &(mx)->entry.tqe_next; } while (0); | |||
| 262 | return; | |||
| 263 | } | |||
| 264 | } | |||
| 265 | TAILQ_INSERT_TAIL(&domain->mxs, mx, entry)do { (mx)->entry.tqe_next = ((void*)0); (mx)->entry.tqe_prev = (&domain->mxs)->tqh_last; *(&domain->mxs) ->tqh_last = (mx); (&domain->mxs)->tqh_last = & (mx)->entry.tqe_next; } while (0); | |||
| 266 | return; | |||
| 267 | ||||
| 268 | case IMSG_MTA_DNS_HOST_END: | |||
| 269 | m_msg(&m, imsg); | |||
| 270 | m_get_id(&m, &reqid); | |||
| 271 | m_get_int(&m, &dnserror); | |||
| 272 | m_end(&m); | |||
| 273 | domain = tree_xpop(&wait_mx, reqid); | |||
| 274 | domain->mxstatus = dnserror; | |||
| 275 | if (domain->mxstatus == DNS_OK) { | |||
| 276 | log_debug("debug: MXs for domain %s:", | |||
| 277 | domain->name); | |||
| 278 | TAILQ_FOREACH(mx, &domain->mxs, entry)for((mx) = ((&domain->mxs)->tqh_first); (mx) != ((void *)0); (mx) = ((mx)->entry.tqe_next)) | |||
| 279 | log_debug(" %s preference %d", | |||
| 280 | sa_to_text(mx->host->sa), | |||
| 281 | mx->preference); | |||
| 282 | } | |||
| 283 | else { | |||
| 284 | log_debug("debug: Failed MX query for %s:", | |||
| 285 | domain->name); | |||
| 286 | } | |||
| 287 | domain->lastmxquery = time(NULL((void*)0)); | |||
| 288 | waitq_run(&domain->mxs, domain); | |||
| 289 | return; | |||
| 290 | ||||
| 291 | case IMSG_MTA_DNS_MX_PREFERENCE: | |||
| 292 | m_msg(&m, imsg); | |||
| 293 | m_get_id(&m, &reqid); | |||
| 294 | m_get_int(&m, &dnserror); | |||
| 295 | if (dnserror == 0) | |||
| 296 | m_get_int(&m, &preference); | |||
| 297 | m_end(&m); | |||
| 298 | ||||
| 299 | relay = tree_xpop(&wait_preference, reqid); | |||
| 300 | if (dnserror) { | |||
| 301 | log_warnx("warn: Couldn't find backup " | |||
| 302 | "preference for %s: error %d", | |||
| 303 | mta_relay_to_text(relay), dnserror); | |||
| 304 | preference = INT_MAX2147483647; | |||
| 305 | } | |||
| 306 | mta_on_preference(relay, preference); | |||
| 307 | return; | |||
| 308 | ||||
| 309 | case IMSG_CTL_RESUME_ROUTE: | |||
| 310 | u64 = *((uint64_t *)imsg->data); | |||
| 311 | if (u64) | |||
| 312 | log_debug("resuming route: %llu", | |||
| 313 | (unsigned long long)u64); | |||
| 314 | else | |||
| 315 | log_debug("resuming all routes"); | |||
| 316 | SPLAY_FOREACH(route, mta_route_tree, &routes)for ((route) = (((&routes)->sph_root == ((void*)0)) ? ( (void*)0) : mta_route_tree_SPLAY_MIN_MAX(&routes, -1)); ( route) != ((void*)0); (route) = mta_route_tree_SPLAY_NEXT(& routes, route)) { | |||
| 317 | if (u64 && route->id != u64) | |||
| 318 | continue; | |||
| 319 | ||||
| 320 | if (route->flags & ROUTE_DISABLED0xf0) { | |||
| 321 | log_info("smtp-out: Enabling route %s per admin request", | |||
| 322 | mta_route_to_text(route)); | |||
| 323 | if (!runq_cancel(runq_route, route)) { | |||
| 324 | log_warnx("warn: route not on runq"); | |||
| 325 | fatalx("exiting"); | |||
| 326 | } | |||
| 327 | route->flags &= ~ROUTE_DISABLED0xf0; | |||
| 328 | route->flags |= ROUTE_NEW0x01; | |||
| 329 | route->nerror = 0; | |||
| 330 | route->penalty = 0; | |||
| 331 | mta_route_unref(route); /* from mta_route_disable */ | |||
| 332 | } | |||
| 333 | ||||
| 334 | if (u64) | |||
| 335 | break; | |||
| 336 | } | |||
| 337 | return; | |||
| 338 | ||||
| 339 | case IMSG_CTL_MTA_SHOW_HOSTS: | |||
| 340 | t = time(NULL((void*)0)); | |||
| 341 | SPLAY_FOREACH(host, mta_host_tree, &hosts)for ((host) = (((&hosts)->sph_root == ((void*)0)) ? (( void*)0) : mta_host_tree_SPLAY_MIN_MAX(&hosts, -1)); (host ) != ((void*)0); (host) = mta_host_tree_SPLAY_NEXT(&hosts , host)) { | |||
| 342 | (void)snprintf(buf, sizeof(buf), | |||
| 343 | "%s %s refcount=%d nconn=%zu lastconn=%s", | |||
| 344 | sockaddr_to_text(host->sa), | |||
| 345 | host->ptrname, | |||
| 346 | host->refcount, | |||
| 347 | host->nconn, | |||
| 348 | host->lastconn ? duration_to_text(t - host->lastconn) : "-"); | |||
| 349 | m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, | |||
| 350 | imsg->hdr.peerid, 0, -1, | |||
| 351 | buf, strlen(buf) + 1); | |||
| 352 | } | |||
| 353 | m_compose(p, IMSG_CTL_MTA_SHOW_HOSTS, imsg->hdr.peerid, | |||
| 354 | 0, -1, NULL((void*)0), 0); | |||
| 355 | return; | |||
| 356 | ||||
| 357 | case IMSG_CTL_MTA_SHOW_RELAYS: | |||
| 358 | t = time(NULL((void*)0)); | |||
| 359 | SPLAY_FOREACH(relay, mta_relay_tree, &relays)for ((relay) = (((&relays)->sph_root == ((void*)0)) ? ( (void*)0) : mta_relay_tree_SPLAY_MIN_MAX(&relays, -1)); ( relay) != ((void*)0); (relay) = mta_relay_tree_SPLAY_NEXT(& relays, relay)) | |||
| 360 | mta_relay_show(relay, p, imsg->hdr.peerid, t); | |||
| 361 | m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, imsg->hdr.peerid, | |||
| 362 | 0, -1, NULL((void*)0), 0); | |||
| 363 | return; | |||
| 364 | ||||
| 365 | case IMSG_CTL_MTA_SHOW_ROUTES: | |||
| 366 | SPLAY_FOREACH(route, mta_route_tree, &routes)for ((route) = (((&routes)->sph_root == ((void*)0)) ? ( (void*)0) : mta_route_tree_SPLAY_MIN_MAX(&routes, -1)); ( route) != ((void*)0); (route) = mta_route_tree_SPLAY_NEXT(& routes, route)) { | |||
| 367 | v = runq_pending(runq_route, route, &t); | |||
| 368 | (void)snprintf(buf, sizeof(buf), | |||
| 369 | "%llu. %s %c%c%c%c nconn=%zu nerror=%d penalty=%d timeout=%s", | |||
| 370 | (unsigned long long)route->id, | |||
| 371 | mta_route_to_text(route), | |||
| 372 | route->flags & ROUTE_NEW0x01 ? 'N' : '-', | |||
| 373 | route->flags & ROUTE_DISABLED0xf0 ? 'D' : '-', | |||
| 374 | route->flags & ROUTE_RUNQ0x02 ? 'Q' : '-', | |||
| 375 | route->flags & ROUTE_KEEPALIVE0x04 ? 'K' : '-', | |||
| 376 | route->nconn, | |||
| 377 | route->nerror, | |||
| 378 | route->penalty, | |||
| 379 | v ? duration_to_text(t - time(NULL((void*)0))) : "-"); | |||
| 380 | m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, | |||
| 381 | imsg->hdr.peerid, 0, -1, | |||
| 382 | buf, strlen(buf) + 1); | |||
| 383 | } | |||
| 384 | m_compose(p, IMSG_CTL_MTA_SHOW_ROUTES, imsg->hdr.peerid, | |||
| 385 | 0, -1, NULL((void*)0), 0); | |||
| 386 | return; | |||
| 387 | ||||
| 388 | case IMSG_CTL_MTA_SHOW_HOSTSTATS: | |||
| 389 | iter = NULL((void*)0); | |||
| 390 | while (dict_iter(&hoststat, &iter, &hostname, | |||
| 391 | (void **)&hs)) { | |||
| 392 | (void)snprintf(buf, sizeof(buf), | |||
| 393 | "%s|%llu|%s", | |||
| 394 | hostname, (unsigned long long) hs->tm, | |||
| 395 | hs->error); | |||
| 396 | m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, | |||
| 397 | imsg->hdr.peerid, 0, -1, | |||
| 398 | buf, strlen(buf) + 1); | |||
| 399 | } | |||
| 400 | m_compose(p, IMSG_CTL_MTA_SHOW_HOSTSTATS, | |||
| 401 | imsg->hdr.peerid, | |||
| 402 | 0, -1, NULL((void*)0), 0); | |||
| 403 | return; | |||
| 404 | ||||
| 405 | case IMSG_CTL_MTA_BLOCK: | |||
| 406 | m_msg(&m, imsg); | |||
| 407 | m_get_sockaddr(&m, (struct sockaddr*)&ss); | |||
| 408 | m_get_string(&m, &dom); | |||
| 409 | m_end(&m); | |||
| 410 | source = mta_source((struct sockaddr*)&ss); | |||
| 411 | if (*dom != '\0') { | |||
| 412 | if (!(strlcpy(buf, dom, sizeof(buf)) | |||
| 413 | >= sizeof(buf))) | |||
| 414 | mta_block(source, buf); | |||
| 415 | } | |||
| 416 | else | |||
| 417 | mta_block(source, NULL((void*)0)); | |||
| 418 | mta_source_unref(source); | |||
| 419 | m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL((void*)0), 0); | |||
| 420 | return; | |||
| 421 | ||||
| 422 | case IMSG_CTL_MTA_UNBLOCK: | |||
| 423 | m_msg(&m, imsg); | |||
| 424 | m_get_sockaddr(&m, (struct sockaddr*)&ss); | |||
| 425 | m_get_string(&m, &dom); | |||
| 426 | m_end(&m); | |||
| 427 | source = mta_source((struct sockaddr*)&ss); | |||
| 428 | if (*dom != '\0') { | |||
| 429 | if (!(strlcpy(buf, dom, sizeof(buf)) | |||
| 430 | >= sizeof(buf))) | |||
| 431 | mta_unblock(source, buf); | |||
| 432 | } | |||
| 433 | else | |||
| 434 | mta_unblock(source, NULL((void*)0)); | |||
| 435 | mta_source_unref(source); | |||
| 436 | m_compose(p, IMSG_CTL_OK, imsg->hdr.peerid, 0, -1, NULL((void*)0), 0); | |||
| 437 | return; | |||
| 438 | ||||
| 439 | case IMSG_CTL_MTA_SHOW_BLOCK: | |||
| 440 | SPLAY_FOREACH(block, mta_block_tree, &blocks)for ((block) = (((&blocks)->sph_root == ((void*)0)) ? ( (void*)0) : mta_block_tree_SPLAY_MIN_MAX(&blocks, -1)); ( block) != ((void*)0); (block) = mta_block_tree_SPLAY_NEXT(& blocks, block)) { | |||
| 441 | (void)snprintf(buf, sizeof(buf), "%s -> %s", | |||
| 442 | mta_source_to_text(block->source), | |||
| 443 | block->domain ? block->domain : "*"); | |||
| 444 | m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, | |||
| 445 | imsg->hdr.peerid, 0, -1, buf, strlen(buf) + 1); | |||
| 446 | } | |||
| 447 | m_compose(p, IMSG_CTL_MTA_SHOW_BLOCK, imsg->hdr.peerid, | |||
| 448 | 0, -1, NULL((void*)0), 0); | |||
| 449 | return; | |||
| 450 | } | |||
| 451 | ||||
| 452 | fatalx("mta_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); | |||
| 453 | } | |||
| 454 | ||||
| 455 | void | |||
| 456 | mta_postfork(void) | |||
| 457 | { | |||
| 458 | struct dispatcher *dispatcher; | |||
| 459 | const char *key; | |||
| 460 | void *iter; | |||
| 461 | ||||
| 462 | iter = NULL((void*)0); | |||
| 463 | while (dict_iter(env->sc_dispatchers, &iter, &key, (void **)&dispatcher)) { | |||
| 464 | log_debug("%s: %s", __func__, key); | |||
| 465 | mta_setup_dispatcher(dispatcher); | |||
| 466 | } | |||
| 467 | } | |||
| 468 | ||||
| 469 | static void | |||
| 470 | mta_setup_dispatcher(struct dispatcher *dispatcher) | |||
| 471 | { | |||
| 472 | struct dispatcher_remote *remote; | |||
| 473 | static const char *dheparams[] = { "none", "auto", "legacy" }; | |||
| 474 | struct tls_config *config; | |||
| 475 | struct pki *pki; | |||
| 476 | struct ca *ca; | |||
| 477 | const char *ciphers; | |||
| 478 | uint32_t protos; | |||
| 479 | ||||
| 480 | if (dispatcher->type != DISPATCHER_REMOTE) | |||
| 481 | return; | |||
| 482 | ||||
| 483 | remote = &dispatcher->u.remote; | |||
| 484 | ||||
| 485 | if ((config = tls_config_new()) == NULL((void*)0)) | |||
| 486 | fatal("smtpd: tls_config_new"); | |||
| 487 | ||||
| 488 | ciphers = env->sc_tls_ciphers; | |||
| 489 | if (remote->tls_ciphers) | |||
| 490 | ciphers = remote->tls_ciphers; | |||
| 491 | if (ciphers && tls_config_set_ciphers(config, ciphers) == -1) | |||
| 492 | fatal("%s", tls_config_error(config)); | |||
| 493 | ||||
| 494 | if (remote->tls_protocols) { | |||
| 495 | if (tls_config_parse_protocols(&protos, | |||
| 496 | remote->tls_protocols) == -1) | |||
| 497 | fatal("failed to parse protocols \"%s\"", | |||
| 498 | remote->tls_protocols); | |||
| 499 | if (tls_config_set_protocols(config, protos) == -1) | |||
| 500 | fatal("%s", tls_config_error(config)); | |||
| 501 | } | |||
| 502 | ||||
| 503 | if (remote->pki) { | |||
| 504 | pki = dict_get(env->sc_pki_dict, remote->pki); | |||
| 505 | if (pki == NULL((void*)0)) | |||
| 506 | fatal("client pki \"%s\" not found ", remote->pki); | |||
| 507 | ||||
| 508 | tls_config_set_dheparams(config, dheparams[pki->pki_dhe]); | |||
| 509 | tls_config_use_fake_private_key(config); | |||
| 510 | if (tls_config_set_keypair_mem(config, pki->pki_cert, | |||
| 511 | pki->pki_cert_len, NULL((void*)0), 0) == -1) | |||
| 512 | fatal("tls_config_set_keypair_mem"); | |||
| 513 | } | |||
| 514 | ||||
| 515 | if (remote->ca) { | |||
| 516 | ca = dict_get(env->sc_ca_dict, remote->ca); | |||
| 517 | if (tls_config_set_ca_mem(config, ca->ca_cert, ca->ca_cert_len) | |||
| 518 | == -1) | |||
| 519 | fatal("tls_config_set_ca_mem"); | |||
| 520 | } | |||
| 521 | else if (tls_config_set_ca_file(config, tls_default_ca_cert_file()) | |||
| 522 | == -1) | |||
| 523 | fatal("tls_config_set_ca_file"); | |||
| 524 | ||||
| 525 | if (remote->tls_noverify) { | |||
| 526 | tls_config_insecure_noverifycert(config); | |||
| 527 | tls_config_insecure_noverifyname(config); | |||
| 528 | tls_config_insecure_noverifytime(config); | |||
| 529 | } | |||
| 530 | else | |||
| 531 | tls_config_verify(config); | |||
| 532 | ||||
| 533 | remote->tls_config = config; | |||
| 534 | } | |||
| 535 | ||||
| 536 | void | |||
| 537 | mta_postprivdrop(void) | |||
| 538 | { | |||
| 539 | SPLAY_INIT(&relays)do { (&relays)->sph_root = ((void*)0); } while (0); | |||
| 540 | SPLAY_INIT(&domains)do { (&domains)->sph_root = ((void*)0); } while (0); | |||
| 541 | SPLAY_INIT(&hosts)do { (&hosts)->sph_root = ((void*)0); } while (0); | |||
| 542 | SPLAY_INIT(&sources)do { (&sources)->sph_root = ((void*)0); } while (0); | |||
| 543 | SPLAY_INIT(&routes)do { (&routes)->sph_root = ((void*)0); } while (0); | |||
| 544 | SPLAY_INIT(&blocks)do { (&blocks)->sph_root = ((void*)0); } while (0); | |||
| 545 | ||||
| 546 | tree_init(&wait_secret)do { do { (&((&wait_secret)->tree))->sph_root = ((void*)0); } while (0); (&wait_secret)->count = 0; } while(0); | |||
| 547 | tree_init(&wait_smarthost)do { do { (&((&wait_smarthost)->tree))->sph_root = ((void*)0); } while (0); (&wait_smarthost)->count = 0; } while(0); | |||
| 548 | tree_init(&wait_mx)do { do { (&((&wait_mx)->tree))->sph_root = ((void *)0); } while (0); (&wait_mx)->count = 0; } while(0); | |||
| 549 | tree_init(&wait_preference)do { do { (&((&wait_preference)->tree))->sph_root = ((void*)0); } while (0); (&wait_preference)->count = 0; } while(0); | |||
| 550 | tree_init(&wait_source)do { do { (&((&wait_source)->tree))->sph_root = ((void*)0); } while (0); (&wait_source)->count = 0; } while(0); | |||
| 551 | tree_init(&flush_evp)do { do { (&((&flush_evp)->tree))->sph_root = ( (void*)0); } while (0); (&flush_evp)->count = 0; } while (0); | |||
| 552 | dict_init(&hoststat)do { do { (&((&hoststat)->dict))->sph_root = (( void*)0); } while (0); (&hoststat)->count = 0; } while (0); | |||
| 553 | ||||
| 554 | evtimer_set(&ev_flush_evp, mta_delivery_flush_event, NULL)event_set(&ev_flush_evp, -1, 0, mta_delivery_flush_event, ((void*)0)); | |||
| 555 | ||||
| 556 | runq_init(&runq_relay, mta_on_timeout); | |||
| 557 | runq_init(&runq_connector, mta_on_timeout); | |||
| 558 | runq_init(&runq_route, mta_on_timeout); | |||
| 559 | runq_init(&runq_hoststat, mta_on_timeout); | |||
| 560 | } | |||
| 561 | ||||
| 562 | ||||
| 563 | /* | |||
| 564 | * Local error on the given source. | |||
| 565 | */ | |||
| 566 | void | |||
| 567 | mta_source_error(struct mta_relay *relay, struct mta_route *route, const char *e) | |||
| 568 | { | |||
| 569 | struct mta_connector *c; | |||
| 570 | ||||
| 571 | /* | |||
| 572 | * Remember the source as broken for this connector. | |||
| 573 | */ | |||
| 574 | c = mta_connector(relay, route->src); | |||
| 575 | if (!(c->flags & CONNECTOR_ERROR_SOURCE0x0002)) | |||
| 576 | log_info("smtp-out: Error on %s: %s", | |||
| 577 | mta_route_to_text(route), e); | |||
| 578 | c->flags |= CONNECTOR_ERROR_SOURCE0x0002; | |||
| 579 | } | |||
| 580 | ||||
| 581 | void | |||
| 582 | mta_route_error(struct mta_relay *relay, struct mta_route *route) | |||
| 583 | { | |||
| 584 | #if 0 | |||
| 585 | route->nerror += 1; | |||
| 586 | ||||
| 587 | if (route->nerror > MAXERROR_PER_ROUTE4) { | |||
| 588 | log_info("smtp-out: Too many errors on %s: " | |||
| 589 | "disabling for a while", mta_route_to_text(route)); | |||
| 590 | mta_route_disable(route, 2, ROUTE_DISABLED_SMTP0x20); | |||
| 591 | } | |||
| 592 | #endif | |||
| 593 | } | |||
| 594 | ||||
| 595 | void | |||
| 596 | mta_route_ok(struct mta_relay *relay, struct mta_route *route) | |||
| 597 | { | |||
| 598 | struct mta_connector *c; | |||
| 599 | ||||
| 600 | if (!(route->flags & ROUTE_NEW0x01)) | |||
| 601 | return; | |||
| 602 | ||||
| 603 | log_debug("debug: mta-routing: route %s is now valid.", | |||
| 604 | mta_route_to_text(route)); | |||
| 605 | ||||
| 606 | route->nerror = 0; | |||
| 607 | route->flags &= ~ROUTE_NEW0x01; | |||
| 608 | ||||
| 609 | c = mta_connector(relay, route->src); | |||
| 610 | mta_connect(c); | |||
| 611 | } | |||
| 612 | ||||
| 613 | void | |||
| 614 | mta_route_down(struct mta_relay *relay, struct mta_route *route) | |||
| 615 | { | |||
| 616 | #if 0 | |||
| 617 | mta_route_disable(route, 2, ROUTE_DISABLED_SMTP0x20); | |||
| 618 | #endif | |||
| 619 | } | |||
| 620 | ||||
| 621 | void | |||
| 622 | mta_route_collect(struct mta_relay *relay, struct mta_route *route) | |||
| 623 | { | |||
| 624 | struct mta_connector *c; | |||
| 625 | ||||
| 626 | log_debug("debug: mta_route_collect(%s)", | |||
| 627 | mta_route_to_text(route)); | |||
| 628 | ||||
| 629 | relay->nconn -= 1; | |||
| 630 | relay->domain->nconn -= 1; | |||
| 631 | route->nconn -= 1; | |||
| 632 | route->src->nconn -= 1; | |||
| 633 | route->dst->nconn -= 1; | |||
| 634 | route->lastdisc = time(NULL((void*)0)); | |||
| 635 | ||||
| 636 | /* First connection failed */ | |||
| 637 | if (route->flags & ROUTE_NEW0x01) | |||
| 638 | mta_route_disable(route, 1, ROUTE_DISABLED_NET0x10); | |||
| 639 | ||||
| 640 | c = mta_connector(relay, route->src); | |||
| 641 | c->nconn -= 1; | |||
| 642 | mta_connect(c); | |||
| 643 | mta_route_unref(route); /* from mta_find_route() */ | |||
| 644 | mta_relay_unref(relay); /* from mta_connect() */ | |||
| 645 | } | |||
| 646 | ||||
| 647 | struct mta_task * | |||
| 648 | mta_route_next_task(struct mta_relay *relay, struct mta_route *route) | |||
| 649 | { | |||
| 650 | struct mta_task *task; | |||
| 651 | ||||
| 652 | if ((task = TAILQ_FIRST(&relay->tasks)((&relay->tasks)->tqh_first))) { | |||
| 653 | TAILQ_REMOVE(&relay->tasks, task, entry)do { if (((task)->entry.tqe_next) != ((void*)0)) (task)-> entry.tqe_next->entry.tqe_prev = (task)->entry.tqe_prev ; else (&relay->tasks)->tqh_last = (task)->entry .tqe_prev; *(task)->entry.tqe_prev = (task)->entry.tqe_next ; ; ; } while (0); | |||
| 654 | relay->ntask -= 1; | |||
| 655 | task->relay = NULL((void*)0); | |||
| 656 | ||||
| 657 | /* When the number of tasks is down to lowat, query some evp */ | |||
| 658 | if (relay->ntask == (size_t)relay->limits->task_lowat) { | |||
| 659 | if (relay->state & RELAY_ONHOLD0x01) { | |||
| 660 | log_info("smtp-out: back to lowat on %s: releasing", | |||
| 661 | mta_relay_to_text(relay)); | |||
| 662 | relay->state &= ~RELAY_ONHOLD0x01; | |||
| 663 | } | |||
| 664 | if (relay->state & RELAY_HOLDQ0x02) { | |||
| 665 | m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); | |||
| 666 | m_add_id(p_queue, relay->id); | |||
| 667 | m_add_int(p_queue, relay->limits->task_release); | |||
| 668 | m_close(p_queue); | |||
| 669 | } | |||
| 670 | } | |||
| 671 | else if (relay->ntask == 0 && relay->state & RELAY_HOLDQ0x02) { | |||
| 672 | m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); | |||
| 673 | m_add_id(p_queue, relay->id); | |||
| 674 | m_add_int(p_queue, 0); | |||
| 675 | m_close(p_queue); | |||
| 676 | } | |||
| 677 | } | |||
| 678 | ||||
| 679 | return (task); | |||
| 680 | } | |||
| 681 | ||||
| 682 | static void | |||
| 683 | mta_handle_envelope(struct envelope *evp, const char *smarthost) | |||
| 684 | { | |||
| 685 | struct mta_relay *relay; | |||
| 686 | struct mta_task *task; | |||
| 687 | struct mta_envelope *e; | |||
| 688 | struct dispatcher *dispatcher; | |||
| 689 | struct mailaddr maddr; | |||
| 690 | struct relayhost relayh; | |||
| 691 | char buf[LINE_MAX2048]; | |||
| 692 | ||||
| 693 | dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); | |||
| 694 | if (dispatcher->u.remote.smarthost && smarthost == NULL((void*)0)) { | |||
| 695 | mta_query_smarthost(evp); | |||
| 696 | return; | |||
| 697 | } | |||
| 698 | ||||
| 699 | memset(&relayh, 0, sizeof(relayh)); | |||
| 700 | relayh.tls = RELAY_TLS_OPPORTUNISTIC0; | |||
| 701 | if (smarthost && !text_to_relayhost(&relayh, smarthost)) { | |||
| 702 | log_warnx("warn: Failed to parse smarthost %s", smarthost); | |||
| 703 | m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); | |||
| 704 | m_add_evpid(p_queue, evp->id); | |||
| 705 | m_add_string(p_queue, "Cannot parse smarthost"); | |||
| 706 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 707 | m_close(p_queue); | |||
| 708 | return; | |||
| 709 | } | |||
| 710 | ||||
| 711 | if (relayh.flags & RELAY_AUTH0x08 && dispatcher->u.remote.auth == NULL((void*)0)) { | |||
| 712 | log_warnx("warn: No auth table on action \"%s\" for relay %s", | |||
| 713 | evp->dispatcher, smarthost); | |||
| 714 | m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); | |||
| 715 | m_add_evpid(p_queue, evp->id); | |||
| 716 | m_add_string(p_queue, "No auth table for relaying"); | |||
| 717 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 718 | m_close(p_queue); | |||
| 719 | return; | |||
| 720 | } | |||
| 721 | ||||
| 722 | if (dispatcher->u.remote.tls_required) { | |||
| 723 | /* Reject relay if smtp+notls:// is requested */ | |||
| 724 | if (relayh.tls == RELAY_TLS_NO3) { | |||
| 725 | log_warnx("warn: TLS required for action \"%s\"", | |||
| 726 | evp->dispatcher); | |||
| 727 | m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); | |||
| 728 | m_add_evpid(p_queue, evp->id); | |||
| 729 | m_add_string(p_queue, "TLS required for relaying"); | |||
| 730 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 731 | m_close(p_queue); | |||
| 732 | return; | |||
| 733 | } | |||
| 734 | /* Update smtp:// to smtp+tls:// */ | |||
| 735 | if (relayh.tls == RELAY_TLS_OPPORTUNISTIC0) | |||
| 736 | relayh.tls = RELAY_TLS_STARTTLS1; | |||
| 737 | } | |||
| 738 | ||||
| 739 | relay = mta_relay(evp, &relayh); | |||
| 740 | /* ignore if we don't know the limits yet */ | |||
| 741 | if (relay->limits && | |||
| 742 | relay->ntask >= (size_t)relay->limits->task_hiwat) { | |||
| 743 | if (!(relay->state & RELAY_ONHOLD0x01)) { | |||
| 744 | log_info("smtp-out: hiwat reached on %s: holding envelopes", | |||
| 745 | mta_relay_to_text(relay)); | |||
| 746 | relay->state |= RELAY_ONHOLD0x01; | |||
| 747 | } | |||
| 748 | } | |||
| 749 | ||||
| 750 | /* | |||
| 751 | * If the relay has too many pending tasks, tell the | |||
| 752 | * scheduler to hold it until further notice | |||
| 753 | */ | |||
| 754 | if (relay->state & RELAY_ONHOLD0x01) { | |||
| 755 | relay->state |= RELAY_HOLDQ0x02; | |||
| 756 | m_create(p_queue, IMSG_MTA_DELIVERY_HOLD, 0, 0, -1); | |||
| 757 | m_add_evpid(p_queue, evp->id); | |||
| 758 | m_add_id(p_queue, relay->id); | |||
| 759 | m_close(p_queue); | |||
| 760 | mta_relay_unref(relay); /* from here */ | |||
| 761 | return; | |||
| 762 | } | |||
| 763 | ||||
| 764 | task = NULL((void*)0); | |||
| 765 | TAILQ_FOREACH(task, &relay->tasks, entry)for((task) = ((&relay->tasks)->tqh_first); (task) != ((void*)0); (task) = ((task)->entry.tqe_next)) | |||
| 766 | if (task->msgid == evpid_to_msgid(evp->id)) | |||
| 767 | break; | |||
| 768 | ||||
| 769 | if (task == NULL((void*)0)) { | |||
| 770 | task = xmalloc(sizeof *task); | |||
| 771 | TAILQ_INIT(&task->envelopes)do { (&task->envelopes)->tqh_first = ((void*)0); (& task->envelopes)->tqh_last = &(&task->envelopes )->tqh_first; } while (0); | |||
| 772 | task->relay = relay; | |||
| 773 | relay->ntask += 1; | |||
| 774 | TAILQ_INSERT_TAIL(&relay->tasks, task, entry)do { (task)->entry.tqe_next = ((void*)0); (task)->entry .tqe_prev = (&relay->tasks)->tqh_last; *(&relay ->tasks)->tqh_last = (task); (&relay->tasks)-> tqh_last = &(task)->entry.tqe_next; } while (0); | |||
| 775 | task->msgid = evpid_to_msgid(evp->id); | |||
| 776 | if (evp->sender.user[0] || evp->sender.domain[0]) | |||
| 777 | (void)snprintf(buf, sizeof buf, "%s@%s", | |||
| 778 | evp->sender.user, evp->sender.domain); | |||
| 779 | else | |||
| 780 | buf[0] = '\0'; | |||
| 781 | ||||
| 782 | if (dispatcher->u.remote.mail_from && evp->sender.user[0]) { | |||
| 783 | memset(&maddr, 0, sizeof (maddr)); | |||
| 784 | if (text_to_mailaddr(&maddr, | |||
| 785 | dispatcher->u.remote.mail_from)) { | |||
| 786 | (void)snprintf(buf, sizeof buf, "%s@%s", | |||
| 787 | maddr.user[0] ? maddr.user : evp->sender.user, | |||
| 788 | maddr.domain[0] ? maddr.domain : evp->sender.domain); | |||
| 789 | } | |||
| 790 | } | |||
| 791 | ||||
| 792 | task->sender = xstrdup(buf); | |||
| 793 | stat_increment("mta.task", 1); | |||
| 794 | } | |||
| 795 | ||||
| 796 | e = xcalloc(1, sizeof *e); | |||
| 797 | e->id = evp->id; | |||
| 798 | e->creation = evp->creation; | |||
| 799 | e->smtpname = xstrdup(evp->smtpname); | |||
| 800 | (void)snprintf(buf, sizeof buf, "%s@%s", | |||
| 801 | evp->dest.user, evp->dest.domain); | |||
| 802 | e->dest = xstrdup(buf); | |||
| 803 | (void)snprintf(buf, sizeof buf, "%s@%s", | |||
| 804 | evp->rcpt.user, evp->rcpt.domain); | |||
| 805 | if (strcmp(buf, e->dest)) | |||
| 806 | e->rcpt = xstrdup(buf); | |||
| 807 | e->task = task; | |||
| 808 | if (evp->dsn_orcpt.user[0] && evp->dsn_orcpt.domain[0]) { | |||
| 809 | (void)snprintf(buf, sizeof buf, "%s@%s", | |||
| 810 | evp->dsn_orcpt.user, evp->dsn_orcpt.domain); | |||
| 811 | e->dsn_orcpt = xstrdup(buf); | |||
| 812 | } | |||
| 813 | (void)strlcpy(e->dsn_envid, evp->dsn_envid, | |||
| 814 | sizeof e->dsn_envid); | |||
| 815 | e->dsn_notify = evp->dsn_notify; | |||
| 816 | e->dsn_ret = evp->dsn_ret; | |||
| 817 | ||||
| 818 | TAILQ_INSERT_TAIL(&task->envelopes, e, entry)do { (e)->entry.tqe_next = ((void*)0); (e)->entry.tqe_prev = (&task->envelopes)->tqh_last; *(&task->envelopes )->tqh_last = (e); (&task->envelopes)->tqh_last = &(e)->entry.tqe_next; } while (0); | |||
| 819 | log_debug("debug: mta: received evp:%016" PRIx64"llx" | |||
| 820 | " for <%s>", e->id, e->dest); | |||
| 821 | ||||
| 822 | stat_increment("mta.envelope", 1); | |||
| 823 | ||||
| 824 | mta_drain(relay); | |||
| 825 | mta_relay_unref(relay); /* from here */ | |||
| 826 | } | |||
| 827 | ||||
| 828 | static void | |||
| 829 | mta_delivery_flush_event(int fd, short event, void *arg) | |||
| 830 | { | |||
| 831 | struct mta_envelope *e; | |||
| 832 | struct timeval tv; | |||
| 833 | ||||
| 834 | if (tree_poproot(&flush_evp, NULL((void*)0), (void**)(&e))) { | |||
| 835 | ||||
| 836 | if (e->delivery == IMSG_MTA_DELIVERY_OK) { | |||
| 837 | m_create(p_queue, IMSG_MTA_DELIVERY_OK, 0, 0, -1); | |||
| 838 | m_add_evpid(p_queue, e->id); | |||
| 839 | m_add_int(p_queue, e->ext); | |||
| 840 | m_close(p_queue); | |||
| 841 | } else if (e->delivery == IMSG_MTA_DELIVERY_TEMPFAIL) { | |||
| 842 | m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); | |||
| 843 | m_add_evpid(p_queue, e->id); | |||
| 844 | m_add_string(p_queue, e->status); | |||
| 845 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 846 | m_close(p_queue); | |||
| 847 | } | |||
| 848 | else if (e->delivery == IMSG_MTA_DELIVERY_PERMFAIL) { | |||
| 849 | m_create(p_queue, IMSG_MTA_DELIVERY_PERMFAIL, 0, 0, -1); | |||
| 850 | m_add_evpid(p_queue, e->id); | |||
| 851 | m_add_string(p_queue, e->status); | |||
| 852 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 853 | m_close(p_queue); | |||
| 854 | } | |||
| 855 | else if (e->delivery == IMSG_MTA_DELIVERY_LOOP) { | |||
| 856 | m_create(p_queue, IMSG_MTA_DELIVERY_LOOP, 0, 0, -1); | |||
| 857 | m_add_evpid(p_queue, e->id); | |||
| 858 | m_close(p_queue); | |||
| 859 | } | |||
| 860 | else { | |||
| 861 | log_warnx("warn: bad delivery type %d for %016" PRIx64"llx", | |||
| 862 | e->delivery, e->id); | |||
| 863 | fatalx("aborting"); | |||
| 864 | } | |||
| 865 | ||||
| 866 | log_debug("debug: mta: flush for %016"PRIx64"llx"" (-> %s)", e->id, e->dest); | |||
| 867 | ||||
| 868 | free(e->smtpname); | |||
| 869 | free(e->dest); | |||
| 870 | free(e->rcpt); | |||
| 871 | free(e->dsn_orcpt); | |||
| 872 | free(e); | |||
| 873 | ||||
| 874 | tv.tv_sec = 0; | |||
| 875 | tv.tv_usec = 0; | |||
| 876 | evtimer_add(&ev_flush_evp, &tv)event_add(&ev_flush_evp, &tv); | |||
| 877 | } | |||
| 878 | } | |||
| 879 | ||||
| 880 | void | |||
| 881 | mta_delivery_log(struct mta_envelope *e, const char *source, const char *relay, | |||
| 882 | int delivery, const char *status) | |||
| 883 | { | |||
| 884 | if (delivery == IMSG_MTA_DELIVERY_OK) | |||
| 885 | mta_log(e, "Ok", source, relay, status); | |||
| 886 | else if (delivery == IMSG_MTA_DELIVERY_TEMPFAIL) | |||
| 887 | mta_log(e, "TempFail", source, relay, status); | |||
| 888 | else if (delivery == IMSG_MTA_DELIVERY_PERMFAIL) | |||
| 889 | mta_log(e, "PermFail", source, relay, status); | |||
| 890 | else if (delivery == IMSG_MTA_DELIVERY_LOOP) | |||
| 891 | mta_log(e, "PermFail", source, relay, "Loop detected"); | |||
| 892 | else { | |||
| 893 | log_warnx("warn: bad delivery type %d for %016" PRIx64"llx", | |||
| 894 | delivery, e->id); | |||
| 895 | fatalx("aborting"); | |||
| 896 | } | |||
| 897 | ||||
| 898 | e->delivery = delivery; | |||
| 899 | if (status) | |||
| 900 | (void)strlcpy(e->status, status, sizeof(e->status)); | |||
| 901 | } | |||
| 902 | ||||
| 903 | void | |||
| 904 | mta_delivery_notify(struct mta_envelope *e) | |||
| 905 | { | |||
| 906 | struct timeval tv; | |||
| 907 | ||||
| 908 | tree_xset(&flush_evp, e->id, e); | |||
| 909 | if (tree_count(&flush_evp)((&flush_evp)->count) == 1) { | |||
| 910 | tv.tv_sec = 0; | |||
| 911 | tv.tv_usec = 0; | |||
| 912 | evtimer_add(&ev_flush_evp, &tv)event_add(&ev_flush_evp, &tv); | |||
| 913 | } | |||
| 914 | } | |||
| 915 | ||||
| 916 | static void | |||
| 917 | mta_query_mx(struct mta_relay *relay) | |||
| 918 | { | |||
| 919 | uint64_t id; | |||
| 920 | ||||
| 921 | if (relay->status & RELAY_WAIT_MX0x01) | |||
| 922 | return; | |||
| 923 | ||||
| 924 | log_debug("debug: mta: querying MX for %s...", | |||
| 925 | mta_relay_to_text(relay)); | |||
| 926 | ||||
| 927 | if (waitq_wait(&relay->domain->mxs, mta_on_mx, relay)) { | |||
| 928 | id = generate_uid(); | |||
| 929 | tree_xset(&wait_mx, id, relay->domain); | |||
| 930 | if (relay->domain->as_host) | |||
| 931 | m_create(p_lka, IMSG_MTA_DNS_HOST, 0, 0, -1); | |||
| 932 | else | |||
| 933 | m_create(p_lka, IMSG_MTA_DNS_MX, 0, 0, -1); | |||
| 934 | m_add_id(p_lka, id); | |||
| 935 | m_add_string(p_lka, relay->domain->name); | |||
| 936 | m_close(p_lka); | |||
| 937 | } | |||
| 938 | relay->status |= RELAY_WAIT_MX0x01; | |||
| 939 | mta_relay_ref(relay); | |||
| 940 | } | |||
| 941 | ||||
| 942 | static void | |||
| 943 | mta_query_limits(struct mta_relay *relay) | |||
| 944 | { | |||
| 945 | if (relay->status & RELAY_WAIT_LIMITS0x08) | |||
| 946 | return; | |||
| 947 | ||||
| 948 | relay->limits = dict_get(env->sc_limits_dict, relay->domain->name); | |||
| 949 | if (relay->limits == NULL((void*)0)) | |||
| 950 | relay->limits = dict_get(env->sc_limits_dict, "default"); | |||
| 951 | ||||
| 952 | if (max_seen_conndelay_route < relay->limits->conndelay_route) | |||
| 953 | max_seen_conndelay_route = relay->limits->conndelay_route; | |||
| 954 | if (max_seen_discdelay_route < relay->limits->discdelay_route) | |||
| 955 | max_seen_discdelay_route = relay->limits->discdelay_route; | |||
| 956 | } | |||
| 957 | ||||
| 958 | static void | |||
| 959 | mta_query_secret(struct mta_relay *relay) | |||
| 960 | { | |||
| 961 | if (relay->status & RELAY_WAIT_SECRET0x04) | |||
| 962 | return; | |||
| 963 | ||||
| 964 | log_debug("debug: mta: querying secret for %s...", | |||
| 965 | mta_relay_to_text(relay)); | |||
| 966 | ||||
| 967 | tree_xset(&wait_secret, relay->id, relay); | |||
| 968 | relay->status |= RELAY_WAIT_SECRET0x04; | |||
| 969 | ||||
| 970 | m_create(p_lka, IMSG_MTA_LOOKUP_CREDENTIALS, 0, 0, -1); | |||
| 971 | m_add_id(p_lka, relay->id); | |||
| 972 | m_add_string(p_lka, relay->authtable); | |||
| 973 | m_add_string(p_lka, relay->authlabel); | |||
| 974 | m_close(p_lka); | |||
| 975 | ||||
| 976 | mta_relay_ref(relay); | |||
| 977 | } | |||
| 978 | ||||
| 979 | static void | |||
| 980 | mta_query_smarthost(struct envelope *evp0) | |||
| 981 | { | |||
| 982 | struct dispatcher *dispatcher; | |||
| 983 | struct envelope *evp; | |||
| 984 | ||||
| 985 | evp = malloc(sizeof(*evp)); | |||
| 986 | memmove(evp, evp0, sizeof(*evp)); | |||
| 987 | ||||
| 988 | dispatcher = dict_xget(env->sc_dispatchers, evp->dispatcher); | |||
| 989 | ||||
| 990 | log_debug("debug: mta: querying smarthost for %s:%s...", | |||
| 991 | evp->dispatcher, dispatcher->u.remote.smarthost); | |||
| 992 | ||||
| 993 | tree_xset(&wait_smarthost, evp->id, evp); | |||
| 994 | ||||
| 995 | m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); | |||
| 996 | m_add_id(p_lka, evp->id); | |||
| 997 | if (dispatcher->u.remote.smarthost_domain) | |||
| 998 | m_add_string(p_lka, evp->dest.domain); | |||
| 999 | else | |||
| 1000 | m_add_string(p_lka, NULL((void*)0)); | |||
| 1001 | m_add_string(p_lka, dispatcher->u.remote.smarthost); | |||
| 1002 | m_close(p_lka); | |||
| 1003 | ||||
| 1004 | log_debug("debug: mta: querying smarthost"); | |||
| 1005 | } | |||
| 1006 | ||||
| 1007 | static void | |||
| 1008 | mta_query_preference(struct mta_relay *relay) | |||
| 1009 | { | |||
| 1010 | if (relay->status & RELAY_WAIT_PREFERENCE0x02) | |||
| 1011 | return; | |||
| 1012 | ||||
| 1013 | log_debug("debug: mta: querying preference for %s...", | |||
| 1014 | mta_relay_to_text(relay)); | |||
| 1015 | ||||
| 1016 | tree_xset(&wait_preference, relay->id, relay); | |||
| 1017 | relay->status |= RELAY_WAIT_PREFERENCE0x02; | |||
| 1018 | ||||
| 1019 | m_create(p_lka, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); | |||
| 1020 | m_add_id(p_lka, relay->id); | |||
| 1021 | m_add_string(p_lka, relay->domain->name); | |||
| 1022 | m_add_string(p_lka, relay->backupname); | |||
| 1023 | m_close(p_lka); | |||
| 1024 | ||||
| 1025 | mta_relay_ref(relay); | |||
| 1026 | } | |||
| 1027 | ||||
| 1028 | static void | |||
| 1029 | mta_query_source(struct mta_relay *relay) | |||
| 1030 | { | |||
| 1031 | log_debug("debug: mta: querying source for %s...", | |||
| 1032 | mta_relay_to_text(relay)); | |||
| 1033 | ||||
| 1034 | relay->sourceloop += 1; | |||
| 1035 | ||||
| 1036 | if (relay->sourcetable
| |||
| 1037 | /* | |||
| 1038 | * This is a recursive call, but it only happens once, since | |||
| 1039 | * another source will not be queried immediately. | |||
| 1040 | */ | |||
| 1041 | mta_relay_ref(relay); | |||
| 1042 | mta_on_source(relay, mta_source(NULL((void*)0))); | |||
| 1043 | return; | |||
| 1044 | } | |||
| 1045 | ||||
| 1046 | m_create(p_lka, IMSG_MTA_LOOKUP_SOURCE, 0, 0, -1); | |||
| 1047 | m_add_id(p_lka, relay->id); | |||
| 1048 | m_add_string(p_lka, relay->sourcetable); | |||
| 1049 | m_close(p_lka); | |||
| 1050 | ||||
| 1051 | tree_xset(&wait_source, relay->id, relay); | |||
| 1052 | relay->status |= RELAY_WAIT_SOURCE0x10; | |||
| 1053 | mta_relay_ref(relay); | |||
| 1054 | } | |||
| 1055 | ||||
| 1056 | static void | |||
| 1057 | mta_on_mx(void *tag, void *arg, void *data) | |||
| 1058 | { | |||
| 1059 | struct mta_domain *domain = data; | |||
| 1060 | struct mta_relay *relay = arg; | |||
| 1061 | ||||
| 1062 | log_debug("debug: mta: ... got mx (%p, %s, %s)", | |||
| 1063 | tag, domain->name, mta_relay_to_text(relay)); | |||
| 1064 | ||||
| 1065 | switch (domain->mxstatus) { | |||
| 1066 | case DNS_OK: | |||
| 1067 | break; | |||
| 1068 | case DNS_RETRY: | |||
| 1069 | relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; | |||
| 1070 | relay->failstr = "Temporary failure in MX lookup"; | |||
| 1071 | break; | |||
| 1072 | case DNS_EINVAL: | |||
| 1073 | relay->fail = IMSG_MTA_DELIVERY_PERMFAIL; | |||
| 1074 | relay->failstr = "Invalid domain name"; | |||
| 1075 | break; | |||
| 1076 | case DNS_ENONAME: | |||
| 1077 | relay->fail = IMSG_MTA_DELIVERY_PERMFAIL; | |||
| 1078 | relay->failstr = "Domain does not exist"; | |||
| 1079 | break; | |||
| 1080 | case DNS_ENOTFOUND: | |||
| 1081 | relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; | |||
| 1082 | if (relay->domain->as_host) | |||
| 1083 | relay->failstr = "Host not found"; | |||
| 1084 | else | |||
| 1085 | relay->failstr = "No MX found for domain"; | |||
| 1086 | break; | |||
| 1087 | default: | |||
| 1088 | fatalx("bad DNS lookup error code"); | |||
| 1089 | break; | |||
| 1090 | } | |||
| 1091 | ||||
| 1092 | if (domain->mxstatus) | |||
| 1093 | log_info("smtp-out: Failed to resolve MX for %s: %s", | |||
| 1094 | mta_relay_to_text(relay), relay->failstr); | |||
| 1095 | ||||
| 1096 | relay->status &= ~RELAY_WAIT_MX0x01; | |||
| 1097 | mta_drain(relay); | |||
| 1098 | mta_relay_unref(relay); /* from mta_drain() */ | |||
| 1099 | } | |||
| 1100 | ||||
| 1101 | static void | |||
| 1102 | mta_on_secret(struct mta_relay *relay, const char *secret) | |||
| 1103 | { | |||
| 1104 | log_debug("debug: mta: ... got secret for %s: %s", | |||
| 1105 | mta_relay_to_text(relay), secret); | |||
| 1106 | ||||
| 1107 | if (secret) | |||
| 1108 | relay->secret = strdup(secret); | |||
| 1109 | ||||
| 1110 | if (relay->secret == NULL((void*)0)) { | |||
| 1111 | log_warnx("warn: Failed to retrieve secret " | |||
| 1112 | "for %s", mta_relay_to_text(relay)); | |||
| 1113 | relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; | |||
| 1114 | relay->failstr = "Could not retrieve credentials"; | |||
| 1115 | } | |||
| 1116 | ||||
| 1117 | relay->status &= ~RELAY_WAIT_SECRET0x04; | |||
| 1118 | mta_drain(relay); | |||
| 1119 | mta_relay_unref(relay); /* from mta_query_secret() */ | |||
| 1120 | } | |||
| 1121 | ||||
| 1122 | static void | |||
| 1123 | mta_on_smarthost(struct envelope *evp, const char *smarthost) | |||
| 1124 | { | |||
| 1125 | if (smarthost == NULL((void*)0)) { | |||
| 1126 | log_warnx("warn: Failed to retrieve smarthost " | |||
| 1127 | "for envelope %"PRIx64"llx", evp->id); | |||
| 1128 | m_create(p_queue, IMSG_MTA_DELIVERY_TEMPFAIL, 0, 0, -1); | |||
| 1129 | m_add_evpid(p_queue, evp->id); | |||
| 1130 | m_add_string(p_queue, "Cannot retrieve smarthost"); | |||
| 1131 | m_add_int(p_queue, ESC_OTHER_STATUS); | |||
| 1132 | m_close(p_queue); | |||
| 1133 | return; | |||
| 1134 | } | |||
| 1135 | ||||
| 1136 | log_debug("debug: mta: ... got smarthost for %016"PRIx64"llx"": %s", | |||
| 1137 | evp->id, smarthost); | |||
| 1138 | mta_handle_envelope(evp, smarthost); | |||
| 1139 | free(evp); | |||
| 1140 | } | |||
| 1141 | ||||
| 1142 | static void | |||
| 1143 | mta_on_preference(struct mta_relay *relay, int preference) | |||
| 1144 | { | |||
| 1145 | log_debug("debug: mta: ... got preference for %s: %d", | |||
| 1146 | mta_relay_to_text(relay), preference); | |||
| 1147 | ||||
| 1148 | relay->backuppref = preference; | |||
| 1149 | ||||
| 1150 | relay->status &= ~RELAY_WAIT_PREFERENCE0x02; | |||
| 1151 | mta_drain(relay); | |||
| 1152 | mta_relay_unref(relay); /* from mta_query_preference() */ | |||
| 1153 | } | |||
| 1154 | ||||
| 1155 | static void | |||
| 1156 | mta_on_source(struct mta_relay *relay, struct mta_source *source) | |||
| 1157 | { | |||
| 1158 | struct mta_connector *c; | |||
| 1159 | void *iter; | |||
| 1160 | int delay, errmask; | |||
| 1161 | ||||
| 1162 | log_debug("debug: mta: ... got source for %s: %s", | |||
| 1163 | mta_relay_to_text(relay), source
| |||
| 1164 | ||||
| 1165 | relay->lastsource = time(NULL((void*)0)); | |||
| 1166 | delay = DELAY_CHECK_SOURCE_SLOW10; | |||
| 1167 | ||||
| 1168 | if (source
| |||
| 1169 | c = mta_connector(relay, source); | |||
| 1170 | if (c->flags & CONNECTOR_NEW0x10000) { | |||
| 1171 | c->flags &= ~CONNECTOR_NEW0x10000; | |||
| 1172 | delay = DELAY_CHECK_SOURCE1; | |||
| 1173 | } | |||
| 1174 | mta_connect(c); | |||
| 1175 | if ((c->flags & CONNECTOR_ERROR0x00ff) == 0) | |||
| 1176 | relay->sourceloop = 0; | |||
| 1177 | else | |||
| 1178 | delay = DELAY_CHECK_SOURCE_FAST0; | |||
| 1179 | mta_source_unref(source); /* from constructor */ | |||
| 1180 | } | |||
| 1181 | else { | |||
| 1182 | log_warnx("warn: Failed to get source address for %s", | |||
| 1183 | mta_relay_to_text(relay)); | |||
| 1184 | } | |||
| 1185 | ||||
| 1186 | if (tree_count(&relay->connectors)((&relay->connectors)->count) == 0) { | |||
| 1187 | relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; | |||
| 1188 | relay->failstr = "Could not retrieve source address"; | |||
| 1189 | } | |||
| 1190 | if (tree_count(&relay->connectors)((&relay->connectors)->count) < relay->sourceloop) { | |||
| 1191 | relay->fail = IMSG_MTA_DELIVERY_TEMPFAIL; | |||
| 1192 | relay->failstr = "No valid route to remote MX"; | |||
| 1193 | ||||
| 1194 | errmask = 0; | |||
| 1195 | iter = NULL((void*)0); | |||
| 1196 | while (tree_iter(&relay->connectors, &iter, NULL((void*)0), (void **)&c)) | |||
| 1197 | errmask |= c->flags; | |||
| 1198 | ||||
| 1199 | if (errmask & CONNECTOR_ERROR_ROUTE_SMTP0x0010) | |||
| 1200 | relay->failstr = "Destination seem to reject all mails"; | |||
| 1201 | else if (errmask & CONNECTOR_ERROR_ROUTE_NET0x0008) | |||
| 1202 | relay->failstr = "Network error on destination MXs"; | |||
| 1203 | else if (errmask & CONNECTOR_ERROR_MX0x0004) | |||
| 1204 | relay->failstr = "No MX found for destination"; | |||
| 1205 | else if (errmask & CONNECTOR_ERROR_FAMILY0x0001) | |||
| 1206 | relay->failstr = "Address family mismatch on destination MXs"; | |||
| 1207 | else if (errmask & CONNECTOR_ERROR_BLOCKED0x0020) | |||
| 1208 | relay->failstr = "All routes to destination blocked"; | |||
| 1209 | else | |||
| 1210 | relay->failstr = "No valid route to destination"; | |||
| 1211 | } | |||
| 1212 | ||||
| 1213 | relay->nextsource = relay->lastsource + delay; | |||
| 1214 | relay->status &= ~RELAY_WAIT_SOURCE0x10; | |||
| 1215 | mta_drain(relay); | |||
| 1216 | mta_relay_unref(relay); /* from mta_query_source() */ | |||
| 1217 | } | |||
| 1218 | ||||
| 1219 | static void | |||
| 1220 | mta_connect(struct mta_connector *c) | |||
| 1221 | { | |||
| 1222 | struct mta_route *route; | |||
| 1223 | struct mta_mx *mx; | |||
| 1224 | struct mta_limits *l = c->relay->limits; | |||
| 1225 | int limits; | |||
| 1226 | time_t nextconn, now; | |||
| 1227 | ||||
| 1228 | /* toggle the block flag */ | |||
| 1229 | if (mta_is_blocked(c->source, c->relay->domain->name)) | |||
| 1230 | c->flags |= CONNECTOR_ERROR_BLOCKED0x0020; | |||
| 1231 | else | |||
| 1232 | c->flags &= ~CONNECTOR_ERROR_BLOCKED0x0020; | |||
| 1233 | ||||
| 1234 | again: | |||
| 1235 | ||||
| 1236 | log_debug("debug: mta: connecting with %s", mta_connector_to_text(c)); | |||
| 1237 | ||||
| 1238 | /* Do not connect if this connector has an error. */ | |||
| 1239 | if (c->flags & CONNECTOR_ERROR0x00ff) { | |||
| 1240 | log_debug("debug: mta: connector error"); | |||
| 1241 | return; | |||
| 1242 | } | |||
| 1243 | ||||
| 1244 | if (c->flags & CONNECTOR_WAIT0x20000) { | |||
| 1245 | log_debug("debug: mta: cancelling connector timeout"); | |||
| 1246 | runq_cancel(runq_connector, c); | |||
| 1247 | c->flags &= ~CONNECTOR_WAIT0x20000; | |||
| 1248 | } | |||
| 1249 | ||||
| 1250 | /* No job. */ | |||
| 1251 | if (c->relay->ntask == 0) { | |||
| 1252 | log_debug("debug: mta: no task for connector"); | |||
| 1253 | return; | |||
| 1254 | } | |||
| 1255 | ||||
| 1256 | /* Do not create more connections than necessary */ | |||
| 1257 | if ((c->relay->nconn_ready >= c->relay->ntask) || | |||
| 1258 | (c->relay->nconn > 2 && c->relay->nconn >= c->relay->ntask / 2)) { | |||
| 1259 | log_debug("debug: mta: enough connections already"); | |||
| 1260 | return; | |||
| 1261 | } | |||
| 1262 | ||||
| 1263 | limits = 0; | |||
| 1264 | nextconn = now = time(NULL((void*)0)); | |||
| 1265 | ||||
| 1266 | if (c->relay->domain->lastconn + l->conndelay_domain > nextconn) { | |||
| 1267 | log_debug("debug: mta: cannot use domain %s before %llus", | |||
| 1268 | c->relay->domain->name, | |||
| 1269 | (unsigned long long) c->relay->domain->lastconn + l->conndelay_domain - now); | |||
| 1270 | nextconn = c->relay->domain->lastconn + l->conndelay_domain; | |||
| 1271 | } | |||
| 1272 | if (c->relay->domain->nconn >= l->maxconn_per_domain) { | |||
| 1273 | log_debug("debug: mta: hit domain limit"); | |||
| 1274 | limits |= CONNECTOR_LIMIT_DOMAIN0x2000; | |||
| 1275 | } | |||
| 1276 | ||||
| 1277 | if (c->source->lastconn + l->conndelay_source > nextconn) { | |||
| 1278 | log_debug("debug: mta: cannot use source %s before %llus", | |||
| 1279 | mta_source_to_text(c->source), | |||
| 1280 | (unsigned long long) c->source->lastconn + l->conndelay_source - now); | |||
| 1281 | nextconn = c->source->lastconn + l->conndelay_source; | |||
| 1282 | } | |||
| 1283 | if (c->source->nconn >= l->maxconn_per_source) { | |||
| 1284 | log_debug("debug: mta: hit source limit"); | |||
| 1285 | limits |= CONNECTOR_LIMIT_SOURCE0x0400; | |||
| 1286 | } | |||
| 1287 | ||||
| 1288 | if (c->lastconn + l->conndelay_connector > nextconn) { | |||
| 1289 | log_debug("debug: mta: cannot use %s before %llus", | |||
| 1290 | mta_connector_to_text(c), | |||
| 1291 | (unsigned long long) c->lastconn + l->conndelay_connector - now); | |||
| 1292 | nextconn = c->lastconn + l->conndelay_connector; | |||
| 1293 | } | |||
| 1294 | if (c->nconn >= l->maxconn_per_connector) { | |||
| 1295 | log_debug("debug: mta: hit connector limit"); | |||
| 1296 | limits |= CONNECTOR_LIMIT_CONN0x1000; | |||
| 1297 | } | |||
| 1298 | ||||
| 1299 | if (c->relay->lastconn + l->conndelay_relay > nextconn) { | |||
| 1300 | log_debug("debug: mta: cannot use %s before %llus", | |||
| 1301 | mta_relay_to_text(c->relay), | |||
| 1302 | (unsigned long long) c->relay->lastconn + l->conndelay_relay - now); | |||
| 1303 | nextconn = c->relay->lastconn + l->conndelay_relay; | |||
| 1304 | } | |||
| 1305 | if (c->relay->nconn >= l->maxconn_per_relay) { | |||
| 1306 | log_debug("debug: mta: hit relay limit"); | |||
| 1307 | limits |= CONNECTOR_LIMIT_RELAY0x0800; | |||
| 1308 | } | |||
| 1309 | ||||
| 1310 | /* We can connect now, find a route */ | |||
| 1311 | if (!limits && nextconn <= now) | |||
| 1312 | route = mta_find_route(c, now, &limits, &nextconn, &mx); | |||
| 1313 | else | |||
| 1314 | route = NULL((void*)0); | |||
| 1315 | ||||
| 1316 | /* No route */ | |||
| 1317 | if (route == NULL((void*)0)) { | |||
| 1318 | ||||
| 1319 | if (c->flags & CONNECTOR_ERROR0x00ff) { | |||
| 1320 | /* XXX we might want to clear this flag later */ | |||
| 1321 | log_debug("debug: mta-routing: no route available for %s: errors on connector", | |||
| 1322 | mta_connector_to_text(c)); | |||
| 1323 | return; | |||
| 1324 | } | |||
| 1325 | else if (limits) { | |||
| 1326 | log_debug("debug: mta-routing: no route available for %s: limits reached", | |||
| 1327 | mta_connector_to_text(c)); | |||
| 1328 | nextconn = now + DELAY_CHECK_LIMIT5; | |||
| 1329 | } | |||
| 1330 | else { | |||
| 1331 | log_debug("debug: mta-routing: no route available for %s: must wait a bit", | |||
| 1332 | mta_connector_to_text(c)); | |||
| 1333 | } | |||
| 1334 | log_debug("debug: mta: retrying to connect on %s in %llus...", | |||
| 1335 | mta_connector_to_text(c), | |||
| 1336 | (unsigned long long) nextconn - time(NULL((void*)0))); | |||
| 1337 | c->flags |= CONNECTOR_WAIT0x20000; | |||
| 1338 | runq_schedule_at(runq_connector, nextconn, c); | |||
| 1339 | return; | |||
| 1340 | } | |||
| 1341 | ||||
| 1342 | log_debug("debug: mta-routing: spawning new connection on %s", | |||
| 1343 | mta_route_to_text(route)); | |||
| 1344 | ||||
| 1345 | c->nconn += 1; | |||
| 1346 | c->lastconn = time(NULL((void*)0)); | |||
| 1347 | ||||
| 1348 | c->relay->nconn += 1; | |||
| 1349 | c->relay->lastconn = c->lastconn; | |||
| 1350 | c->relay->domain->nconn += 1; | |||
| 1351 | c->relay->domain->lastconn = c->lastconn; | |||
| 1352 | route->nconn += 1; | |||
| 1353 | route->lastconn = c->lastconn; | |||
| 1354 | route->src->nconn += 1; | |||
| 1355 | route->src->lastconn = c->lastconn; | |||
| 1356 | route->dst->nconn += 1; | |||
| 1357 | route->dst->lastconn = c->lastconn; | |||
| 1358 | ||||
| 1359 | mta_session(c->relay, route, mx->mxname); /* this never fails synchronously */ | |||
| 1360 | mta_relay_ref(c->relay); | |||
| 1361 | ||||
| 1362 | goto again; | |||
| 1363 | } | |||
| 1364 | ||||
| 1365 | static void | |||
| 1366 | mta_on_timeout(struct runq *runq, void *arg) | |||
| 1367 | { | |||
| 1368 | struct mta_connector *connector = arg; | |||
| 1369 | struct mta_relay *relay = arg; | |||
| 1370 | struct mta_route *route = arg; | |||
| 1371 | struct hoststat *hs = arg; | |||
| 1372 | ||||
| 1373 | if (runq == runq_relay) { | |||
| ||||
| 1374 | log_debug("debug: mta: ... timeout for %s", | |||
| 1375 | mta_relay_to_text(relay)); | |||
| 1376 | relay->status &= ~RELAY_WAIT_CONNECTOR0x20; | |||
| 1377 | mta_drain(relay); | |||
| 1378 | mta_relay_unref(relay); /* from mta_drain() */ | |||
| ||||
| 1379 | } | |||
| 1380 | else if (runq == runq_connector) { | |||
| 1381 | log_debug("debug: mta: ... timeout for %s", | |||
| 1382 | mta_connector_to_text(connector)); | |||
| 1383 | connector->flags &= ~CONNECTOR_WAIT0x20000; | |||
| 1384 | mta_connect(connector); | |||
| 1385 | } | |||
| 1386 | else if (runq == runq_route) { | |||
| 1387 | route->flags &= ~ROUTE_RUNQ0x02; | |||
| 1388 | mta_route_enable(route); | |||
| 1389 | mta_route_unref(route); | |||
| 1390 | } | |||
| 1391 | else if (runq == runq_hoststat) { | |||
| 1392 | log_debug("debug: mta: ... timeout for hoststat %s", | |||
| 1393 | hs->name); | |||
| 1394 | mta_hoststat_remove_entry(hs); | |||
| 1395 | free(hs); | |||
| 1396 | } | |||
| 1397 | } | |||
| 1398 | ||||
| 1399 | static void | |||
| 1400 | mta_route_disable(struct mta_route *route, int penalty, int reason) | |||
| 1401 | { | |||
| 1402 | unsigned long long delay; | |||
| 1403 | ||||
| 1404 | route->penalty += penalty; | |||
| 1405 | route->lastpenalty = time(NULL((void*)0)); | |||
| 1406 | delay = (unsigned long long)DELAY_ROUTE_BASE15 * route->penalty * route->penalty; | |||
| 1407 | if (delay > DELAY_ROUTE_MAX3600) | |||
| 1408 | delay = DELAY_ROUTE_MAX3600; | |||
| 1409 | #if 0 | |||
| 1410 | delay = 60; | |||
| 1411 | #endif | |||
| 1412 | ||||
| 1413 | log_info("smtp-out: Disabling route %s for %llus", | |||
| 1414 | mta_route_to_text(route), delay); | |||
| 1415 | ||||
| 1416 | if (route->flags & ROUTE_DISABLED0xf0) | |||
| 1417 | runq_cancel(runq_route, route); | |||
| 1418 | else | |||
| 1419 | mta_route_ref(route); | |||
| 1420 | ||||
| 1421 | route->flags |= reason & ROUTE_DISABLED0xf0; | |||
| 1422 | runq_schedule(runq_route, delay, route); | |||
| 1423 | } | |||
| 1424 | ||||
| 1425 | static void | |||
| 1426 | mta_route_enable(struct mta_route *route) | |||
| 1427 | { | |||
| 1428 | if (route->flags & ROUTE_DISABLED0xf0) { | |||
| 1429 | log_info("smtp-out: Enabling route %s", | |||
| 1430 | mta_route_to_text(route)); | |||
| 1431 | route->flags &= ~ROUTE_DISABLED0xf0; | |||
| 1432 | route->flags |= ROUTE_NEW0x01; | |||
| 1433 | route->nerror = 0; | |||
| 1434 | } | |||
| 1435 | ||||
| 1436 | if (route->penalty) { | |||
| 1437 | #if DELAY_QUADRATIC1 | |||
| 1438 | route->penalty -= 1; | |||
| 1439 | route->lastpenalty = time(NULL((void*)0)); | |||
| 1440 | #else | |||
| 1441 | route->penalty = 0; | |||
| 1442 | #endif | |||
| 1443 | } | |||
| 1444 | } | |||
| 1445 | ||||
| 1446 | static void | |||
| 1447 | mta_drain(struct mta_relay *r) | |||
| 1448 | { | |||
| 1449 | char buf[64]; | |||
| 1450 | ||||
| 1451 | log_debug("debug: mta: draining %s " | |||
| 1452 | "refcount=%d, ntask=%zu, nconnector=%zu, nconn=%zu", | |||
| 1453 | mta_relay_to_text(r), | |||
| 1454 | r->refcount, r->ntask, tree_count(&r->connectors)((&r->connectors)->count), r->nconn); | |||
| 1455 | ||||
| 1456 | /* | |||
| 1457 | * All done. | |||
| 1458 | */ | |||
| 1459 | if (r->ntask == 0) { | |||
| 1460 | log_debug("debug: mta: all done for %s", mta_relay_to_text(r)); | |||
| 1461 | return; | |||
| 1462 | } | |||
| 1463 | ||||
| 1464 | /* | |||
| 1465 | * If we know that this relay is failing flush the tasks. | |||
| 1466 | */ | |||
| 1467 | if (r->fail) { | |||
| 1468 | mta_flush(r, r->fail, r->failstr); | |||
| 1469 | return; | |||
| 1470 | } | |||
| 1471 | ||||
| 1472 | /* Query secret if needed. */ | |||
| 1473 | if (r->flags & RELAY_AUTH0x08 && r->secret == NULL((void*)0)) | |||
| 1474 | mta_query_secret(r); | |||
| 1475 | ||||
| 1476 | /* Query our preference if needed. */ | |||
| 1477 | if (r->backupname
| |||
| 1478 | mta_query_preference(r); | |||
| 1479 | ||||
| 1480 | /* Query the domain MXs if needed. */ | |||
| 1481 | if (r->domain->lastmxquery == 0) | |||
| 1482 | mta_query_mx(r); | |||
| 1483 | ||||
| 1484 | /* Query the limits if needed. */ | |||
| 1485 | if (r->limits == NULL((void*)0)) | |||
| 1486 | mta_query_limits(r); | |||
| 1487 | ||||
| 1488 | /* Wait until we are ready to proceed. */ | |||
| 1489 | if (r->status & RELAY_WAITMASK0x7f) { | |||
| 1490 | buf[0] = '\0'; | |||
| 1491 | if (r->status & RELAY_WAIT_MX0x01) | |||
| 1492 | (void)strlcat(buf, " MX", sizeof buf); | |||
| 1493 | if (r->status & RELAY_WAIT_PREFERENCE0x02) | |||
| 1494 | (void)strlcat(buf, " preference", sizeof buf); | |||
| 1495 | if (r->status & RELAY_WAIT_SECRET0x04) | |||
| 1496 | (void)strlcat(buf, " secret", sizeof buf); | |||
| 1497 | if (r->status & RELAY_WAIT_SOURCE0x10) | |||
| 1498 | (void)strlcat(buf, " source", sizeof buf); | |||
| 1499 | if (r->status & RELAY_WAIT_CONNECTOR0x20) | |||
| 1500 | (void)strlcat(buf, " connector", sizeof buf); | |||
| 1501 | log_debug("debug: mta: %s waiting for%s", | |||
| 1502 | mta_relay_to_text(r), buf); | |||
| 1503 | return; | |||
| 1504 | } | |||
| 1505 | ||||
| 1506 | /* | |||
| 1507 | * We have pending task, and it's maybe time too try a new source. | |||
| 1508 | */ | |||
| 1509 | if (r->nextsource <= time(NULL((void*)0))) | |||
| 1510 | mta_query_source(r); | |||
| 1511 | else { | |||
| 1512 | log_debug("debug: mta: scheduling relay %s in %llus...", | |||
| 1513 | mta_relay_to_text(r), | |||
| 1514 | (unsigned long long) r->nextsource - time(NULL((void*)0))); | |||
| 1515 | runq_schedule_at(runq_relay, r->nextsource, r); | |||
| 1516 | r->status |= RELAY_WAIT_CONNECTOR0x20; | |||
| 1517 | mta_relay_ref(r); | |||
| 1518 | } | |||
| 1519 | } | |||
| 1520 | ||||
| 1521 | static void | |||
| 1522 | mta_flush(struct mta_relay *relay, int fail, const char *error) | |||
| 1523 | { | |||
| 1524 | struct mta_envelope *e; | |||
| 1525 | struct mta_task *task; | |||
| 1526 | const char *domain; | |||
| 1527 | void *iter; | |||
| 1528 | struct mta_connector *c; | |||
| 1529 | size_t n, r; | |||
| 1530 | ||||
| 1531 | log_debug("debug: mta_flush(%s, %d, \"%s\")", | |||
| 1532 | mta_relay_to_text(relay), fail, error); | |||
| 1533 | ||||
| 1534 | if (fail != IMSG_MTA_DELIVERY_TEMPFAIL && fail != IMSG_MTA_DELIVERY_PERMFAIL) | |||
| 1535 | fatalx("unexpected delivery status %d", fail); | |||
| 1536 | ||||
| 1537 | n = 0; | |||
| 1538 | while ((task = TAILQ_FIRST(&relay->tasks)((&relay->tasks)->tqh_first))) { | |||
| 1539 | TAILQ_REMOVE(&relay->tasks, task, entry)do { if (((task)->entry.tqe_next) != ((void*)0)) (task)-> entry.tqe_next->entry.tqe_prev = (task)->entry.tqe_prev ; else (&relay->tasks)->tqh_last = (task)->entry .tqe_prev; *(task)->entry.tqe_prev = (task)->entry.tqe_next ; ; ; } while (0); | |||
| 1540 | while ((e = TAILQ_FIRST(&task->envelopes)((&task->envelopes)->tqh_first))) { | |||
| 1541 | TAILQ_REMOVE(&task->envelopes, e, entry)do { if (((e)->entry.tqe_next) != ((void*)0)) (e)->entry .tqe_next->entry.tqe_prev = (e)->entry.tqe_prev; else ( &task->envelopes)->tqh_last = (e)->entry.tqe_prev ; *(e)->entry.tqe_prev = (e)->entry.tqe_next; ; ; } while (0); | |||
| 1542 | ||||
| 1543 | /* | |||
| 1544 | * host was suspended, cache envelope id in hoststat tree | |||
| 1545 | * so that it can be retried when a delivery succeeds for | |||
| 1546 | * that domain. | |||
| 1547 | */ | |||
| 1548 | domain = strchr(e->dest, '@'); | |||
| 1549 | if (fail == IMSG_MTA_DELIVERY_TEMPFAIL && domain) { | |||
| 1550 | r = 0; | |||
| 1551 | iter = NULL((void*)0); | |||
| 1552 | while (tree_iter(&relay->connectors, &iter, | |||
| 1553 | NULL((void*)0), (void **)&c)) { | |||
| 1554 | if (c->flags & CONNECTOR_ERROR_ROUTE0x0018) | |||
| 1555 | r++; | |||
| 1556 | } | |||
| 1557 | if (tree_count(&relay->connectors)((&relay->connectors)->count) == r) | |||
| 1558 | mta_hoststat_cache(domain+1, e->id); | |||
| 1559 | } | |||
| 1560 | ||||
| 1561 | mta_delivery_log(e, NULL((void*)0), relay->domain->name, fail, error); | |||
| 1562 | mta_delivery_notify(e); | |||
| 1563 | ||||
| 1564 | n++; | |||
| 1565 | } | |||
| 1566 | free(task->sender); | |||
| 1567 | free(task); | |||
| 1568 | } | |||
| 1569 | ||||
| 1570 | stat_decrement("mta.task", relay->ntask); | |||
| 1571 | stat_decrement("mta.envelope", n); | |||
| 1572 | relay->ntask = 0; | |||
| 1573 | ||||
| 1574 | /* release all waiting envelopes for the relay */ | |||
| 1575 | if (relay->state & RELAY_HOLDQ0x02) { | |||
| 1576 | m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); | |||
| 1577 | m_add_id(p_queue, relay->id); | |||
| 1578 | m_add_int(p_queue, -1); | |||
| 1579 | m_close(p_queue); | |||
| 1580 | } | |||
| 1581 | } | |||
| 1582 | ||||
| 1583 | /* | |||
| 1584 | * Find a route to use for this connector | |||
| 1585 | */ | |||
| 1586 | static struct mta_route * | |||
| 1587 | mta_find_route(struct mta_connector *c, time_t now, int *limits, | |||
| 1588 | time_t *nextconn, struct mta_mx **pmx) | |||
| 1589 | { | |||
| 1590 | struct mta_route *route, *best; | |||
| 1591 | struct mta_limits *l = c->relay->limits; | |||
| 1592 | struct mta_mx *mx; | |||
| 1593 | int level, limit_host, limit_route; | |||
| 1594 | int family_mismatch, seen, suspended_route; | |||
| 1595 | time_t tm; | |||
| 1596 | ||||
| 1597 | log_debug("debug: mta-routing: searching new route for %s...", | |||
| 1598 | mta_connector_to_text(c)); | |||
| 1599 | ||||
| 1600 | tm = 0; | |||
| 1601 | limit_host = 0; | |||
| 1602 | limit_route = 0; | |||
| 1603 | suspended_route = 0; | |||
| 1604 | family_mismatch = 0; | |||
| 1605 | level = -1; | |||
| 1606 | best = NULL((void*)0); | |||
| 1607 | seen = 0; | |||
| 1608 | ||||
| 1609 | TAILQ_FOREACH(mx, &c->relay->domain->mxs, entry)for((mx) = ((&c->relay->domain->mxs)->tqh_first ); (mx) != ((void*)0); (mx) = ((mx)->entry.tqe_next)) { | |||
| 1610 | /* | |||
| 1611 | * New preference level | |||
| 1612 | */ | |||
| 1613 | if (mx->preference > level) { | |||
| 1614 | #ifndef IGNORE_MX_PREFERENCE | |||
| 1615 | /* | |||
| 1616 | * Use the current best MX if found. | |||
| 1617 | */ | |||
| 1618 | if (best) | |||
| 1619 | break; | |||
| 1620 | ||||
| 1621 | /* | |||
| 1622 | * No candidate found. There are valid MXs at this | |||
| 1623 | * preference level but they reached their limit, or | |||
| 1624 | * we can't connect yet. | |||
| 1625 | */ | |||
| 1626 | if (limit_host || limit_route || tm) | |||
| 1627 | break; | |||
| 1628 | ||||
| 1629 | /* | |||
| 1630 | * If we are a backup MX, do not relay to MXs with | |||
| 1631 | * a greater preference value. | |||
| 1632 | */ | |||
| 1633 | if (c->relay->backuppref >= 0 && | |||
| 1634 | mx->preference >= c->relay->backuppref) | |||
| 1635 | break; | |||
| 1636 | ||||
| 1637 | /* | |||
| 1638 | * Start looking at MXs on this preference level. | |||
| 1639 | */ | |||
| 1640 | #endif | |||
| 1641 | level = mx->preference; | |||
| 1642 | } | |||
| 1643 | ||||
| 1644 | if (mx->host->flags & HOST_IGNORE0x01) | |||
| 1645 | continue; | |||
| 1646 | ||||
| 1647 | /* Found a possibly valid mx */ | |||
| 1648 | seen++; | |||
| 1649 | ||||
| 1650 | if ((c->source->sa && | |||
| 1651 | c->source->sa->sa_family != mx->host->sa->sa_family) || | |||
| 1652 | (l->family && l->family != mx->host->sa->sa_family)) { | |||
| 1653 | log_debug("debug: mta-routing: skipping host %s: AF mismatch", | |||
| 1654 | mta_host_to_text(mx->host)); | |||
| 1655 | family_mismatch = 1; | |||
| 1656 | continue; | |||
| 1657 | } | |||
| 1658 | ||||
| 1659 | if (mx->host->nconn >= l->maxconn_per_host) { | |||
| 1660 | log_debug("debug: mta-routing: skipping host %s: too many connections", | |||
| 1661 | mta_host_to_text(mx->host)); | |||
| 1662 | limit_host = 1; | |||
| 1663 | continue; | |||
| 1664 | } | |||
| 1665 | ||||
| 1666 | if (mx->host->lastconn + l->conndelay_host > now) { | |||
| 1667 | log_debug("debug: mta-routing: skipping host %s: cannot use before %llus", | |||
| 1668 | mta_host_to_text(mx->host), | |||
| 1669 | (unsigned long long) mx->host->lastconn + l->conndelay_host - now); | |||
| 1670 | if (tm == 0 || mx->host->lastconn + l->conndelay_host < tm) | |||
| 1671 | tm = mx->host->lastconn + l->conndelay_host; | |||
| 1672 | continue; | |||
| 1673 | } | |||
| 1674 | ||||
| 1675 | route = mta_route(c->source, mx->host); | |||
| 1676 | ||||
| 1677 | if (route->flags & ROUTE_DISABLED0xf0) { | |||
| 1678 | log_debug("debug: mta-routing: skipping route %s: suspend", | |||
| 1679 | mta_route_to_text(route)); | |||
| 1680 | suspended_route |= route->flags & ROUTE_DISABLED0xf0; | |||
| 1681 | mta_route_unref(route); /* from here */ | |||
| 1682 | continue; | |||
| 1683 | } | |||
| 1684 | ||||
| 1685 | if (route->nconn && (route->flags & ROUTE_NEW0x01)) { | |||
| 1686 | log_debug("debug: mta-routing: skipping route %s: not validated yet", | |||
| 1687 | mta_route_to_text(route)); | |||
| 1688 | limit_route = 1; | |||
| 1689 | mta_route_unref(route); /* from here */ | |||
| 1690 | continue; | |||
| 1691 | } | |||
| 1692 | ||||
| 1693 | if (route->nconn >= l->maxconn_per_route) { | |||
| 1694 | log_debug("debug: mta-routing: skipping route %s: too many connections", | |||
| 1695 | mta_route_to_text(route)); | |||
| 1696 | limit_route = 1; | |||
| 1697 | mta_route_unref(route); /* from here */ | |||
| 1698 | continue; | |||
| 1699 | } | |||
| 1700 | ||||
| 1701 | if (route->lastconn + l->conndelay_route > now) { | |||
| 1702 | log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after connect)", | |||
| 1703 | mta_route_to_text(route), | |||
| 1704 | (unsigned long long) route->lastconn + l->conndelay_route - now); | |||
| 1705 | if (tm == 0 || route->lastconn + l->conndelay_route < tm) | |||
| 1706 | tm = route->lastconn + l->conndelay_route; | |||
| 1707 | mta_route_unref(route); /* from here */ | |||
| 1708 | continue; | |||
| 1709 | } | |||
| 1710 | ||||
| 1711 | if (route->lastdisc + l->discdelay_route > now) { | |||
| 1712 | log_debug("debug: mta-routing: skipping route %s: cannot use before %llus (delay after disconnect)", | |||
| 1713 | mta_route_to_text(route), | |||
| 1714 | (unsigned long long) route->lastdisc + l->discdelay_route - now); | |||
| 1715 | if (tm == 0 || route->lastdisc + l->discdelay_route < tm) | |||
| 1716 | tm = route->lastdisc + l->discdelay_route; | |||
| 1717 | mta_route_unref(route); /* from here */ | |||
| 1718 | continue; | |||
| 1719 | } | |||
| 1720 | ||||
| 1721 | /* Use the route with the lowest number of connections. */ | |||
| 1722 | if (best && route->nconn >= best->nconn) { | |||
| 1723 | log_debug("debug: mta-routing: skipping route %s: current one is better", | |||
| 1724 | mta_route_to_text(route)); | |||
| 1725 | mta_route_unref(route); /* from here */ | |||
| 1726 | continue; | |||
| 1727 | } | |||
| 1728 | ||||
| 1729 | if (best) | |||
| 1730 | mta_route_unref(best); /* from here */ | |||
| 1731 | best = route; | |||
| 1732 | *pmx = mx; | |||
| 1733 | log_debug("debug: mta-routing: selecting candidate route %s", | |||
| 1734 | mta_route_to_text(route)); | |||
| 1735 | } | |||
| 1736 | ||||
| 1737 | if (best) | |||
| 1738 | return (best); | |||
| 1739 | ||||
| 1740 | /* Order is important */ | |||
| 1741 | if (seen == 0) { | |||
| 1742 | log_info("smtp-out: No MX found for %s", | |||
| 1743 | mta_connector_to_text(c)); | |||
| 1744 | c->flags |= CONNECTOR_ERROR_MX0x0004; | |||
| 1745 | } | |||
| 1746 | else if (limit_route) { | |||
| 1747 | log_debug("debug: mta: hit route limit"); | |||
| 1748 | *limits |= CONNECTOR_LIMIT_ROUTE0x0200; | |||
| 1749 | } | |||
| 1750 | else if (limit_host) { | |||
| 1751 | log_debug("debug: mta: hit host limit"); | |||
| 1752 | *limits |= CONNECTOR_LIMIT_HOST0x0100; | |||
| 1753 | } | |||
| 1754 | else if (tm) { | |||
| 1755 | if (tm > *nextconn) | |||
| 1756 | *nextconn = tm; | |||
| 1757 | } | |||
| 1758 | else if (family_mismatch) { | |||
| 1759 | log_info("smtp-out: Address family mismatch on %s", | |||
| 1760 | mta_connector_to_text(c)); | |||
| 1761 | c->flags |= CONNECTOR_ERROR_FAMILY0x0001; | |||
| 1762 | } | |||
| 1763 | else if (suspended_route) { | |||
| 1764 | log_info("smtp-out: No valid route for %s", | |||
| 1765 | mta_connector_to_text(c)); | |||
| 1766 | if (suspended_route & ROUTE_DISABLED_NET0x10) | |||
| 1767 | c->flags |= CONNECTOR_ERROR_ROUTE_NET0x0008; | |||
| 1768 | if (suspended_route & ROUTE_DISABLED_SMTP0x20) | |||
| 1769 | c->flags |= CONNECTOR_ERROR_ROUTE_SMTP0x0010; | |||
| 1770 | } | |||
| 1771 | ||||
| 1772 | return (NULL((void*)0)); | |||
| 1773 | } | |||
| 1774 | ||||
| 1775 | static void | |||
| 1776 | mta_log(const struct mta_envelope *evp, const char *prefix, const char *source, | |||
| 1777 | const char *relay, const char *status) | |||
| 1778 | { | |||
| 1779 | log_info("%016"PRIx64"llx"" mta delivery evpid=%016"PRIx64"llx"" " | |||
| 1780 | "from=<%s> to=<%s> rcpt=<%s> source=\"%s\" " | |||
| 1781 | "relay=\"%s\" delay=%s result=\"%s\" stat=\"%s\"", | |||
| 1782 | evp->session, | |||
| 1783 | evp->id, | |||
| 1784 | evp->task->sender, | |||
| 1785 | evp->dest, | |||
| 1786 | evp->rcpt ? evp->rcpt : "-", | |||
| 1787 | source ? source : "-", | |||
| 1788 | relay, | |||
| 1789 | duration_to_text(time(NULL((void*)0)) - evp->creation), | |||
| 1790 | prefix, | |||
| 1791 | status); | |||
| 1792 | } | |||
| 1793 | ||||
| 1794 | static struct mta_relay * | |||
| 1795 | mta_relay(struct envelope *e, struct relayhost *relayh) | |||
| 1796 | { | |||
| 1797 | struct dispatcher *dispatcher; | |||
| 1798 | struct mta_relay key, *r; | |||
| 1799 | ||||
| 1800 | dispatcher = dict_xget(env->sc_dispatchers, e->dispatcher); | |||
| 1801 | ||||
| 1802 | memset(&key, 0, sizeof key); | |||
| 1803 | ||||
| 1804 | key.pki_name = dispatcher->u.remote.pki; | |||
| 1805 | key.ca_name = dispatcher->u.remote.ca; | |||
| 1806 | key.authtable = dispatcher->u.remote.auth; | |||
| 1807 | key.sourcetable = dispatcher->u.remote.source; | |||
| 1808 | key.helotable = dispatcher->u.remote.helo_source; | |||
| 1809 | key.heloname = dispatcher->u.remote.helo; | |||
| 1810 | key.srs = dispatcher->u.remote.srs; | |||
| 1811 | ||||
| 1812 | if (relayh->hostname[0]) { | |||
| 1813 | key.domain = mta_domain(relayh->hostname, 1); | |||
| 1814 | } | |||
| 1815 | else { | |||
| 1816 | key.domain = mta_domain(e->dest.domain, 0); | |||
| 1817 | if (dispatcher->u.remote.backup) { | |||
| 1818 | key.backupname = dispatcher->u.remote.backupmx; | |||
| 1819 | if (key.backupname == NULL((void*)0)) | |||
| 1820 | key.backupname = e->smtpname; | |||
| 1821 | } | |||
| 1822 | } | |||
| 1823 | ||||
| 1824 | key.tls = relayh->tls; | |||
| 1825 | key.flags |= relayh->flags; | |||
| 1826 | key.port = relayh->port; | |||
| 1827 | key.authlabel = relayh->authlabel; | |||
| 1828 | if (!key.authlabel[0]) | |||
| 1829 | key.authlabel = NULL((void*)0); | |||
| 1830 | ||||
| 1831 | if ((key.tls == RELAY_TLS_STARTTLS1 || key.tls == RELAY_TLS_SMTPS2) && | |||
| 1832 | dispatcher->u.remote.tls_noverify == 0) | |||
| 1833 | key.flags |= RELAY_TLS_VERIFY0x200; | |||
| 1834 | ||||
| 1835 | if ((r = SPLAY_FIND(mta_relay_tree, &relays, &key)mta_relay_tree_SPLAY_FIND(&relays, &key)) == NULL((void*)0)) { | |||
| 1836 | r = xcalloc(1, sizeof *r); | |||
| 1837 | TAILQ_INIT(&r->tasks)do { (&r->tasks)->tqh_first = ((void*)0); (&r-> tasks)->tqh_last = &(&r->tasks)->tqh_first; } while (0); | |||
| 1838 | r->id = generate_uid(); | |||
| 1839 | r->dispatcher = dispatcher; | |||
| 1840 | r->tls = key.tls; | |||
| 1841 | r->flags = key.flags; | |||
| 1842 | r->domain = key.domain; | |||
| 1843 | r->backupname = key.backupname ? | |||
| 1844 | xstrdup(key.backupname) : NULL((void*)0); | |||
| 1845 | r->backuppref = -1; | |||
| 1846 | r->port = key.port; | |||
| 1847 | r->pki_name = key.pki_name ? xstrdup(key.pki_name) : NULL((void*)0); | |||
| 1848 | r->ca_name = key.ca_name ? xstrdup(key.ca_name) : NULL((void*)0); | |||
| 1849 | if (key.authtable) | |||
| 1850 | r->authtable = xstrdup(key.authtable); | |||
| 1851 | if (key.authlabel) | |||
| 1852 | r->authlabel = xstrdup(key.authlabel); | |||
| 1853 | if (key.sourcetable) | |||
| 1854 | r->sourcetable = xstrdup(key.sourcetable); | |||
| 1855 | if (key.helotable) | |||
| 1856 | r->helotable = xstrdup(key.helotable); | |||
| 1857 | if (key.heloname) | |||
| 1858 | r->heloname = xstrdup(key.heloname); | |||
| 1859 | r->srs = key.srs; | |||
| 1860 | SPLAY_INSERT(mta_relay_tree, &relays, r)mta_relay_tree_SPLAY_INSERT(&relays, r); | |||
| 1861 | stat_increment("mta.relay", 1); | |||
| 1862 | } else { | |||
| 1863 | mta_domain_unref(key.domain); /* from here */ | |||
| 1864 | } | |||
| 1865 | ||||
| 1866 | r->refcount++; | |||
| 1867 | return (r); | |||
| 1868 | } | |||
| 1869 | ||||
| 1870 | static void | |||
| 1871 | mta_relay_ref(struct mta_relay *r) | |||
| 1872 | { | |||
| 1873 | r->refcount++; | |||
| 1874 | } | |||
| 1875 | ||||
| 1876 | static void | |||
| 1877 | mta_relay_unref(struct mta_relay *relay) | |||
| 1878 | { | |||
| 1879 | struct mta_connector *c; | |||
| 1880 | ||||
| 1881 | if (--relay->refcount) | |||
| 1882 | return; | |||
| 1883 | ||||
| 1884 | /* Make sure they are no envelopes held for this relay */ | |||
| 1885 | if (relay->state & RELAY_HOLDQ0x02) { | |||
| 1886 | m_create(p_queue, IMSG_MTA_HOLDQ_RELEASE, 0, 0, -1); | |||
| 1887 | m_add_id(p_queue, relay->id); | |||
| 1888 | m_add_int(p_queue, 0); | |||
| 1889 | m_close(p_queue); | |||
| 1890 | } | |||
| 1891 | ||||
| 1892 | log_debug("debug: mta: freeing %s", mta_relay_to_text(relay)); | |||
| 1893 | SPLAY_REMOVE(mta_relay_tree, &relays, relay)mta_relay_tree_SPLAY_REMOVE(&relays, relay); | |||
| 1894 | ||||
| 1895 | while ((tree_poproot(&relay->connectors, NULL((void*)0), (void**)&c))) | |||
| 1896 | mta_connector_free(c); | |||
| 1897 | ||||
| 1898 | free(relay->authlabel); | |||
| 1899 | free(relay->authtable); | |||
| 1900 | free(relay->backupname); | |||
| 1901 | free(relay->pki_name); | |||
| 1902 | free(relay->ca_name); | |||
| 1903 | free(relay->helotable); | |||
| 1904 | free(relay->heloname); | |||
| 1905 | free(relay->secret); | |||
| 1906 | free(relay->sourcetable); | |||
| 1907 | ||||
| 1908 | mta_domain_unref(relay->domain); /* from constructor */ | |||
| 1909 | free(relay); | |||
| 1910 | stat_decrement("mta.relay", 1); | |||
| 1911 | } | |||
| 1912 | ||||
| 1913 | const char * | |||
| 1914 | mta_relay_to_text(struct mta_relay *relay) | |||
| 1915 | { | |||
| 1916 | static char buf[1024]; | |||
| 1917 | char tmp[32]; | |||
| 1918 | const char *sep = ","; | |||
| 1919 | ||||
| 1920 | (void)snprintf(buf, sizeof buf, "[relay:%s", relay->domain->name); | |||
| 1921 | ||||
| 1922 | if (relay->port) { | |||
| 1923 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1924 | (void)snprintf(tmp, sizeof tmp, "port=%d", (int)relay->port); | |||
| 1925 | (void)strlcat(buf, tmp, sizeof buf); | |||
| 1926 | } | |||
| 1927 | ||||
| 1928 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1929 | switch(relay->tls) { | |||
| 1930 | case RELAY_TLS_OPPORTUNISTIC0: | |||
| 1931 | (void)strlcat(buf, "smtp", sizeof buf); | |||
| 1932 | break; | |||
| 1933 | case RELAY_TLS_STARTTLS1: | |||
| 1934 | (void)strlcat(buf, "smtp+tls", sizeof buf); | |||
| 1935 | break; | |||
| 1936 | case RELAY_TLS_SMTPS2: | |||
| 1937 | (void)strlcat(buf, "smtps", sizeof buf); | |||
| 1938 | break; | |||
| 1939 | case RELAY_TLS_NO3: | |||
| 1940 | if (relay->flags & RELAY_LMTP0x80) | |||
| 1941 | (void)strlcat(buf, "lmtp", sizeof buf); | |||
| 1942 | else | |||
| 1943 | (void)strlcat(buf, "smtp+notls", sizeof buf); | |||
| 1944 | break; | |||
| 1945 | default: | |||
| 1946 | (void)strlcat(buf, "???", sizeof buf); | |||
| 1947 | } | |||
| 1948 | ||||
| 1949 | if (relay->flags & RELAY_AUTH0x08) { | |||
| 1950 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1951 | (void)strlcat(buf, "auth=", sizeof buf); | |||
| 1952 | (void)strlcat(buf, relay->authtable, sizeof buf); | |||
| 1953 | (void)strlcat(buf, ":", sizeof buf); | |||
| 1954 | (void)strlcat(buf, relay->authlabel, sizeof buf); | |||
| 1955 | } | |||
| 1956 | ||||
| 1957 | if (relay->pki_name) { | |||
| 1958 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1959 | (void)strlcat(buf, "pki_name=", sizeof buf); | |||
| 1960 | (void)strlcat(buf, relay->pki_name, sizeof buf); | |||
| 1961 | } | |||
| 1962 | ||||
| 1963 | if (relay->domain->as_host) { | |||
| 1964 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1965 | (void)strlcat(buf, "mx", sizeof buf); | |||
| 1966 | } | |||
| 1967 | ||||
| 1968 | if (relay->backupname) { | |||
| 1969 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1970 | (void)strlcat(buf, "backup=", sizeof buf); | |||
| 1971 | (void)strlcat(buf, relay->backupname, sizeof buf); | |||
| 1972 | } | |||
| 1973 | ||||
| 1974 | if (relay->sourcetable) { | |||
| 1975 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1976 | (void)strlcat(buf, "sourcetable=", sizeof buf); | |||
| 1977 | (void)strlcat(buf, relay->sourcetable, sizeof buf); | |||
| 1978 | } | |||
| 1979 | ||||
| 1980 | if (relay->helotable) { | |||
| 1981 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1982 | (void)strlcat(buf, "helotable=", sizeof buf); | |||
| 1983 | (void)strlcat(buf, relay->helotable, sizeof buf); | |||
| 1984 | } | |||
| 1985 | ||||
| 1986 | if (relay->heloname) { | |||
| 1987 | (void)strlcat(buf, sep, sizeof buf); | |||
| 1988 | (void)strlcat(buf, "heloname=", sizeof buf); | |||
| 1989 | (void)strlcat(buf, relay->heloname, sizeof buf); | |||
| 1990 | } | |||
| 1991 | ||||
| 1992 | (void)strlcat(buf, "]", sizeof buf); | |||
| 1993 | ||||
| 1994 | return (buf); | |||
| 1995 | } | |||
| 1996 | ||||
| 1997 | static void | |||
| 1998 | mta_relay_show(struct mta_relay *r, struct mproc *p, uint32_t id, time_t t) | |||
| 1999 | { | |||
| 2000 | struct mta_connector *c; | |||
| 2001 | void *iter; | |||
| 2002 | char buf[1024], flags[1024], dur[64]; | |||
| 2003 | time_t to; | |||
| 2004 | ||||
| 2005 | flags[0] = '\0'; | |||
| 2006 | ||||
| 2007 | #define SHOWSTATUS(f, n) do { \ | |||
| 2008 | if (r->status & (f)) { \ | |||
| 2009 | if (flags[0]) \ | |||
| 2010 | (void)strlcat(flags, ",", sizeof(flags)); \ | |||
| 2011 | (void)strlcat(flags, (n), sizeof(flags)); \ | |||
| 2012 | } \ | |||
| 2013 | } while(0) | |||
| 2014 | ||||
| 2015 | SHOWSTATUS(RELAY_WAIT_MX0x01, "MX"); | |||
| 2016 | SHOWSTATUS(RELAY_WAIT_PREFERENCE0x02, "preference"); | |||
| 2017 | SHOWSTATUS(RELAY_WAIT_SECRET0x04, "secret"); | |||
| 2018 | SHOWSTATUS(RELAY_WAIT_LIMITS0x08, "limits"); | |||
| 2019 | SHOWSTATUS(RELAY_WAIT_SOURCE0x10, "source"); | |||
| 2020 | SHOWSTATUS(RELAY_WAIT_CONNECTOR0x20, "connector"); | |||
| 2021 | #undef SHOWSTATUS | |||
| 2022 | ||||
| 2023 | if (runq_pending(runq_relay, r, &to)) | |||
| 2024 | (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t)); | |||
| 2025 | else | |||
| 2026 | (void)strlcpy(dur, "-", sizeof(dur)); | |||
| 2027 | ||||
| 2028 | (void)snprintf(buf, sizeof(buf), "%s refcount=%d ntask=%zu nconn=%zu lastconn=%s timeout=%s wait=%s%s", | |||
| 2029 | mta_relay_to_text(r), | |||
| 2030 | r->refcount, | |||
| 2031 | r->ntask, | |||
| 2032 | r->nconn, | |||
| 2033 | r->lastconn ? duration_to_text(t - r->lastconn) : "-", | |||
| 2034 | dur, | |||
| 2035 | flags, | |||
| 2036 | (r->state & RELAY_ONHOLD0x01) ? "ONHOLD" : ""); | |||
| 2037 | m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, strlen(buf) + 1); | |||
| 2038 | ||||
| 2039 | iter = NULL((void*)0); | |||
| 2040 | while (tree_iter(&r->connectors, &iter, NULL((void*)0), (void **)&c)) { | |||
| 2041 | ||||
| 2042 | if (runq_pending(runq_connector, c, &to)) | |||
| 2043 | (void)snprintf(dur, sizeof(dur), "%s", duration_to_text(to - t)); | |||
| 2044 | else | |||
| 2045 | (void)strlcpy(dur, "-", sizeof(dur)); | |||
| 2046 | ||||
| 2047 | flags[0] = '\0'; | |||
| 2048 | ||||
| 2049 | #define SHOWFLAG(f, n) do { \ | |||
| 2050 | if (c->flags & (f)) { \ | |||
| 2051 | if (flags[0]) \ | |||
| 2052 | (void)strlcat(flags, ",", sizeof(flags)); \ | |||
| 2053 | (void)strlcat(flags, (n), sizeof(flags)); \ | |||
| 2054 | } \ | |||
| 2055 | } while(0) | |||
| 2056 | ||||
| 2057 | SHOWFLAG(CONNECTOR_NEW0x10000, "NEW"); | |||
| 2058 | SHOWFLAG(CONNECTOR_WAIT0x20000, "WAIT"); | |||
| 2059 | ||||
| 2060 | SHOWFLAG(CONNECTOR_ERROR_FAMILY0x0001, "ERROR_FAMILY"); | |||
| 2061 | SHOWFLAG(CONNECTOR_ERROR_SOURCE0x0002, "ERROR_SOURCE"); | |||
| 2062 | SHOWFLAG(CONNECTOR_ERROR_MX0x0004, "ERROR_MX"); | |||
| 2063 | SHOWFLAG(CONNECTOR_ERROR_ROUTE_NET0x0008, "ERROR_ROUTE_NET"); | |||
| 2064 | SHOWFLAG(CONNECTOR_ERROR_ROUTE_SMTP0x0010, "ERROR_ROUTE_SMTP"); | |||
| 2065 | SHOWFLAG(CONNECTOR_ERROR_BLOCKED0x0020, "ERROR_BLOCKED"); | |||
| 2066 | ||||
| 2067 | SHOWFLAG(CONNECTOR_LIMIT_HOST0x0100, "LIMIT_HOST"); | |||
| 2068 | SHOWFLAG(CONNECTOR_LIMIT_ROUTE0x0200, "LIMIT_ROUTE"); | |||
| 2069 | SHOWFLAG(CONNECTOR_LIMIT_SOURCE0x0400, "LIMIT_SOURCE"); | |||
| 2070 | SHOWFLAG(CONNECTOR_LIMIT_RELAY0x0800, "LIMIT_RELAY"); | |||
| 2071 | SHOWFLAG(CONNECTOR_LIMIT_CONN0x1000, "LIMIT_CONN"); | |||
| 2072 | SHOWFLAG(CONNECTOR_LIMIT_DOMAIN0x2000, "LIMIT_DOMAIN"); | |||
| 2073 | #undef SHOWFLAG | |||
| 2074 | ||||
| 2075 | (void)snprintf(buf, sizeof(buf), | |||
| 2076 | " connector %s refcount=%d nconn=%zu lastconn=%s timeout=%s flags=%s", | |||
| 2077 | mta_source_to_text(c->source), | |||
| 2078 | c->refcount, | |||
| 2079 | c->nconn, | |||
| 2080 | c->lastconn ? duration_to_text(t - c->lastconn) : "-", | |||
| 2081 | dur, | |||
| 2082 | flags); | |||
| 2083 | m_compose(p, IMSG_CTL_MTA_SHOW_RELAYS, id, 0, -1, buf, | |||
| 2084 | strlen(buf) + 1); | |||
| 2085 | ||||
| 2086 | ||||
| 2087 | } | |||
| 2088 | } | |||
| 2089 | ||||
| 2090 | static int | |||
| 2091 | mta_relay_cmp(const struct mta_relay *a, const struct mta_relay *b) | |||
| 2092 | { | |||
| 2093 | int r; | |||
| 2094 | ||||
| 2095 | if (a->domain < b->domain) | |||
| 2096 | return (-1); | |||
| 2097 | if (a->domain > b->domain) | |||
| 2098 | return (1); | |||
| 2099 | ||||
| 2100 | if (a->tls < b->tls) | |||
| 2101 | return (-1); | |||
| 2102 | if (a->tls > b->tls) | |||
| 2103 | return (1); | |||
| 2104 | ||||
| 2105 | if (a->flags < b->flags) | |||
| 2106 | return (-1); | |||
| 2107 | if (a->flags > b->flags) | |||
| 2108 | return (1); | |||
| 2109 | ||||
| 2110 | if (a->port < b->port) | |||
| 2111 | return (-1); | |||
| 2112 | if (a->port > b->port) | |||
| 2113 | return (1); | |||
| 2114 | ||||
| 2115 | if (a->authtable == NULL((void*)0) && b->authtable) | |||
| 2116 | return (-1); | |||
| 2117 | if (a->authtable && b->authtable == NULL((void*)0)) | |||
| 2118 | return (1); | |||
| 2119 | if (a->authtable && ((r = strcmp(a->authtable, b->authtable)))) | |||
| 2120 | return (r); | |||
| 2121 | if (a->authlabel == NULL((void*)0) && b->authlabel) | |||
| 2122 | return (-1); | |||
| 2123 | if (a->authlabel && b->authlabel == NULL((void*)0)) | |||
| 2124 | return (1); | |||
| 2125 | if (a->authlabel && ((r = strcmp(a->authlabel, b->authlabel)))) | |||
| 2126 | return (r); | |||
| 2127 | if (a->sourcetable == NULL((void*)0) && b->sourcetable) | |||
| 2128 | return (-1); | |||
| 2129 | if (a->sourcetable && b->sourcetable == NULL((void*)0)) | |||
| 2130 | return (1); | |||
| 2131 | if (a->sourcetable && ((r = strcmp(a->sourcetable, b->sourcetable)))) | |||
| 2132 | return (r); | |||
| 2133 | if (a->helotable == NULL((void*)0) && b->helotable) | |||
| 2134 | return (-1); | |||
| 2135 | if (a->helotable && b->helotable == NULL((void*)0)) | |||
| 2136 | return (1); | |||
| 2137 | if (a->helotable && ((r = strcmp(a->helotable, b->helotable)))) | |||
| 2138 | return (r); | |||
| 2139 | if (a->heloname == NULL((void*)0) && b->heloname) | |||
| 2140 | return (-1); | |||
| 2141 | if (a->heloname && b->heloname == NULL((void*)0)) | |||
| 2142 | return (1); | |||
| 2143 | if (a->heloname && ((r = strcmp(a->heloname, b->heloname)))) | |||
| 2144 | return (r); | |||
| 2145 | ||||
| 2146 | if (a->pki_name == NULL((void*)0) && b->pki_name) | |||
| 2147 | return (-1); | |||
| 2148 | if (a->pki_name && b->pki_name == NULL((void*)0)) | |||
| 2149 | return (1); | |||
| 2150 | if (a->pki_name && ((r = strcmp(a->pki_name, b->pki_name)))) | |||
| 2151 | return (r); | |||
| 2152 | ||||
| 2153 | if (a->ca_name == NULL((void*)0) && b->ca_name) | |||
| 2154 | return (-1); | |||
| 2155 | if (a->ca_name && b->ca_name == NULL((void*)0)) | |||
| 2156 | return (1); | |||
| 2157 | if (a->ca_name && ((r = strcmp(a->ca_name, b->ca_name)))) | |||
| 2158 | return (r); | |||
| 2159 | ||||
| 2160 | if (a->backupname == NULL((void*)0) && b->backupname) | |||
| 2161 | return (-1); | |||
| 2162 | if (a->backupname && b->backupname == NULL((void*)0)) | |||
| 2163 | return (1); | |||
| 2164 | if (a->backupname && ((r = strcmp(a->backupname, b->backupname)))) | |||
| 2165 | return (r); | |||
| 2166 | ||||
| 2167 | if (a->srs < b->srs) | |||
| 2168 | return (-1); | |||
| 2169 | if (a->srs > b->srs) | |||
| 2170 | return (1); | |||
| 2171 | ||||
| 2172 | return (0); | |||
| 2173 | } | |||
| 2174 | ||||
| 2175 | SPLAY_GENERATE(mta_relay_tree, mta_relay, entry, mta_relay_cmp)struct mta_relay * mta_relay_tree_SPLAY_INSERT(struct mta_relay_tree *head, struct mta_relay *elm) { if (((head)->sph_root == ( (void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_relay_tree_SPLAY(head , elm); __comp = (mta_relay_cmp)(elm, (head)->sph_root); if (__comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_relay * mta_relay_tree_SPLAY_REMOVE (struct mta_relay_tree *head, struct mta_relay *elm) { struct mta_relay *__tmp; if (((head)->sph_root == ((void*)0))) return (((void*)0)); mta_relay_tree_SPLAY(head, elm); if ((mta_relay_cmp )(elm, (head)->sph_root) == 0) { if (((head)->sph_root) ->entry.spe_left == ((void*)0)) { (head)->sph_root = (( head)->sph_root)->entry.spe_right; } else { __tmp = ((head )->sph_root)->entry.spe_right; (head)->sph_root = (( head)->sph_root)->entry.spe_left; mta_relay_tree_SPLAY( head, elm); ((head)->sph_root)->entry.spe_right = __tmp ; } return (elm); } return (((void*)0)); } void mta_relay_tree_SPLAY (struct mta_relay_tree *head, struct mta_relay *elm) { struct mta_relay __node, *__left, *__right, *__tmp; int __comp; (& __node)->entry.spe_left = (&__node)->entry.spe_right = ((void*)0); __left = __right = &__node; while ((__comp = (mta_relay_cmp)(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ((head)->sph_root)->entry.spe_left; if (__tmp == ((void*)0)) break; if ((mta_relay_cmp)(elm, __tmp) < 0 ){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_relay_cmp)(elm, __tmp) > 0){ do { ((head)->sph_root )->entry.spe_right = (__tmp)->entry.spe_left; (__tmp)-> entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = (head )->sph_root; __left = (head)->sph_root; (head)->sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root)-> entry.spe_left; (__right)->entry.spe_left = ((head)->sph_root )->entry.spe_right; ((head)->sph_root)->entry.spe_left = (&__node)->entry.spe_right; ((head)->sph_root)-> entry.spe_right = (&__node)->entry.spe_left; } while ( 0); } void mta_relay_tree_SPLAY_MINMAX(struct mta_relay_tree * head, int __comp) { struct mta_relay __node, *__left, *__right , *__tmp; (&__node)->entry.spe_left = (&__node)-> entry.spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root )->entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp )->entry.spe_right; (__tmp)->entry.spe_right = (head)-> sph_root; (head)->sph_root = __tmp; } while (0); if (((head )->sph_root)->entry.spe_left == ((void*)0)) break; } do { (__right)->entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head)->sph_root = ((head)->sph_root )->entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break; if (__comp > 0) { do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2176 | ||||
| 2177 | static struct mta_host * | |||
| 2178 | mta_host(const struct sockaddr *sa) | |||
| 2179 | { | |||
| 2180 | struct mta_host key, *h; | |||
| 2181 | struct sockaddr_storage ss; | |||
| 2182 | ||||
| 2183 | memmove(&ss, sa, sa->sa_len); | |||
| 2184 | key.sa = (struct sockaddr*)&ss; | |||
| 2185 | h = SPLAY_FIND(mta_host_tree, &hosts, &key)mta_host_tree_SPLAY_FIND(&hosts, &key); | |||
| 2186 | ||||
| 2187 | if (h == NULL((void*)0)) { | |||
| 2188 | h = xcalloc(1, sizeof(*h)); | |||
| 2189 | h->sa = xmemdup(sa, sa->sa_len); | |||
| 2190 | SPLAY_INSERT(mta_host_tree, &hosts, h)mta_host_tree_SPLAY_INSERT(&hosts, h); | |||
| 2191 | stat_increment("mta.host", 1); | |||
| 2192 | } | |||
| 2193 | ||||
| 2194 | h->refcount++; | |||
| 2195 | return (h); | |||
| 2196 | } | |||
| 2197 | ||||
| 2198 | static void | |||
| 2199 | mta_host_ref(struct mta_host *h) | |||
| 2200 | { | |||
| 2201 | h->refcount++; | |||
| 2202 | } | |||
| 2203 | ||||
| 2204 | static void | |||
| 2205 | mta_host_unref(struct mta_host *h) | |||
| 2206 | { | |||
| 2207 | if (--h->refcount) | |||
| 2208 | return; | |||
| 2209 | ||||
| 2210 | SPLAY_REMOVE(mta_host_tree, &hosts, h)mta_host_tree_SPLAY_REMOVE(&hosts, h); | |||
| 2211 | free(h->sa); | |||
| 2212 | free(h->ptrname); | |||
| 2213 | free(h); | |||
| 2214 | stat_decrement("mta.host", 1); | |||
| 2215 | } | |||
| 2216 | ||||
| 2217 | const char * | |||
| 2218 | mta_host_to_text(struct mta_host *h) | |||
| 2219 | { | |||
| 2220 | static char buf[1024]; | |||
| 2221 | ||||
| 2222 | if (h->ptrname) | |||
| 2223 | (void)snprintf(buf, sizeof buf, "%s (%s)", | |||
| 2224 | sa_to_text(h->sa), h->ptrname); | |||
| 2225 | else | |||
| 2226 | (void)snprintf(buf, sizeof buf, "%s", sa_to_text(h->sa)); | |||
| 2227 | ||||
| 2228 | return (buf); | |||
| 2229 | } | |||
| 2230 | ||||
| 2231 | static int | |||
| 2232 | mta_host_cmp(const struct mta_host *a, const struct mta_host *b) | |||
| 2233 | { | |||
| 2234 | if (a->sa->sa_len < b->sa->sa_len) | |||
| 2235 | return (-1); | |||
| 2236 | if (a->sa->sa_len > b->sa->sa_len) | |||
| 2237 | return (1); | |||
| 2238 | return (memcmp(a->sa, b->sa, a->sa->sa_len)); | |||
| 2239 | } | |||
| 2240 | ||||
| 2241 | SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp)struct mta_host * mta_host_tree_SPLAY_INSERT(struct mta_host_tree *head, struct mta_host *elm) { if (((head)->sph_root == ( (void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_host_tree_SPLAY(head, elm); __comp = (mta_host_cmp)(elm, (head)->sph_root); if( __comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_host * mta_host_tree_SPLAY_REMOVE (struct mta_host_tree *head, struct mta_host *elm) { struct mta_host *__tmp; if (((head)->sph_root == ((void*)0))) return (((void *)0)); mta_host_tree_SPLAY(head, elm); if ((mta_host_cmp)(elm , (head)->sph_root) == 0) { if (((head)->sph_root)-> entry.spe_left == ((void*)0)) { (head)->sph_root = ((head) ->sph_root)->entry.spe_right; } else { __tmp = ((head)-> sph_root)->entry.spe_right; (head)->sph_root = ((head)-> sph_root)->entry.spe_left; mta_host_tree_SPLAY(head, elm); ((head)->sph_root)->entry.spe_right = __tmp; } return ( elm); } return (((void*)0)); } void mta_host_tree_SPLAY(struct mta_host_tree *head, struct mta_host *elm) { struct mta_host __node, *__left, *__right, *__tmp; int __comp; (&__node) ->entry.spe_left = (&__node)->entry.spe_right = ((void *)0); __left = __right = &__node; while ((__comp = (mta_host_cmp )(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ( (head)->sph_root)->entry.spe_left; if (__tmp == ((void* )0)) break; if ((mta_host_cmp)(elm, __tmp) < 0){ do { ((head )->sph_root)->entry.spe_left = (__tmp)->entry.spe_right ; (__tmp)->entry.spe_right = (head)->sph_root; (head)-> sph_root = __tmp; } while (0); if (((head)->sph_root)-> entry.spe_left == ((void*)0)) break; } do { (__right)->entry .spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_host_cmp)(elm, __tmp) > 0){ do { ((head)->sph_root )->entry.spe_right = (__tmp)->entry.spe_left; (__tmp)-> entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = (head )->sph_root; __left = (head)->sph_root; (head)->sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root)-> entry.spe_left; (__right)->entry.spe_left = ((head)->sph_root )->entry.spe_right; ((head)->sph_root)->entry.spe_left = (&__node)->entry.spe_right; ((head)->sph_root)-> entry.spe_right = (&__node)->entry.spe_left; } while ( 0); } void mta_host_tree_SPLAY_MINMAX(struct mta_host_tree *head , int __comp) { struct mta_host __node, *__left, *__right, *__tmp ; (&__node)->entry.spe_left = (&__node)->entry. spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root)-> entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if (__comp > 0) { do { ((head)->sph_root)->entry.spe_right = (__tmp)->entry.spe_left; (__tmp)->entry.spe_left = ( head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = (head)->sph_root ; __left = (head)->sph_root; (head)->sph_root = ((head) ->sph_root)->entry.spe_right; } while (0); } } do { (__left )->entry.spe_right = ((head)->sph_root)->entry.spe_left ; (__right)->entry.spe_left = ((head)->sph_root)->entry .spe_right; ((head)->sph_root)->entry.spe_left = (& __node)->entry.spe_right; ((head)->sph_root)->entry. spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2242 | ||||
| 2243 | static struct mta_domain * | |||
| 2244 | mta_domain(char *name, int as_host) | |||
| 2245 | { | |||
| 2246 | struct mta_domain key, *d; | |||
| 2247 | ||||
| 2248 | key.name = name; | |||
| 2249 | key.as_host = as_host; | |||
| 2250 | d = SPLAY_FIND(mta_domain_tree, &domains, &key)mta_domain_tree_SPLAY_FIND(&domains, &key); | |||
| 2251 | ||||
| 2252 | if (d == NULL((void*)0)) { | |||
| 2253 | d = xcalloc(1, sizeof(*d)); | |||
| 2254 | d->name = xstrdup(name); | |||
| 2255 | d->as_host = as_host; | |||
| 2256 | TAILQ_INIT(&d->mxs)do { (&d->mxs)->tqh_first = ((void*)0); (&d-> mxs)->tqh_last = &(&d->mxs)->tqh_first; } while (0); | |||
| 2257 | SPLAY_INSERT(mta_domain_tree, &domains, d)mta_domain_tree_SPLAY_INSERT(&domains, d); | |||
| 2258 | stat_increment("mta.domain", 1); | |||
| 2259 | } | |||
| 2260 | ||||
| 2261 | d->refcount++; | |||
| 2262 | return (d); | |||
| 2263 | } | |||
| 2264 | ||||
| 2265 | #if 0 | |||
| 2266 | static void | |||
| 2267 | mta_domain_ref(struct mta_domain *d) | |||
| 2268 | { | |||
| 2269 | d->refcount++; | |||
| 2270 | } | |||
| 2271 | #endif | |||
| 2272 | ||||
| 2273 | static void | |||
| 2274 | mta_domain_unref(struct mta_domain *d) | |||
| 2275 | { | |||
| 2276 | struct mta_mx *mx; | |||
| 2277 | ||||
| 2278 | if (--d->refcount) | |||
| 2279 | return; | |||
| 2280 | ||||
| 2281 | while ((mx = TAILQ_FIRST(&d->mxs)((&d->mxs)->tqh_first))) { | |||
| 2282 | TAILQ_REMOVE(&d->mxs, mx, entry)do { if (((mx)->entry.tqe_next) != ((void*)0)) (mx)->entry .tqe_next->entry.tqe_prev = (mx)->entry.tqe_prev; else ( &d->mxs)->tqh_last = (mx)->entry.tqe_prev; *(mx) ->entry.tqe_prev = (mx)->entry.tqe_next; ; ; } while (0 ); | |||
| 2283 | mta_host_unref(mx->host); /* from IMSG_DNS_HOST */ | |||
| 2284 | free(mx->mxname); | |||
| 2285 | free(mx); | |||
| 2286 | } | |||
| 2287 | ||||
| 2288 | SPLAY_REMOVE(mta_domain_tree, &domains, d)mta_domain_tree_SPLAY_REMOVE(&domains, d); | |||
| 2289 | free(d->name); | |||
| 2290 | free(d); | |||
| 2291 | stat_decrement("mta.domain", 1); | |||
| 2292 | } | |||
| 2293 | ||||
| 2294 | static int | |||
| 2295 | mta_domain_cmp(const struct mta_domain *a, const struct mta_domain *b) | |||
| 2296 | { | |||
| 2297 | if (a->as_host < b->as_host) | |||
| 2298 | return (-1); | |||
| 2299 | if (a->as_host > b->as_host) | |||
| 2300 | return (1); | |||
| 2301 | return (strcasecmp(a->name, b->name)); | |||
| 2302 | } | |||
| 2303 | ||||
| 2304 | SPLAY_GENERATE(mta_domain_tree, mta_domain, entry, mta_domain_cmp)struct mta_domain * mta_domain_tree_SPLAY_INSERT(struct mta_domain_tree *head, struct mta_domain *elm) { if (((head)->sph_root == ((void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_domain_tree_SPLAY(head , elm); __comp = (mta_domain_cmp)(elm, (head)->sph_root); if (__comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_domain * mta_domain_tree_SPLAY_REMOVE (struct mta_domain_tree *head, struct mta_domain *elm) { struct mta_domain *__tmp; if (((head)->sph_root == ((void*)0))) return (((void*)0)); mta_domain_tree_SPLAY(head, elm); if ((mta_domain_cmp )(elm, (head)->sph_root) == 0) { if (((head)->sph_root) ->entry.spe_left == ((void*)0)) { (head)->sph_root = (( head)->sph_root)->entry.spe_right; } else { __tmp = ((head )->sph_root)->entry.spe_right; (head)->sph_root = (( head)->sph_root)->entry.spe_left; mta_domain_tree_SPLAY (head, elm); ((head)->sph_root)->entry.spe_right = __tmp ; } return (elm); } return (((void*)0)); } void mta_domain_tree_SPLAY (struct mta_domain_tree *head, struct mta_domain *elm) { struct mta_domain __node, *__left, *__right, *__tmp; int __comp; (& __node)->entry.spe_left = (&__node)->entry.spe_right = ((void*)0); __left = __right = &__node; while ((__comp = (mta_domain_cmp)(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ((head)->sph_root)->entry.spe_left; if (__tmp == ((void*)0)) break; if ((mta_domain_cmp)(elm, __tmp) < 0 ){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_domain_cmp)(elm, __tmp) > 0){ do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); } void mta_domain_tree_SPLAY_MINMAX(struct mta_domain_tree *head, int __comp) { struct mta_domain __node, *__left, *__right , *__tmp; (&__node)->entry.spe_left = (&__node)-> entry.spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root )->entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp )->entry.spe_right; (__tmp)->entry.spe_right = (head)-> sph_root; (head)->sph_root = __tmp; } while (0); if (((head )->sph_root)->entry.spe_left == ((void*)0)) break; } do { (__right)->entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head)->sph_root = ((head)->sph_root )->entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break; if (__comp > 0) { do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2305 | ||||
| 2306 | static struct mta_source * | |||
| 2307 | mta_source(const struct sockaddr *sa) | |||
| 2308 | { | |||
| 2309 | struct mta_source key, *s; | |||
| 2310 | struct sockaddr_storage ss; | |||
| 2311 | ||||
| 2312 | if (sa) { | |||
| 2313 | memmove(&ss, sa, sa->sa_len); | |||
| 2314 | key.sa = (struct sockaddr*)&ss; | |||
| 2315 | } else | |||
| 2316 | key.sa = NULL((void*)0); | |||
| 2317 | s = SPLAY_FIND(mta_source_tree, &sources, &key)mta_source_tree_SPLAY_FIND(&sources, &key); | |||
| 2318 | ||||
| 2319 | if (s == NULL((void*)0)) { | |||
| 2320 | s = xcalloc(1, sizeof(*s)); | |||
| 2321 | if (sa) | |||
| 2322 | s->sa = xmemdup(sa, sa->sa_len); | |||
| 2323 | SPLAY_INSERT(mta_source_tree, &sources, s)mta_source_tree_SPLAY_INSERT(&sources, s); | |||
| 2324 | stat_increment("mta.source", 1); | |||
| 2325 | } | |||
| 2326 | ||||
| 2327 | s->refcount++; | |||
| 2328 | return (s); | |||
| 2329 | } | |||
| 2330 | ||||
| 2331 | static void | |||
| 2332 | mta_source_ref(struct mta_source *s) | |||
| 2333 | { | |||
| 2334 | s->refcount++; | |||
| 2335 | } | |||
| 2336 | ||||
| 2337 | static void | |||
| 2338 | mta_source_unref(struct mta_source *s) | |||
| 2339 | { | |||
| 2340 | if (--s->refcount) | |||
| 2341 | return; | |||
| 2342 | ||||
| 2343 | SPLAY_REMOVE(mta_source_tree, &sources, s)mta_source_tree_SPLAY_REMOVE(&sources, s); | |||
| 2344 | free(s->sa); | |||
| 2345 | free(s); | |||
| 2346 | stat_decrement("mta.source", 1); | |||
| 2347 | } | |||
| 2348 | ||||
| 2349 | static const char * | |||
| 2350 | mta_source_to_text(struct mta_source *s) | |||
| 2351 | { | |||
| 2352 | static char buf[1024]; | |||
| 2353 | ||||
| 2354 | if (s->sa == NULL((void*)0)) | |||
| 2355 | return "[]"; | |||
| 2356 | (void)snprintf(buf, sizeof buf, "%s", sa_to_text(s->sa)); | |||
| 2357 | return (buf); | |||
| 2358 | } | |||
| 2359 | ||||
| 2360 | static int | |||
| 2361 | mta_source_cmp(const struct mta_source *a, const struct mta_source *b) | |||
| 2362 | { | |||
| 2363 | if (a->sa == NULL((void*)0)) | |||
| 2364 | return ((b->sa == NULL((void*)0)) ? 0 : -1); | |||
| 2365 | if (b->sa == NULL((void*)0)) | |||
| 2366 | return (1); | |||
| 2367 | if (a->sa->sa_len < b->sa->sa_len) | |||
| 2368 | return (-1); | |||
| 2369 | if (a->sa->sa_len > b->sa->sa_len) | |||
| 2370 | return (1); | |||
| 2371 | return (memcmp(a->sa, b->sa, a->sa->sa_len)); | |||
| 2372 | } | |||
| 2373 | ||||
| 2374 | SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp)struct mta_source * mta_source_tree_SPLAY_INSERT(struct mta_source_tree *head, struct mta_source *elm) { if (((head)->sph_root == ((void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_source_tree_SPLAY(head , elm); __comp = (mta_source_cmp)(elm, (head)->sph_root); if (__comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_source * mta_source_tree_SPLAY_REMOVE (struct mta_source_tree *head, struct mta_source *elm) { struct mta_source *__tmp; if (((head)->sph_root == ((void*)0))) return (((void*)0)); mta_source_tree_SPLAY(head, elm); if ((mta_source_cmp )(elm, (head)->sph_root) == 0) { if (((head)->sph_root) ->entry.spe_left == ((void*)0)) { (head)->sph_root = (( head)->sph_root)->entry.spe_right; } else { __tmp = ((head )->sph_root)->entry.spe_right; (head)->sph_root = (( head)->sph_root)->entry.spe_left; mta_source_tree_SPLAY (head, elm); ((head)->sph_root)->entry.spe_right = __tmp ; } return (elm); } return (((void*)0)); } void mta_source_tree_SPLAY (struct mta_source_tree *head, struct mta_source *elm) { struct mta_source __node, *__left, *__right, *__tmp; int __comp; (& __node)->entry.spe_left = (&__node)->entry.spe_right = ((void*)0); __left = __right = &__node; while ((__comp = (mta_source_cmp)(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ((head)->sph_root)->entry.spe_left; if (__tmp == ((void*)0)) break; if ((mta_source_cmp)(elm, __tmp) < 0 ){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_source_cmp)(elm, __tmp) > 0){ do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); } void mta_source_tree_SPLAY_MINMAX(struct mta_source_tree *head, int __comp) { struct mta_source __node, *__left, *__right , *__tmp; (&__node)->entry.spe_left = (&__node)-> entry.spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root )->entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp )->entry.spe_right; (__tmp)->entry.spe_right = (head)-> sph_root; (head)->sph_root = __tmp; } while (0); if (((head )->sph_root)->entry.spe_left == ((void*)0)) break; } do { (__right)->entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head)->sph_root = ((head)->sph_root )->entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break; if (__comp > 0) { do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2375 | ||||
| 2376 | static struct mta_connector * | |||
| 2377 | mta_connector(struct mta_relay *relay, struct mta_source *source) | |||
| 2378 | { | |||
| 2379 | struct mta_connector *c; | |||
| 2380 | ||||
| 2381 | c = tree_get(&relay->connectors, (uintptr_t)(source)); | |||
| 2382 | if (c == NULL((void*)0)) { | |||
| 2383 | c = xcalloc(1, sizeof(*c)); | |||
| 2384 | c->relay = relay; | |||
| 2385 | c->source = source; | |||
| 2386 | c->flags |= CONNECTOR_NEW0x10000; | |||
| 2387 | mta_source_ref(source); | |||
| 2388 | tree_xset(&relay->connectors, (uintptr_t)(source), c); | |||
| 2389 | stat_increment("mta.connector", 1); | |||
| 2390 | log_debug("debug: mta: new %s", mta_connector_to_text(c)); | |||
| 2391 | } | |||
| 2392 | ||||
| 2393 | return (c); | |||
| 2394 | } | |||
| 2395 | ||||
| 2396 | static void | |||
| 2397 | mta_connector_free(struct mta_connector *c) | |||
| 2398 | { | |||
| 2399 | log_debug("debug: mta: freeing %s", | |||
| 2400 | mta_connector_to_text(c)); | |||
| 2401 | ||||
| 2402 | if (c->flags & CONNECTOR_WAIT0x20000) { | |||
| 2403 | log_debug("debug: mta: cancelling timeout for %s", | |||
| 2404 | mta_connector_to_text(c)); | |||
| 2405 | runq_cancel(runq_connector, c); | |||
| 2406 | } | |||
| 2407 | mta_source_unref(c->source); /* from constructor */ | |||
| 2408 | free(c); | |||
| 2409 | ||||
| 2410 | stat_decrement("mta.connector", 1); | |||
| 2411 | } | |||
| 2412 | ||||
| 2413 | static const char * | |||
| 2414 | mta_connector_to_text(struct mta_connector *c) | |||
| 2415 | { | |||
| 2416 | static char buf[1024]; | |||
| 2417 | ||||
| 2418 | (void)snprintf(buf, sizeof buf, "[connector:%s->%s,0x%x]", | |||
| 2419 | mta_source_to_text(c->source), | |||
| 2420 | mta_relay_to_text(c->relay), | |||
| 2421 | c->flags); | |||
| 2422 | return (buf); | |||
| 2423 | } | |||
| 2424 | ||||
| 2425 | static struct mta_route * | |||
| 2426 | mta_route(struct mta_source *src, struct mta_host *dst) | |||
| 2427 | { | |||
| 2428 | struct mta_route key, *r; | |||
| 2429 | static uint64_t rid = 0; | |||
| 2430 | ||||
| 2431 | key.src = src; | |||
| 2432 | key.dst = dst; | |||
| 2433 | r = SPLAY_FIND(mta_route_tree, &routes, &key)mta_route_tree_SPLAY_FIND(&routes, &key); | |||
| 2434 | ||||
| 2435 | if (r == NULL((void*)0)) { | |||
| 2436 | r = xcalloc(1, sizeof(*r)); | |||
| 2437 | r->src = src; | |||
| 2438 | r->dst = dst; | |||
| 2439 | r->flags |= ROUTE_NEW0x01; | |||
| 2440 | r->id = ++rid; | |||
| 2441 | SPLAY_INSERT(mta_route_tree, &routes, r)mta_route_tree_SPLAY_INSERT(&routes, r); | |||
| 2442 | mta_source_ref(src); | |||
| 2443 | mta_host_ref(dst); | |||
| 2444 | stat_increment("mta.route", 1); | |||
| 2445 | } | |||
| 2446 | else if (r->flags & ROUTE_RUNQ0x02) { | |||
| 2447 | log_debug("debug: mta: mta_route_ref(): cancelling runq for route %s", | |||
| 2448 | mta_route_to_text(r)); | |||
| 2449 | r->flags &= ~(ROUTE_RUNQ0x02 | ROUTE_KEEPALIVE0x04); | |||
| 2450 | runq_cancel(runq_route, r); | |||
| 2451 | r->refcount--; /* from mta_route_unref() */ | |||
| 2452 | } | |||
| 2453 | ||||
| 2454 | r->refcount++; | |||
| 2455 | return (r); | |||
| 2456 | } | |||
| 2457 | ||||
| 2458 | static void | |||
| 2459 | mta_route_ref(struct mta_route *r) | |||
| 2460 | { | |||
| 2461 | r->refcount++; | |||
| 2462 | } | |||
| 2463 | ||||
| 2464 | static void | |||
| 2465 | mta_route_unref(struct mta_route *r) | |||
| 2466 | { | |||
| 2467 | time_t sched, now; | |||
| 2468 | int delay; | |||
| 2469 | ||||
| 2470 | if (--r->refcount) | |||
| 2471 | return; | |||
| 2472 | ||||
| 2473 | /* | |||
| 2474 | * Nothing references this route, but we might want to keep it alive | |||
| 2475 | * for a while. | |||
| 2476 | */ | |||
| 2477 | now = time(NULL((void*)0)); | |||
| 2478 | sched = 0; | |||
| 2479 | ||||
| 2480 | if (r->penalty) { | |||
| 2481 | #if DELAY_QUADRATIC1 | |||
| 2482 | delay = DELAY_ROUTE_BASE15 * r->penalty * r->penalty; | |||
| 2483 | #else | |||
| 2484 | delay = 15 * 60; | |||
| 2485 | #endif | |||
| 2486 | if (delay > DELAY_ROUTE_MAX3600) | |||
| 2487 | delay = DELAY_ROUTE_MAX3600; | |||
| 2488 | sched = r->lastpenalty + delay; | |||
| 2489 | log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (penalty %d)", | |||
| 2490 | mta_route_to_text(r), (unsigned long long) sched - now, r->penalty); | |||
| 2491 | } else if (!(r->flags & ROUTE_KEEPALIVE0x04)) { | |||
| 2492 | if (r->lastconn + max_seen_conndelay_route > now) | |||
| 2493 | sched = r->lastconn + max_seen_conndelay_route; | |||
| 2494 | if (r->lastdisc + max_seen_discdelay_route > now && | |||
| 2495 | r->lastdisc + max_seen_discdelay_route < sched) | |||
| 2496 | sched = r->lastdisc + max_seen_discdelay_route; | |||
| 2497 | ||||
| 2498 | if (sched > now) | |||
| 2499 | log_debug("debug: mta: mta_route_unref(): keeping route %s alive for %llus (imposed delay)", | |||
| 2500 | mta_route_to_text(r), (unsigned long long) sched - now); | |||
| 2501 | } | |||
| 2502 | ||||
| 2503 | if (sched > now) { | |||
| 2504 | r->flags |= ROUTE_RUNQ0x02; | |||
| 2505 | runq_schedule_at(runq_route, sched, r); | |||
| 2506 | r->refcount++; | |||
| 2507 | return; | |||
| 2508 | } | |||
| 2509 | ||||
| 2510 | log_debug("debug: mta: mta_route_unref(): really discarding route %s", | |||
| 2511 | mta_route_to_text(r)); | |||
| 2512 | ||||
| 2513 | SPLAY_REMOVE(mta_route_tree, &routes, r)mta_route_tree_SPLAY_REMOVE(&routes, r); | |||
| 2514 | mta_source_unref(r->src); /* from constructor */ | |||
| 2515 | mta_host_unref(r->dst); /* from constructor */ | |||
| 2516 | free(r); | |||
| 2517 | stat_decrement("mta.route", 1); | |||
| 2518 | } | |||
| 2519 | ||||
| 2520 | static const char * | |||
| 2521 | mta_route_to_text(struct mta_route *r) | |||
| 2522 | { | |||
| 2523 | static char buf[1024]; | |||
| 2524 | ||||
| 2525 | (void)snprintf(buf, sizeof buf, "%s <-> %s", | |||
| 2526 | mta_source_to_text(r->src), | |||
| 2527 | mta_host_to_text(r->dst)); | |||
| 2528 | ||||
| 2529 | return (buf); | |||
| 2530 | } | |||
| 2531 | ||||
| 2532 | static int | |||
| 2533 | mta_route_cmp(const struct mta_route *a, const struct mta_route *b) | |||
| 2534 | { | |||
| 2535 | if (a->src < b->src) | |||
| 2536 | return (-1); | |||
| 2537 | if (a->src > b->src) | |||
| 2538 | return (1); | |||
| 2539 | ||||
| 2540 | if (a->dst < b->dst) | |||
| 2541 | return (-1); | |||
| 2542 | if (a->dst > b->dst) | |||
| 2543 | return (1); | |||
| 2544 | ||||
| 2545 | return (0); | |||
| 2546 | } | |||
| 2547 | ||||
| 2548 | SPLAY_GENERATE(mta_route_tree, mta_route, entry, mta_route_cmp)struct mta_route * mta_route_tree_SPLAY_INSERT(struct mta_route_tree *head, struct mta_route *elm) { if (((head)->sph_root == ( (void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_route_tree_SPLAY(head , elm); __comp = (mta_route_cmp)(elm, (head)->sph_root); if (__comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_route * mta_route_tree_SPLAY_REMOVE (struct mta_route_tree *head, struct mta_route *elm) { struct mta_route *__tmp; if (((head)->sph_root == ((void*)0))) return (((void*)0)); mta_route_tree_SPLAY(head, elm); if ((mta_route_cmp )(elm, (head)->sph_root) == 0) { if (((head)->sph_root) ->entry.spe_left == ((void*)0)) { (head)->sph_root = (( head)->sph_root)->entry.spe_right; } else { __tmp = ((head )->sph_root)->entry.spe_right; (head)->sph_root = (( head)->sph_root)->entry.spe_left; mta_route_tree_SPLAY( head, elm); ((head)->sph_root)->entry.spe_right = __tmp ; } return (elm); } return (((void*)0)); } void mta_route_tree_SPLAY (struct mta_route_tree *head, struct mta_route *elm) { struct mta_route __node, *__left, *__right, *__tmp; int __comp; (& __node)->entry.spe_left = (&__node)->entry.spe_right = ((void*)0); __left = __right = &__node; while ((__comp = (mta_route_cmp)(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ((head)->sph_root)->entry.spe_left; if (__tmp == ((void*)0)) break; if ((mta_route_cmp)(elm, __tmp) < 0 ){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_route_cmp)(elm, __tmp) > 0){ do { ((head)->sph_root )->entry.spe_right = (__tmp)->entry.spe_left; (__tmp)-> entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = (head )->sph_root; __left = (head)->sph_root; (head)->sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root)-> entry.spe_left; (__right)->entry.spe_left = ((head)->sph_root )->entry.spe_right; ((head)->sph_root)->entry.spe_left = (&__node)->entry.spe_right; ((head)->sph_root)-> entry.spe_right = (&__node)->entry.spe_left; } while ( 0); } void mta_route_tree_SPLAY_MINMAX(struct mta_route_tree * head, int __comp) { struct mta_route __node, *__left, *__right , *__tmp; (&__node)->entry.spe_left = (&__node)-> entry.spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root )->entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp )->entry.spe_right; (__tmp)->entry.spe_right = (head)-> sph_root; (head)->sph_root = __tmp; } while (0); if (((head )->sph_root)->entry.spe_left == ((void*)0)) break; } do { (__right)->entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head)->sph_root = ((head)->sph_root )->entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break; if (__comp > 0) { do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2549 | ||||
| 2550 | void | |||
| 2551 | mta_block(struct mta_source *src, char *dom) | |||
| 2552 | { | |||
| 2553 | struct mta_block key, *b; | |||
| 2554 | ||||
| 2555 | key.source = src; | |||
| 2556 | key.domain = dom; | |||
| 2557 | ||||
| 2558 | b = SPLAY_FIND(mta_block_tree, &blocks, &key)mta_block_tree_SPLAY_FIND(&blocks, &key); | |||
| 2559 | if (b != NULL((void*)0)) | |||
| 2560 | return; | |||
| 2561 | ||||
| 2562 | b = xcalloc(1, sizeof(*b)); | |||
| 2563 | if (dom) | |||
| 2564 | b->domain = xstrdup(dom); | |||
| 2565 | b->source = src; | |||
| 2566 | mta_source_ref(src); | |||
| 2567 | SPLAY_INSERT(mta_block_tree, &blocks, b)mta_block_tree_SPLAY_INSERT(&blocks, b); | |||
| 2568 | } | |||
| 2569 | ||||
| 2570 | void | |||
| 2571 | mta_unblock(struct mta_source *src, char *dom) | |||
| 2572 | { | |||
| 2573 | struct mta_block key, *b; | |||
| 2574 | ||||
| 2575 | key.source = src; | |||
| 2576 | key.domain = dom; | |||
| 2577 | ||||
| 2578 | b = SPLAY_FIND(mta_block_tree, &blocks, &key)mta_block_tree_SPLAY_FIND(&blocks, &key); | |||
| 2579 | if (b == NULL((void*)0)) | |||
| 2580 | return; | |||
| 2581 | ||||
| 2582 | SPLAY_REMOVE(mta_block_tree, &blocks, b)mta_block_tree_SPLAY_REMOVE(&blocks, b); | |||
| 2583 | ||||
| 2584 | mta_source_unref(b->source); | |||
| 2585 | free(b->domain); | |||
| 2586 | free(b); | |||
| 2587 | } | |||
| 2588 | ||||
| 2589 | int | |||
| 2590 | mta_is_blocked(struct mta_source *src, char *dom) | |||
| 2591 | { | |||
| 2592 | struct mta_block key; | |||
| 2593 | ||||
| 2594 | key.source = src; | |||
| 2595 | key.domain = dom; | |||
| 2596 | ||||
| 2597 | if (SPLAY_FIND(mta_block_tree, &blocks, &key)mta_block_tree_SPLAY_FIND(&blocks, &key)) | |||
| 2598 | return (1); | |||
| 2599 | ||||
| 2600 | return (0); | |||
| 2601 | } | |||
| 2602 | ||||
| 2603 | static | |||
| 2604 | int | |||
| 2605 | mta_block_cmp(const struct mta_block *a, const struct mta_block *b) | |||
| 2606 | { | |||
| 2607 | if (a->source < b->source) | |||
| 2608 | return (-1); | |||
| 2609 | if (a->source > b->source) | |||
| 2610 | return (1); | |||
| 2611 | if (!a->domain && b->domain) | |||
| 2612 | return (-1); | |||
| 2613 | if (a->domain && !b->domain) | |||
| 2614 | return (1); | |||
| 2615 | if (a->domain == b->domain) | |||
| 2616 | return (0); | |||
| 2617 | return (strcasecmp(a->domain, b->domain)); | |||
| 2618 | } | |||
| 2619 | ||||
| 2620 | SPLAY_GENERATE(mta_block_tree, mta_block, entry, mta_block_cmp)struct mta_block * mta_block_tree_SPLAY_INSERT(struct mta_block_tree *head, struct mta_block *elm) { if (((head)->sph_root == ( (void*)0))) { (elm)->entry.spe_left = (elm)->entry.spe_right = ((void*)0); } else { int __comp; mta_block_tree_SPLAY(head , elm); __comp = (mta_block_cmp)(elm, (head)->sph_root); if (__comp < 0) { (elm)->entry.spe_left = ((head)->sph_root )->entry.spe_left; (elm)->entry.spe_right = (head)-> sph_root; ((head)->sph_root)->entry.spe_left = ((void*) 0); } else if (__comp > 0) { (elm)->entry.spe_right = ( (head)->sph_root)->entry.spe_right; (elm)->entry.spe_left = (head)->sph_root; ((head)->sph_root)->entry.spe_right = ((void*)0); } else return ((head)->sph_root); } (head)-> sph_root = (elm); return (((void*)0)); } struct mta_block * mta_block_tree_SPLAY_REMOVE (struct mta_block_tree *head, struct mta_block *elm) { struct mta_block *__tmp; if (((head)->sph_root == ((void*)0))) return (((void*)0)); mta_block_tree_SPLAY(head, elm); if ((mta_block_cmp )(elm, (head)->sph_root) == 0) { if (((head)->sph_root) ->entry.spe_left == ((void*)0)) { (head)->sph_root = (( head)->sph_root)->entry.spe_right; } else { __tmp = ((head )->sph_root)->entry.spe_right; (head)->sph_root = (( head)->sph_root)->entry.spe_left; mta_block_tree_SPLAY( head, elm); ((head)->sph_root)->entry.spe_right = __tmp ; } return (elm); } return (((void*)0)); } void mta_block_tree_SPLAY (struct mta_block_tree *head, struct mta_block *elm) { struct mta_block __node, *__left, *__right, *__tmp; int __comp; (& __node)->entry.spe_left = (&__node)->entry.spe_right = ((void*)0); __left = __right = &__node; while ((__comp = (mta_block_cmp)(elm, (head)->sph_root))) { if (__comp < 0) { __tmp = ((head)->sph_root)->entry.spe_left; if (__tmp == ((void*)0)) break; if ((mta_block_cmp)(elm, __tmp) < 0 ){ do { ((head)->sph_root)->entry.spe_left = (__tmp)-> entry.spe_right; (__tmp)->entry.spe_right = (head)->sph_root ; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root )->entry.spe_left == ((void*)0)) break; } do { (__right)-> entry.spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break ; if ((mta_block_cmp)(elm, __tmp) > 0){ do { ((head)->sph_root )->entry.spe_right = (__tmp)->entry.spe_left; (__tmp)-> entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = (head )->sph_root; __left = (head)->sph_root; (head)->sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root)-> entry.spe_left; (__right)->entry.spe_left = ((head)->sph_root )->entry.spe_right; ((head)->sph_root)->entry.spe_left = (&__node)->entry.spe_right; ((head)->sph_root)-> entry.spe_right = (&__node)->entry.spe_left; } while ( 0); } void mta_block_tree_SPLAY_MINMAX(struct mta_block_tree * head, int __comp) { struct mta_block __node, *__left, *__right , *__tmp; (&__node)->entry.spe_left = (&__node)-> entry.spe_right = ((void*)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head)->sph_root )->entry.spe_left; if (__tmp == ((void*)0)) break; if (__comp < 0){ do { ((head)->sph_root)->entry.spe_left = (__tmp )->entry.spe_right; (__tmp)->entry.spe_right = (head)-> sph_root; (head)->sph_root = __tmp; } while (0); if (((head )->sph_root)->entry.spe_left == ((void*)0)) break; } do { (__right)->entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head)->sph_root = ((head)->sph_root )->entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root)->entry.spe_right; if (__tmp == ((void*)0)) break; if (__comp > 0) { do { ((head)-> sph_root)->entry.spe_right = (__tmp)->entry.spe_left; ( __tmp)->entry.spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->entry.spe_right == ((void*)0)) break; } do { (__left)->entry.spe_right = ( head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->entry.spe_right; } while (0); } } do { (__left)->entry.spe_right = ((head)->sph_root )->entry.spe_left; (__right)->entry.spe_left = ((head)-> sph_root)->entry.spe_right; ((head)->sph_root)->entry .spe_left = (&__node)->entry.spe_right; ((head)->sph_root )->entry.spe_right = (&__node)->entry.spe_left; } while (0); }; | |||
| 2621 | ||||
| 2622 | ||||
| 2623 | ||||
| 2624 | /* hoststat errors are not critical, we do best effort */ | |||
| 2625 | void | |||
| 2626 | mta_hoststat_update(const char *host, const char *error) | |||
| 2627 | { | |||
| 2628 | struct hoststat *hs = NULL((void*)0); | |||
| 2629 | char buf[HOST_NAME_MAX255+1]; | |||
| 2630 | ||||
| 2631 | if (!lowercase(buf, host, sizeof buf)) | |||
| 2632 | return; | |||
| 2633 | ||||
| 2634 | hs = dict_get(&hoststat, buf); | |||
| 2635 | if (hs == NULL((void*)0)) { | |||
| 2636 | if ((hs = calloc(1, sizeof *hs)) == NULL((void*)0)) | |||
| 2637 | return; | |||
| 2638 | tree_init(&hs->deferred)do { do { (&((&hs->deferred)->tree))->sph_root = ((void*)0); } while (0); (&hs->deferred)->count = 0; } while(0); | |||
| 2639 | runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY(4 * 3600), hs); | |||
| 2640 | } | |||
| 2641 | (void)strlcpy(hs->name, buf, sizeof hs->name); | |||
| 2642 | (void)strlcpy(hs->error, error, sizeof hs->error); | |||
| 2643 | hs->tm = time(NULL((void*)0)); | |||
| 2644 | dict_set(&hoststat, buf, hs); | |||
| 2645 | ||||
| 2646 | runq_cancel(runq_hoststat, hs); | |||
| 2647 | runq_schedule(runq_hoststat, HOSTSTAT_EXPIRE_DELAY(4 * 3600), hs); | |||
| 2648 | } | |||
| 2649 | ||||
| 2650 | void | |||
| 2651 | mta_hoststat_cache(const char *host, uint64_t evpid) | |||
| 2652 | { | |||
| 2653 | struct hoststat *hs = NULL((void*)0); | |||
| 2654 | char buf[HOST_NAME_MAX255+1]; | |||
| 2655 | ||||
| 2656 | if (!lowercase(buf, host, sizeof buf)) | |||
| 2657 | return; | |||
| 2658 | ||||
| 2659 | hs = dict_get(&hoststat, buf); | |||
| 2660 | if (hs == NULL((void*)0)) | |||
| 2661 | return; | |||
| 2662 | ||||
| 2663 | if (tree_count(&hs->deferred)((&hs->deferred)->count) >= env->sc_mta_max_deferred) | |||
| 2664 | return; | |||
| 2665 | ||||
| 2666 | tree_set(&hs->deferred, evpid, NULL((void*)0)); | |||
| 2667 | } | |||
| 2668 | ||||
| 2669 | void | |||
| 2670 | mta_hoststat_uncache(const char *host, uint64_t evpid) | |||
| 2671 | { | |||
| 2672 | struct hoststat *hs = NULL((void*)0); | |||
| 2673 | char buf[HOST_NAME_MAX255+1]; | |||
| 2674 | ||||
| 2675 | if (!lowercase(buf, host, sizeof buf)) | |||
| 2676 | return; | |||
| 2677 | ||||
| 2678 | hs = dict_get(&hoststat, buf); | |||
| 2679 | if (hs == NULL((void*)0)) | |||
| 2680 | return; | |||
| 2681 | ||||
| 2682 | tree_pop(&hs->deferred, evpid); | |||
| 2683 | } | |||
| 2684 | ||||
| 2685 | void | |||
| 2686 | mta_hoststat_reschedule(const char *host) | |||
| 2687 | { | |||
| 2688 | struct hoststat *hs = NULL((void*)0); | |||
| 2689 | char buf[HOST_NAME_MAX255+1]; | |||
| 2690 | uint64_t evpid; | |||
| 2691 | ||||
| 2692 | if (!lowercase(buf, host, sizeof buf)) | |||
| 2693 | return; | |||
| 2694 | ||||
| 2695 | hs = dict_get(&hoststat, buf); | |||
| 2696 | if (hs == NULL((void*)0)) | |||
| 2697 | return; | |||
| 2698 | ||||
| 2699 | while (tree_poproot(&hs->deferred, &evpid, NULL((void*)0))) { | |||
| 2700 | m_compose(p_queue, IMSG_MTA_SCHEDULE, 0, 0, -1, | |||
| 2701 | &evpid, sizeof evpid); | |||
| 2702 | } | |||
| 2703 | } | |||
| 2704 | ||||
| 2705 | static void | |||
| 2706 | mta_hoststat_remove_entry(struct hoststat *hs) | |||
| 2707 | { | |||
| 2708 | while (tree_poproot(&hs->deferred, NULL((void*)0), NULL((void*)0))) | |||
| 2709 | ; | |||
| 2710 | dict_pop(&hoststat, hs->name); | |||
| 2711 | runq_cancel(runq_hoststat, hs); | |||
| 2712 | } |