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