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 | } |