| File: | src/usr.sbin/smtpd/smtpd/../bounce.c |
| Warning: | line 607, column 28 Use of memory after it is freed |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: bounce.c,v 1.90 2023/05/31 16:51:46 op Exp $ */ | ||||
| 2 | |||||
| 3 | /* | ||||
| 4 | * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> | ||||
| 5 | * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> | ||||
| 6 | * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> | ||||
| 7 | * | ||||
| 8 | * Permission to use, copy, modify, and distribute this software for any | ||||
| 9 | * purpose with or without fee is hereby granted, provided that the above | ||||
| 10 | * copyright notice and this permission notice appear in all copies. | ||||
| 11 | * | ||||
| 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||||
| 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||||
| 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||||
| 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||
| 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||||
| 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||||
| 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||||
| 19 | */ | ||||
| 20 | |||||
| 21 | #include <errno(*__errno()).h> | ||||
| 22 | #include <inttypes.h> | ||||
| 23 | #include <stdlib.h> | ||||
| 24 | #include <string.h> | ||||
| 25 | #include <time.h> | ||||
| 26 | #include <unistd.h> | ||||
| 27 | |||||
| 28 | #include "smtpd.h" | ||||
| 29 | #include "log.h" | ||||
| 30 | |||||
| 31 | #define BOUNCE_MAXRUN2 2 | ||||
| 32 | #define BOUNCE_HIWAT65535 65535 | ||||
| 33 | |||||
| 34 | enum { | ||||
| 35 | BOUNCE_EHLO, | ||||
| 36 | BOUNCE_MAIL, | ||||
| 37 | BOUNCE_RCPT, | ||||
| 38 | BOUNCE_DATA, | ||||
| 39 | BOUNCE_DATA_NOTICE, | ||||
| 40 | BOUNCE_DATA_MESSAGE, | ||||
| 41 | BOUNCE_DATA_END, | ||||
| 42 | BOUNCE_QUIT, | ||||
| 43 | BOUNCE_CLOSE, | ||||
| 44 | }; | ||||
| 45 | |||||
| 46 | struct bounce_envelope { | ||||
| 47 | TAILQ_ENTRY(bounce_envelope)struct { struct bounce_envelope *tqe_next; struct bounce_envelope **tqe_prev; } entry; | ||||
| 48 | uint64_t id; | ||||
| 49 | struct mailaddr dest; | ||||
| 50 | char *report; | ||||
| 51 | uint8_t esc_class; | ||||
| 52 | uint8_t esc_code; | ||||
| 53 | }; | ||||
| 54 | |||||
| 55 | struct bounce_message { | ||||
| 56 | SPLAY_ENTRY(bounce_message)struct { struct bounce_message *spe_left; struct bounce_message *spe_right; } sp_entry; | ||||
| 57 | TAILQ_ENTRY(bounce_message)struct { struct bounce_message *tqe_next; struct bounce_message **tqe_prev; } entry; | ||||
| 58 | uint32_t msgid; | ||||
| 59 | struct delivery_bounce bounce; | ||||
| 60 | char *smtpname; | ||||
| 61 | char *to; | ||||
| 62 | time_t timeout; | ||||
| 63 | TAILQ_HEAD(, bounce_envelope)struct { struct bounce_envelope *tqh_first; struct bounce_envelope **tqh_last; } envelopes; | ||||
| 64 | }; | ||||
| 65 | |||||
| 66 | struct bounce_session { | ||||
| 67 | char *smtpname; | ||||
| 68 | struct bounce_message *msg; | ||||
| 69 | FILE *msgfp; | ||||
| 70 | int state; | ||||
| 71 | struct io *io; | ||||
| 72 | uint64_t boundary; | ||||
| 73 | }; | ||||
| 74 | |||||
| 75 | SPLAY_HEAD(bounce_message_tree, bounce_message)struct bounce_message_tree { struct bounce_message *sph_root; }; | ||||
| 76 | static int bounce_message_cmp(const struct bounce_message *, | ||||
| 77 | const struct bounce_message *); | ||||
| 78 | SPLAY_PROTOTYPE(bounce_message_tree, bounce_message, sp_entry,void bounce_message_tree_SPLAY(struct bounce_message_tree *, struct bounce_message *); void bounce_message_tree_SPLAY_MINMAX(struct bounce_message_tree *, int); struct bounce_message *bounce_message_tree_SPLAY_INSERT (struct bounce_message_tree *, struct bounce_message *); struct bounce_message *bounce_message_tree_SPLAY_REMOVE(struct bounce_message_tree *, struct bounce_message *); static __attribute__((__unused__ )) __inline struct bounce_message * bounce_message_tree_SPLAY_FIND (struct bounce_message_tree *head, struct bounce_message *elm ) { if (((head)->sph_root == ((void *)0))) return(((void * )0)); bounce_message_tree_SPLAY(head, elm); if ((bounce_message_cmp )(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void *)0)); } static __attribute__((__unused__)) __inline struct bounce_message * bounce_message_tree_SPLAY_NEXT(struct bounce_message_tree *head, struct bounce_message *elm) { bounce_message_tree_SPLAY (head, elm); if ((elm)->sp_entry.spe_right != ((void *)0)) { elm = (elm)->sp_entry.spe_right; while ((elm)->sp_entry .spe_left != ((void *)0)) { elm = (elm)->sp_entry.spe_left ; } } else elm = ((void *)0); return (elm); } static __attribute__ ((__unused__)) __inline struct bounce_message * bounce_message_tree_SPLAY_MIN_MAX (struct bounce_message_tree *head, int val) { bounce_message_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); } | ||||
| 79 | bounce_message_cmp)void bounce_message_tree_SPLAY(struct bounce_message_tree *, struct bounce_message *); void bounce_message_tree_SPLAY_MINMAX(struct bounce_message_tree *, int); struct bounce_message *bounce_message_tree_SPLAY_INSERT (struct bounce_message_tree *, struct bounce_message *); struct bounce_message *bounce_message_tree_SPLAY_REMOVE(struct bounce_message_tree *, struct bounce_message *); static __attribute__((__unused__ )) __inline struct bounce_message * bounce_message_tree_SPLAY_FIND (struct bounce_message_tree *head, struct bounce_message *elm ) { if (((head)->sph_root == ((void *)0))) return(((void * )0)); bounce_message_tree_SPLAY(head, elm); if ((bounce_message_cmp )(elm, (head)->sph_root) == 0) return (head->sph_root); return (((void *)0)); } static __attribute__((__unused__)) __inline struct bounce_message * bounce_message_tree_SPLAY_NEXT(struct bounce_message_tree *head, struct bounce_message *elm) { bounce_message_tree_SPLAY (head, elm); if ((elm)->sp_entry.spe_right != ((void *)0)) { elm = (elm)->sp_entry.spe_right; while ((elm)->sp_entry .spe_left != ((void *)0)) { elm = (elm)->sp_entry.spe_left ; } } else elm = ((void *)0); return (elm); } static __attribute__ ((__unused__)) __inline struct bounce_message * bounce_message_tree_SPLAY_MIN_MAX (struct bounce_message_tree *head, int val) { bounce_message_tree_SPLAY_MINMAX (head, val); return ((head)->sph_root); }; | ||||
| 80 | |||||
| 81 | static void bounce_drain(void); | ||||
| 82 | static void bounce_send(struct bounce_session *, const char *, ...) | ||||
| 83 | __attribute__((__format__ (printf, 2, 3))); | ||||
| 84 | static int bounce_next_message(struct bounce_session *); | ||||
| 85 | static int bounce_next(struct bounce_session *); | ||||
| 86 | static void bounce_delivery(struct bounce_message *, int, const char *); | ||||
| 87 | static void bounce_status(struct bounce_session *, const char *, ...) | ||||
| 88 | __attribute__((__format__ (printf, 2, 3))); | ||||
| 89 | static void bounce_io(struct io *, int, void *); | ||||
| 90 | static void bounce_timeout(int, short, void *); | ||||
| 91 | static void bounce_free(struct bounce_session *); | ||||
| 92 | static const char *action_str(const struct delivery_bounce *); | ||||
| 93 | |||||
| 94 | static struct tree wait_fd; | ||||
| 95 | static struct bounce_message_tree messages; | ||||
| 96 | static TAILQ_HEAD(, bounce_message)struct { struct bounce_message *tqh_first; struct bounce_message **tqh_last; } pending; | ||||
| 97 | |||||
| 98 | static int nmessage = 0; | ||||
| 99 | static int running = 0; | ||||
| 100 | static struct event ev_timer; | ||||
| 101 | |||||
| 102 | static void | ||||
| 103 | bounce_init(void) | ||||
| 104 | { | ||||
| 105 | static int init = 0; | ||||
| 106 | |||||
| 107 | if (init == 0) { | ||||
| 108 | TAILQ_INIT(&pending)do { (&pending)->tqh_first = ((void *)0); (&pending )->tqh_last = &(&pending)->tqh_first; } while ( 0); | ||||
| 109 | SPLAY_INIT(&messages)do { (&messages)->sph_root = ((void *)0); } while (0); | ||||
| 110 | tree_init(&wait_fd)do { do { (&((&wait_fd)->tree))->sph_root = ((void *)0); } while (0); (&wait_fd)->count = 0; } while(0); | ||||
| 111 | evtimer_set(&ev_timer, bounce_timeout, NULL)event_set(&ev_timer, -1, 0, bounce_timeout, ((void *)0)); | ||||
| 112 | init = 1; | ||||
| 113 | } | ||||
| 114 | } | ||||
| 115 | |||||
| 116 | void | ||||
| 117 | bounce_add(uint64_t evpid) | ||||
| 118 | { | ||||
| 119 | char buf[LINE_MAX2048], *line; | ||||
| 120 | struct envelope evp; | ||||
| 121 | struct bounce_message key, *msg; | ||||
| 122 | struct bounce_envelope *be; | ||||
| 123 | |||||
| 124 | bounce_init(); | ||||
| 125 | |||||
| 126 | if (queue_envelope_load(evpid, &evp) == 0) { | ||||
| 127 | m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); | ||||
| 128 | m_add_evpid(p_scheduler, evpid); | ||||
| 129 | m_close(p_scheduler); | ||||
| 130 | return; | ||||
| 131 | } | ||||
| 132 | |||||
| 133 | if (evp.type != D_BOUNCE) | ||||
| 134 | fatalx("bounce: evp:%016" PRIx64"llx" " is not of type D_BOUNCE!", | ||||
| 135 | evp.id); | ||||
| 136 | |||||
| 137 | key.msgid = evpid_to_msgid(evpid); | ||||
| 138 | key.bounce = evp.agent.bounce; | ||||
| 139 | key.smtpname = evp.smtpname; | ||||
| 140 | |||||
| 141 | switch (evp.esc_class) { | ||||
| 142 | case ESC_STATUS_OK: | ||||
| 143 | key.bounce.type = B_DELIVERED; | ||||
| 144 | break; | ||||
| 145 | case ESC_STATUS_TEMPFAIL: | ||||
| 146 | key.bounce.type = B_DELAYED; | ||||
| 147 | break; | ||||
| 148 | default: | ||||
| 149 | key.bounce.type = B_FAILED; | ||||
| 150 | } | ||||
| 151 | |||||
| 152 | key.bounce.dsn_ret = evp.dsn_ret; | ||||
| 153 | key.bounce.ttl = evp.ttl; | ||||
| 154 | msg = SPLAY_FIND(bounce_message_tree, &messages, &key)bounce_message_tree_SPLAY_FIND(&messages, &key); | ||||
| 155 | if (msg == NULL((void *)0)) { | ||||
| 156 | msg = xcalloc(1, sizeof(*msg)); | ||||
| 157 | msg->msgid = key.msgid; | ||||
| 158 | msg->bounce = key.bounce; | ||||
| 159 | |||||
| 160 | TAILQ_INIT(&msg->envelopes)do { (&msg->envelopes)->tqh_first = ((void *)0); (& msg->envelopes)->tqh_last = &(&msg->envelopes )->tqh_first; } while (0); | ||||
| 161 | |||||
| 162 | msg->smtpname = xstrdup(evp.smtpname); | ||||
| 163 | (void)snprintf(buf, sizeof(buf), "%s@%s", evp.sender.user, | ||||
| 164 | evp.sender.domain); | ||||
| 165 | msg->to = xstrdup(buf); | ||||
| 166 | nmessage += 1; | ||||
| 167 | SPLAY_INSERT(bounce_message_tree, &messages, msg)bounce_message_tree_SPLAY_INSERT(&messages, msg); | ||||
| 168 | log_debug("debug: bounce: new message %08" PRIx32"x", | ||||
| 169 | msg->msgid); | ||||
| 170 | stat_increment("bounce.message", 1); | ||||
| 171 | } else | ||||
| 172 | TAILQ_REMOVE(&pending, msg, entry)do { if (((msg)->entry.tqe_next) != ((void *)0)) (msg)-> entry.tqe_next->entry.tqe_prev = (msg)->entry.tqe_prev; else (&pending)->tqh_last = (msg)->entry.tqe_prev; *(msg)->entry.tqe_prev = (msg)->entry.tqe_next; ; ; } while (0); | ||||
| 173 | |||||
| 174 | line = evp.errorline; | ||||
| 175 | if (strlen(line) > 4 && (*line == '1' || *line == '6')) | ||||
| 176 | line += 4; | ||||
| 177 | (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user, | ||||
| 178 | evp.dest.domain, line); | ||||
| 179 | |||||
| 180 | be = xmalloc(sizeof *be); | ||||
| 181 | be->id = evpid; | ||||
| 182 | be->report = xstrdup(buf); | ||||
| 183 | (void)strlcpy(be->dest.user, evp.dest.user, sizeof(be->dest.user)); | ||||
| 184 | (void)strlcpy(be->dest.domain, evp.dest.domain, | ||||
| 185 | sizeof(be->dest.domain)); | ||||
| 186 | be->esc_class = evp.esc_class; | ||||
| 187 | be->esc_code = evp.esc_code; | ||||
| 188 | TAILQ_INSERT_TAIL(&msg->envelopes, be, entry)do { (be)->entry.tqe_next = ((void *)0); (be)->entry.tqe_prev = (&msg->envelopes)->tqh_last; *(&msg->envelopes )->tqh_last = (be); (&msg->envelopes)->tqh_last = &(be)->entry.tqe_next; } while (0); | ||||
| 189 | log_debug("debug: bounce: adding report %16"PRIx64"llx"": %s", be->id, be->report); | ||||
| 190 | |||||
| 191 | msg->timeout = time(NULL((void *)0)) + 1; | ||||
| 192 | TAILQ_INSERT_TAIL(&pending, msg, entry)do { (msg)->entry.tqe_next = ((void *)0); (msg)->entry. tqe_prev = (&pending)->tqh_last; *(&pending)->tqh_last = (msg); (&pending)->tqh_last = &(msg)->entry. tqe_next; } while (0); | ||||
| 193 | |||||
| 194 | stat_increment("bounce.envelope", 1); | ||||
| 195 | bounce_drain(); | ||||
| 196 | } | ||||
| 197 | |||||
| 198 | void | ||||
| 199 | bounce_fd(int fd) | ||||
| 200 | { | ||||
| 201 | struct bounce_session *s; | ||||
| 202 | struct bounce_message *msg; | ||||
| 203 | |||||
| 204 | log_debug("debug: bounce: got enqueue socket %d", fd); | ||||
| 205 | |||||
| 206 | if (fd == -1 || TAILQ_EMPTY(&pending)(((&pending)->tqh_first) == ((void *)0))) { | ||||
| 207 | log_debug("debug: bounce: cancelling"); | ||||
| 208 | if (fd != -1) | ||||
| 209 | close(fd); | ||||
| 210 | running -= 1; | ||||
| 211 | bounce_drain(); | ||||
| 212 | return; | ||||
| 213 | } | ||||
| 214 | |||||
| 215 | msg = TAILQ_FIRST(&pending)((&pending)->tqh_first); | ||||
| 216 | |||||
| 217 | s = xcalloc(1, sizeof(*s)); | ||||
| 218 | s->smtpname = xstrdup(msg->smtpname); | ||||
| 219 | s->state = BOUNCE_EHLO; | ||||
| 220 | s->io = io_new(); | ||||
| 221 | io_set_callback(s->io, bounce_io, s); | ||||
| 222 | io_set_fd(s->io, fd); | ||||
| 223 | io_set_timeout(s->io, 30000); | ||||
| 224 | io_set_read(s->io); | ||||
| 225 | s->boundary = generate_uid(); | ||||
| 226 | |||||
| 227 | log_debug("debug: bounce: new session %p", s); | ||||
| 228 | stat_increment("bounce.session", 1); | ||||
| 229 | } | ||||
| 230 | |||||
| 231 | static void | ||||
| 232 | bounce_timeout(int fd, short ev, void *arg) | ||||
| 233 | { | ||||
| 234 | log_debug("debug: bounce: timeout"); | ||||
| 235 | |||||
| 236 | bounce_drain(); | ||||
| 237 | } | ||||
| 238 | |||||
| 239 | static void | ||||
| 240 | bounce_drain(void) | ||||
| 241 | { | ||||
| 242 | struct bounce_message *msg; | ||||
| 243 | struct timeval tv; | ||||
| 244 | time_t t; | ||||
| 245 | |||||
| 246 | log_debug("debug: bounce: drain: nmessage=%d running=%d", | ||||
| 247 | nmessage, running); | ||||
| 248 | |||||
| 249 | while (1) { | ||||
| 250 | if (running >= BOUNCE_MAXRUN2) { | ||||
| 251 | log_debug("debug: bounce: max session reached"); | ||||
| 252 | return; | ||||
| 253 | } | ||||
| 254 | |||||
| 255 | if (nmessage == 0) { | ||||
| 256 | log_debug("debug: bounce: no more messages"); | ||||
| 257 | return; | ||||
| 258 | } | ||||
| 259 | |||||
| 260 | if (running >= nmessage) { | ||||
| 261 | log_debug("debug: bounce: enough sessions running"); | ||||
| 262 | return; | ||||
| 263 | } | ||||
| 264 | |||||
| 265 | if ((msg = TAILQ_FIRST(&pending)((&pending)->tqh_first)) == NULL((void *)0)) { | ||||
| 266 | log_debug("debug: bounce: no more pending messages"); | ||||
| 267 | return; | ||||
| 268 | } | ||||
| 269 | |||||
| 270 | t = time(NULL((void *)0)); | ||||
| 271 | if (msg->timeout > t) { | ||||
| 272 | log_debug("debug: bounce: next message not ready yet"); | ||||
| 273 | if (!evtimer_pending(&ev_timer, NULL)event_pending(&ev_timer, 0x01, ((void *)0))) { | ||||
| 274 | log_debug("debug: bounce: setting timer"); | ||||
| 275 | tv.tv_sec = msg->timeout - t; | ||||
| 276 | tv.tv_usec = 0; | ||||
| 277 | evtimer_add(&ev_timer, &tv)event_add(&ev_timer, &tv); | ||||
| 278 | } | ||||
| 279 | return; | ||||
| 280 | } | ||||
| 281 | |||||
| 282 | log_debug("debug: bounce: requesting new enqueue socket..."); | ||||
| 283 | m_compose(p_dispatcher, IMSG_QUEUE_SMTP_SESSION, 0, 0, -1, NULL((void *)0), 0); | ||||
| 284 | |||||
| 285 | running += 1; | ||||
| 286 | } | ||||
| 287 | } | ||||
| 288 | |||||
| 289 | static void | ||||
| 290 | bounce_send(struct bounce_session *s, const char *fmt, ...) | ||||
| 291 | { | ||||
| 292 | va_list ap; | ||||
| 293 | char *p; | ||||
| 294 | int len; | ||||
| 295 | |||||
| 296 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | ||||
| 297 | if ((len = vasprintf(&p, fmt, ap)) == -1) | ||||
| 298 | fatal("bounce: vasprintf"); | ||||
| 299 | va_end(ap)__builtin_va_end((ap)); | ||||
| 300 | |||||
| 301 | log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p)do { if (tracing & (0x0040)) log_trace0("bounce: %p: >>> %s" , s, p); } while (0); | ||||
| 302 | |||||
| 303 | io_xprintf(s->io, "%s\r\n", p); | ||||
| 304 | |||||
| 305 | free(p); | ||||
| 306 | } | ||||
| 307 | |||||
| 308 | static const char * | ||||
| 309 | bounce_duration(long long d) | ||||
| 310 | { | ||||
| 311 | static char buf[32]; | ||||
| 312 | |||||
| 313 | if (d < 60) { | ||||
| 314 | (void)snprintf(buf, sizeof buf, "%lld second%s", d, | ||||
| 315 | (d == 1) ? "" : "s"); | ||||
| 316 | } else if (d < 3600) { | ||||
| 317 | d = d / 60; | ||||
| 318 | (void)snprintf(buf, sizeof buf, "%lld minute%s", d, | ||||
| 319 | (d == 1) ? "" : "s"); | ||||
| 320 | } | ||||
| 321 | else if (d < 3600 * 24) { | ||||
| 322 | d = d / 3600; | ||||
| 323 | (void)snprintf(buf, sizeof buf, "%lld hour%s", d, | ||||
| 324 | (d == 1) ? "" : "s"); | ||||
| 325 | } | ||||
| 326 | else { | ||||
| 327 | d = d / (3600 * 24); | ||||
| 328 | (void)snprintf(buf, sizeof buf, "%lld day%s", d, | ||||
| 329 | (d == 1) ? "" : "s"); | ||||
| 330 | } | ||||
| 331 | return (buf); | ||||
| 332 | } | ||||
| 333 | |||||
| 334 | #define NOTICE_INTRO" Hi!\r\n\r\n" " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" \ | ||||
| 335 | " Hi!\r\n\r\n" \ | ||||
| 336 | " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" | ||||
| 337 | |||||
| 338 | const char *notice_error = | ||||
| 339 | " An error has occurred while attempting to deliver a message for\r\n" | ||||
| 340 | " the following list of recipients:\r\n\r\n"; | ||||
| 341 | |||||
| 342 | const char *notice_warning = | ||||
| 343 | " A message is delayed for more than %s for the following\r\n" | ||||
| 344 | " list of recipients:\r\n\r\n"; | ||||
| 345 | |||||
| 346 | const char *notice_warning2 = | ||||
| 347 | " Please note that this is only a temporary failure report.\r\n" | ||||
| 348 | " The message is kept in the queue for up to %s.\r\n" | ||||
| 349 | " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n"; | ||||
| 350 | |||||
| 351 | const char *notice_success = | ||||
| 352 | " Your message was successfully delivered to these recipients.\r\n\r\n"; | ||||
| 353 | |||||
| 354 | const char *notice_relay = | ||||
| 355 | " Your message was relayed to these recipients.\r\n\r\n"; | ||||
| 356 | |||||
| 357 | static int | ||||
| 358 | bounce_next_message(struct bounce_session *s) | ||||
| 359 | { | ||||
| 360 | struct bounce_message *msg; | ||||
| 361 | char buf[LINE_MAX2048]; | ||||
| 362 | int fd; | ||||
| 363 | time_t now; | ||||
| 364 | |||||
| 365 | again: | ||||
| 366 | |||||
| 367 | now = time(NULL((void *)0)); | ||||
| 368 | |||||
| 369 | TAILQ_FOREACH(msg, &pending, entry)for((msg) = ((&pending)->tqh_first); (msg) != ((void * )0); (msg) = ((msg)->entry.tqe_next)) { | ||||
| 370 | if (msg->timeout > now) | ||||
| 371 | continue; | ||||
| 372 | if (strcmp(msg->smtpname, s->smtpname)) | ||||
| 373 | continue; | ||||
| 374 | break; | ||||
| 375 | } | ||||
| 376 | if (msg == NULL((void *)0)) | ||||
| 377 | return (0); | ||||
| 378 | |||||
| 379 | TAILQ_REMOVE(&pending, msg, entry)do { if (((msg)->entry.tqe_next) != ((void *)0)) (msg)-> entry.tqe_next->entry.tqe_prev = (msg)->entry.tqe_prev; else (&pending)->tqh_last = (msg)->entry.tqe_prev; *(msg)->entry.tqe_prev = (msg)->entry.tqe_next; ; ; } while (0); | ||||
| 380 | SPLAY_REMOVE(bounce_message_tree, &messages, msg)bounce_message_tree_SPLAY_REMOVE(&messages, msg); | ||||
| 381 | |||||
| 382 | if ((fd = queue_message_fd_r(msg->msgid)) == -1) { | ||||
| 383 | bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, | ||||
| 384 | "Could not open message fd"); | ||||
| 385 | goto again; | ||||
| 386 | } | ||||
| 387 | |||||
| 388 | if ((s->msgfp = fdopen(fd, "r")) == NULL((void *)0)) { | ||||
| 389 | (void)snprintf(buf, sizeof(buf), "fdopen: %s", strerror(errno(*__errno()))); | ||||
| 390 | log_warn("warn: bounce: fdopen"); | ||||
| 391 | close(fd); | ||||
| 392 | bounce_delivery(msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, buf); | ||||
| 393 | goto again; | ||||
| 394 | } | ||||
| 395 | |||||
| 396 | s->msg = msg; | ||||
| 397 | return (1); | ||||
| 398 | } | ||||
| 399 | |||||
| 400 | static int | ||||
| 401 | bounce_next(struct bounce_session *s) | ||||
| 402 | { | ||||
| 403 | struct bounce_envelope *evp; | ||||
| 404 | char *line = NULL((void *)0); | ||||
| 405 | size_t n, sz = 0; | ||||
| 406 | ssize_t len; | ||||
| 407 | |||||
| 408 | switch (s->state) { | ||||
| 409 | case BOUNCE_EHLO: | ||||
| 410 | bounce_send(s, "EHLO %s", s->smtpname); | ||||
| 411 | s->state = BOUNCE_MAIL; | ||||
| 412 | break; | ||||
| 413 | |||||
| 414 | case BOUNCE_MAIL: | ||||
| 415 | case BOUNCE_DATA_END: | ||||
| 416 | log_debug("debug: bounce: %p: getting next message...", s); | ||||
| 417 | if (bounce_next_message(s) == 0) { | ||||
| 418 | log_debug("debug: bounce: %p: no more messages", s); | ||||
| 419 | bounce_send(s, "QUIT"); | ||||
| 420 | s->state = BOUNCE_CLOSE; | ||||
| 421 | break; | ||||
| 422 | } | ||||
| 423 | log_debug("debug: bounce: %p: found message %08"PRIx32"x", | ||||
| 424 | s, s->msg->msgid); | ||||
| 425 | bounce_send(s, "MAIL FROM: <>"); | ||||
| 426 | s->state = BOUNCE_RCPT; | ||||
| 427 | break; | ||||
| 428 | |||||
| 429 | case BOUNCE_RCPT: | ||||
| 430 | bounce_send(s, "RCPT TO: <%s>", s->msg->to); | ||||
| 431 | s->state = BOUNCE_DATA; | ||||
| 432 | break; | ||||
| 433 | |||||
| 434 | case BOUNCE_DATA: | ||||
| 435 | bounce_send(s, "DATA"); | ||||
| 436 | s->state = BOUNCE_DATA_NOTICE; | ||||
| 437 | break; | ||||
| 438 | |||||
| 439 | case BOUNCE_DATA_NOTICE: | ||||
| 440 | /* Construct an appropriate notice. */ | ||||
| 441 | |||||
| 442 | io_xprintf(s->io, | ||||
| 443 | "Subject: Delivery status notification: %s\r\n" | ||||
| 444 | "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n" | ||||
| 445 | "To: %s\r\n" | ||||
| 446 | "Date: %s\r\n" | ||||
| 447 | "MIME-Version: 1.0\r\n" | ||||
| 448 | "Content-Type: multipart/mixed;" | ||||
| 449 | "boundary=\"%16" PRIu64"llu" "/%s\"\r\n" | ||||
| 450 | "\r\n" | ||||
| 451 | "This is a MIME-encapsulated message.\r\n" | ||||
| 452 | "\r\n", | ||||
| 453 | action_str(&s->msg->bounce), | ||||
| 454 | s->smtpname, | ||||
| 455 | s->msg->to, | ||||
| 456 | time_to_text(time(NULL((void *)0))), | ||||
| 457 | s->boundary, | ||||
| 458 | s->smtpname); | ||||
| 459 | |||||
| 460 | io_xprintf(s->io, | ||||
| 461 | "--%16" PRIu64"llu" "/%s\r\n" | ||||
| 462 | "Content-Description: Notification\r\n" | ||||
| 463 | "Content-Type: text/plain; charset=us-ascii\r\n" | ||||
| 464 | "\r\n" | ||||
| 465 | NOTICE_INTRO" Hi!\r\n\r\n" " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" | ||||
| 466 | "\r\n", | ||||
| 467 | s->boundary, s->smtpname); | ||||
| 468 | |||||
| 469 | switch (s->msg->bounce.type) { | ||||
| 470 | case B_FAILED: | ||||
| 471 | io_xprint(s->io, notice_error); | ||||
| 472 | break; | ||||
| 473 | case B_DELAYED: | ||||
| 474 | io_xprintf(s->io, notice_warning, | ||||
| 475 | bounce_duration(s->msg->bounce.delay)); | ||||
| 476 | break; | ||||
| 477 | case B_DELIVERED: | ||||
| 478 | io_xprint(s->io, s->msg->bounce.mta_without_dsn ? | ||||
| 479 | notice_relay : notice_success); | ||||
| 480 | break; | ||||
| 481 | default: | ||||
| 482 | log_warn("warn: bounce: unknown bounce_type"); | ||||
| 483 | } | ||||
| 484 | |||||
| 485 | TAILQ_FOREACH(evp, &s->msg->envelopes, entry)for((evp) = ((&s->msg->envelopes)->tqh_first); ( evp) != ((void *)0); (evp) = ((evp)->entry.tqe_next)) { | ||||
| 486 | io_xprint(s->io, evp->report); | ||||
| 487 | io_xprint(s->io, "\r\n"); | ||||
| 488 | } | ||||
| 489 | io_xprint(s->io, "\r\n"); | ||||
| 490 | |||||
| 491 | if (s->msg->bounce.type == B_DELAYED) | ||||
| 492 | io_xprintf(s->io, notice_warning2, | ||||
| 493 | bounce_duration(s->msg->bounce.ttl)); | ||||
| 494 | |||||
| 495 | io_xprintf(s->io, | ||||
| 496 | " Below is a copy of the original message:\r\n" | ||||
| 497 | "\r\n"); | ||||
| 498 | |||||
| 499 | io_xprintf(s->io, | ||||
| 500 | "--%16" PRIu64"llu" "/%s\r\n" | ||||
| 501 | "Content-Description: Delivery Report\r\n" | ||||
| 502 | "Content-Type: message/delivery-status\r\n" | ||||
| 503 | "\r\n", | ||||
| 504 | s->boundary, s->smtpname); | ||||
| 505 | |||||
| 506 | io_xprintf(s->io, | ||||
| 507 | "Reporting-MTA: dns; %s\r\n" | ||||
| 508 | "\r\n", | ||||
| 509 | s->smtpname); | ||||
| 510 | |||||
| 511 | TAILQ_FOREACH(evp, &s->msg->envelopes, entry)for((evp) = ((&s->msg->envelopes)->tqh_first); ( evp) != ((void *)0); (evp) = ((evp)->entry.tqe_next)) { | ||||
| 512 | io_xprintf(s->io, | ||||
| 513 | "Final-Recipient: rfc822; %s@%s\r\n" | ||||
| 514 | "Action: %s\r\n" | ||||
| 515 | "Status: %s\r\n" | ||||
| 516 | "\r\n", | ||||
| 517 | evp->dest.user, | ||||
| 518 | evp->dest.domain, | ||||
| 519 | action_str(&s->msg->bounce), | ||||
| 520 | esc_code(evp->esc_class, evp->esc_code)); | ||||
| 521 | } | ||||
| 522 | |||||
| 523 | log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",do { if (tracing & (0x0040)) log_trace0("bounce: %p: >>> [... %zu bytes ...]" , s, io_queued(s->io)); } while (0) | ||||
| 524 | s, io_queued(s->io))do { if (tracing & (0x0040)) log_trace0("bounce: %p: >>> [... %zu bytes ...]" , s, io_queued(s->io)); } while (0); | ||||
| 525 | |||||
| 526 | s->state = BOUNCE_DATA_MESSAGE; | ||||
| 527 | break; | ||||
| 528 | |||||
| 529 | case BOUNCE_DATA_MESSAGE: | ||||
| 530 | io_xprintf(s->io, | ||||
| 531 | "--%16" PRIu64"llu" "/%s\r\n" | ||||
| 532 | "Content-Description: Message headers\r\n" | ||||
| 533 | "Content-Type: text/rfc822-headers\r\n" | ||||
| 534 | "\r\n", | ||||
| 535 | s->boundary, s->smtpname); | ||||
| 536 | |||||
| 537 | n = io_queued(s->io); | ||||
| 538 | while (io_queued(s->io) < BOUNCE_HIWAT65535) { | ||||
| 539 | if ((len = getline(&line, &sz, s->msgfp)) == -1) | ||||
| 540 | break; | ||||
| 541 | if (len == 1 && line[0] == '\n' && /* end of headers */ | ||||
| 542 | (s->msg->bounce.type != B_FAILED || | ||||
| 543 | s->msg->bounce.dsn_ret != DSN_RETFULL)) { | ||||
| 544 | free(line); | ||||
| 545 | fclose(s->msgfp); | ||||
| 546 | s->msgfp = NULL((void *)0); | ||||
| 547 | io_xprintf(s->io, | ||||
| 548 | "\r\n--%16" PRIu64"llu" "/%s--\r\n", s->boundary, | ||||
| 549 | s->smtpname); | ||||
| 550 | bounce_send(s, "."); | ||||
| 551 | s->state = BOUNCE_DATA_END; | ||||
| 552 | return (0); | ||||
| 553 | } | ||||
| 554 | line[len - 1] = '\0'; | ||||
| 555 | io_xprintf(s->io, "%s%s\r\n", | ||||
| 556 | (len == 2 && line[0] == '.') ? "." : "", line); | ||||
| 557 | } | ||||
| 558 | free(line); | ||||
| 559 | |||||
| 560 | if (ferror(s->msgfp)(!__isthreaded ? (((s->msgfp)->_flags & 0x0040) != 0 ) : (ferror)(s->msgfp))) { | ||||
| 561 | fclose(s->msgfp); | ||||
| 562 | s->msgfp = NULL((void *)0); | ||||
| 563 | bounce_delivery(s->msg, IMSG_QUEUE_DELIVERY_TEMPFAIL, | ||||
| 564 | "Error reading message"); | ||||
| 565 | s->msg = NULL((void *)0); | ||||
| 566 | return (-1); | ||||
| 567 | } | ||||
| 568 | |||||
| 569 | io_xprintf(s->io, | ||||
| 570 | "\r\n--%16" PRIu64"llu" "/%s--\r\n", s->boundary, s->smtpname); | ||||
| 571 | |||||
| 572 | log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",do { if (tracing & (0x0040)) log_trace0("bounce: %p: >>> [... %zu bytes ...]" , s, io_queued(s->io) - n); } while (0) | ||||
| 573 | s, io_queued(s->io) - n)do { if (tracing & (0x0040)) log_trace0("bounce: %p: >>> [... %zu bytes ...]" , s, io_queued(s->io) - n); } while (0); | ||||
| 574 | |||||
| 575 | if (feof(s->msgfp)(!__isthreaded ? (((s->msgfp)->_flags & 0x0020) != 0 ) : (feof)(s->msgfp))) { | ||||
| 576 | fclose(s->msgfp); | ||||
| 577 | s->msgfp = NULL((void *)0); | ||||
| 578 | bounce_send(s, "."); | ||||
| 579 | s->state = BOUNCE_DATA_END; | ||||
| 580 | } | ||||
| 581 | break; | ||||
| 582 | |||||
| 583 | case BOUNCE_QUIT: | ||||
| 584 | bounce_send(s, "QUIT"); | ||||
| 585 | s->state = BOUNCE_CLOSE; | ||||
| 586 | break; | ||||
| 587 | |||||
| 588 | default: | ||||
| 589 | fatalx("bounce: bad state"); | ||||
| 590 | } | ||||
| 591 | |||||
| 592 | return (0); | ||||
| 593 | } | ||||
| 594 | |||||
| 595 | |||||
| 596 | static void | ||||
| 597 | bounce_delivery(struct bounce_message *msg, int delivery, const char *status) | ||||
| 598 | { | ||||
| 599 | struct bounce_envelope *be; | ||||
| 600 | struct envelope evp; | ||||
| 601 | size_t n; | ||||
| 602 | const char *f; | ||||
| 603 | |||||
| 604 | n = 0; | ||||
| 605 | while ((be = TAILQ_FIRST(&msg->envelopes)((&msg->envelopes)->tqh_first))) { | ||||
| 606 | if (delivery
| ||||
| 607 | if (queue_envelope_load(be->id, &evp) == 0) { | ||||
| |||||
| 608 | fatalx("could not reload envelope!"); | ||||
| 609 | } | ||||
| 610 | evp.retry++; | ||||
| 611 | evp.lasttry = msg->timeout; | ||||
| 612 | envelope_set_errormsg(&evp, "%s", status); | ||||
| 613 | queue_envelope_update(&evp); | ||||
| 614 | m_create(p_scheduler, delivery, 0, 0, -1); | ||||
| 615 | m_add_envelope(p_scheduler, &evp); | ||||
| 616 | m_close(p_scheduler); | ||||
| 617 | } else { | ||||
| 618 | m_create(p_scheduler, delivery, 0, 0, -1); | ||||
| 619 | m_add_evpid(p_scheduler, be->id); | ||||
| 620 | m_close(p_scheduler); | ||||
| 621 | queue_envelope_delete(be->id); | ||||
| 622 | } | ||||
| 623 | TAILQ_REMOVE(&msg->envelopes, be, entry)do { if (((be)->entry.tqe_next) != ((void *)0)) (be)->entry .tqe_next->entry.tqe_prev = (be)->entry.tqe_prev; else ( &msg->envelopes)->tqh_last = (be)->entry.tqe_prev ; *(be)->entry.tqe_prev = (be)->entry.tqe_next; ; ; } while (0); | ||||
| 624 | free(be->report); | ||||
| 625 | free(be); | ||||
| 626 | n += 1; | ||||
| 627 | } | ||||
| 628 | |||||
| 629 | |||||
| 630 | if (delivery == IMSG_QUEUE_DELIVERY_TEMPFAIL) | ||||
| 631 | f = "TempFail"; | ||||
| 632 | else if (delivery == IMSG_QUEUE_DELIVERY_PERMFAIL) | ||||
| 633 | f = "PermFail"; | ||||
| 634 | else | ||||
| 635 | f = NULL((void *)0); | ||||
| 636 | |||||
| 637 | if (f) | ||||
| 638 | log_warnx("warn: %s injecting failure report on message %08" | ||||
| 639 | PRIx32"x" " to <%s> for %zu envelope%s: %s", | ||||
| 640 | f, msg->msgid, msg->to, n, n > 1 ? "s":"", status); | ||||
| 641 | |||||
| 642 | nmessage -= 1; | ||||
| 643 | stat_decrement("bounce.message", 1); | ||||
| 644 | stat_decrement("bounce.envelope", n); | ||||
| 645 | free(msg->smtpname); | ||||
| 646 | free(msg->to); | ||||
| 647 | free(msg); | ||||
| 648 | } | ||||
| 649 | |||||
| 650 | static void | ||||
| 651 | bounce_status(struct bounce_session *s, const char *fmt, ...) | ||||
| 652 | { | ||||
| 653 | va_list ap; | ||||
| 654 | char *status; | ||||
| 655 | int len, delivery; | ||||
| 656 | |||||
| 657 | /* Ignore if there is no message */ | ||||
| 658 | if (s->msg == NULL((void *)0)) | ||||
| 659 | return; | ||||
| 660 | |||||
| 661 | va_start(ap, fmt)__builtin_va_start((ap), fmt); | ||||
| 662 | if ((len = vasprintf(&status, fmt, ap)) == -1) | ||||
| 663 | fatal("bounce: vasprintf"); | ||||
| 664 | va_end(ap)__builtin_va_end((ap)); | ||||
| 665 | |||||
| 666 | if (*status == '2') | ||||
| 667 | delivery = IMSG_QUEUE_DELIVERY_OK; | ||||
| 668 | else if (*status == '5' || *status == '6') | ||||
| 669 | delivery = IMSG_QUEUE_DELIVERY_PERMFAIL; | ||||
| 670 | else | ||||
| 671 | delivery = IMSG_QUEUE_DELIVERY_TEMPFAIL; | ||||
| 672 | |||||
| 673 | bounce_delivery(s->msg, delivery, status); | ||||
| 674 | s->msg = NULL((void *)0); | ||||
| 675 | if (s->msgfp) | ||||
| 676 | fclose(s->msgfp); | ||||
| 677 | |||||
| 678 | free(status); | ||||
| 679 | } | ||||
| 680 | |||||
| 681 | static void | ||||
| 682 | bounce_free(struct bounce_session *s) | ||||
| 683 | { | ||||
| 684 | log_debug("debug: bounce: %p: deleting session", s); | ||||
| 685 | |||||
| 686 | io_free(s->io); | ||||
| 687 | |||||
| 688 | free(s->smtpname); | ||||
| 689 | free(s); | ||||
| 690 | |||||
| 691 | running -= 1; | ||||
| 692 | stat_decrement("bounce.session", 1); | ||||
| 693 | bounce_drain(); | ||||
| 694 | } | ||||
| 695 | |||||
| 696 | static void | ||||
| 697 | bounce_io(struct io *io, int evt, void *arg) | ||||
| 698 | { | ||||
| 699 | struct bounce_session *s = arg; | ||||
| 700 | const char *error; | ||||
| 701 | char *line, *msg; | ||||
| 702 | int cont; | ||||
| 703 | size_t len; | ||||
| 704 | |||||
| 705 | log_trace(TRACE_IO, "bounce: %p: %s %s", s, io_strevent(evt),do { if (tracing & (0x0004)) log_trace0("bounce: %p: %s %s" , s, io_strevent(evt), io_strio(io)); } while (0) | ||||
| |||||
| 706 | io_strio(io))do { if (tracing & (0x0004)) log_trace0("bounce: %p: %s %s" , s, io_strevent(evt), io_strio(io)); } while (0); | ||||
| 707 | |||||
| 708 | switch (evt) { | ||||
| 709 | case IO_DATAIN: | ||||
| 710 | nextline: | ||||
| 711 | line = io_getline(s->io, &len); | ||||
| 712 | if (line == NULL((void *)0) && io_datalen(s->io) >= LINE_MAX2048) { | ||||
| 713 | bounce_status(s, "Input too long"); | ||||
| 714 | bounce_free(s); | ||||
| 715 | return; | ||||
| 716 | } | ||||
| 717 | |||||
| 718 | if (line == NULL((void *)0)) | ||||
| 719 | break; | ||||
| 720 | |||||
| 721 | /* Strip trailing '\r' */ | ||||
| 722 | if (len && line[len - 1] == '\r') | ||||
| 723 | line[--len] = '\0'; | ||||
| 724 | |||||
| 725 | log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line)do { if (tracing & (0x0040)) log_trace0("bounce: %p: <<< %s" , s, line); } while (0); | ||||
| 726 | |||||
| 727 | if ((error = parse_smtp_response(line, len, &msg, &cont))) { | ||||
| 728 | bounce_status(s, "Bad response: %s", error); | ||||
| 729 | bounce_free(s); | ||||
| 730 | return; | ||||
| 731 | } | ||||
| 732 | if (cont) | ||||
| 733 | goto nextline; | ||||
| 734 | |||||
| 735 | if (s->state == BOUNCE_CLOSE) { | ||||
| 736 | bounce_free(s); | ||||
| 737 | return; | ||||
| 738 | } | ||||
| 739 | |||||
| 740 | if (line[0] != '2' && line[0] != '3') { /* fail */ | ||||
| 741 | bounce_status(s, "%s", line); | ||||
| 742 | s->state = BOUNCE_QUIT; | ||||
| 743 | } else if (s->state == BOUNCE_DATA_END) { /* accepted */ | ||||
| 744 | bounce_status(s, "%s", line); | ||||
| 745 | } | ||||
| 746 | |||||
| 747 | if (bounce_next(s) == -1) { | ||||
| 748 | bounce_free(s); | ||||
| 749 | return; | ||||
| 750 | } | ||||
| 751 | |||||
| 752 | io_set_write(io); | ||||
| 753 | break; | ||||
| 754 | |||||
| 755 | case IO_LOWAT: | ||||
| 756 | if (s->state == BOUNCE_DATA_MESSAGE) | ||||
| 757 | if (bounce_next(s) == -1) { | ||||
| 758 | bounce_free(s); | ||||
| 759 | return; | ||||
| 760 | } | ||||
| 761 | if (io_queued(s->io) == 0) | ||||
| 762 | io_set_read(io); | ||||
| 763 | break; | ||||
| 764 | |||||
| 765 | default: | ||||
| 766 | bounce_status(s, "442 i/o error %d", evt); | ||||
| 767 | bounce_free(s); | ||||
| 768 | break; | ||||
| 769 | } | ||||
| 770 | } | ||||
| 771 | |||||
| 772 | static int | ||||
| 773 | bounce_message_cmp(const struct bounce_message *a, | ||||
| 774 | const struct bounce_message *b) | ||||
| 775 | { | ||||
| 776 | int r; | ||||
| 777 | |||||
| 778 | if (a->msgid < b->msgid) | ||||
| 779 | return (-1); | ||||
| 780 | if (a->msgid > b->msgid) | ||||
| 781 | return (1); | ||||
| 782 | if ((r = strcmp(a->smtpname, b->smtpname))) | ||||
| 783 | return (r); | ||||
| 784 | |||||
| 785 | return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce)); | ||||
| 786 | } | ||||
| 787 | |||||
| 788 | static const char * | ||||
| 789 | action_str(const struct delivery_bounce *b) | ||||
| 790 | { | ||||
| 791 | switch (b->type) { | ||||
| 792 | case B_FAILED: | ||||
| 793 | return ("failed"); | ||||
| 794 | case B_DELAYED: | ||||
| 795 | return ("delayed"); | ||||
| 796 | case B_DELIVERED: | ||||
| 797 | if (b->mta_without_dsn) | ||||
| 798 | return ("relayed"); | ||||
| 799 | |||||
| 800 | return ("delivered"); | ||||
| 801 | default: | ||||
| 802 | log_warn("warn: bounce: unknown bounce_type"); | ||||
| 803 | return (""); | ||||
| 804 | } | ||||
| 805 | } | ||||
| 806 | |||||
| 807 | SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,struct bounce_message * bounce_message_tree_SPLAY_INSERT(struct bounce_message_tree *head, struct bounce_message *elm) { if ( ((head)->sph_root == ((void *)0))) { (elm)->sp_entry.spe_left = (elm)->sp_entry.spe_right = ((void *)0); } else { int __comp ; bounce_message_tree_SPLAY(head, elm); __comp = (bounce_message_cmp )(elm, (head)->sph_root); if(__comp < 0) { (elm)->sp_entry .spe_left = ((head)->sph_root)->sp_entry.spe_left; (elm )->sp_entry.spe_right = (head)->sph_root; ((head)->sph_root )->sp_entry.spe_left = ((void *)0); } else if (__comp > 0) { (elm)->sp_entry.spe_right = ((head)->sph_root)-> sp_entry.spe_right; (elm)->sp_entry.spe_left = (head)-> sph_root; ((head)->sph_root)->sp_entry.spe_right = ((void *)0); } else return ((head)->sph_root); } (head)->sph_root = (elm); return (((void *)0)); } struct bounce_message * bounce_message_tree_SPLAY_REMOVE (struct bounce_message_tree *head, struct bounce_message *elm ) { struct bounce_message *__tmp; if (((head)->sph_root == ((void *)0))) return (((void *)0)); bounce_message_tree_SPLAY (head, elm); if ((bounce_message_cmp)(elm, (head)->sph_root ) == 0) { if (((head)->sph_root)->sp_entry.spe_left == ( (void *)0)) { (head)->sph_root = ((head)->sph_root)-> sp_entry.spe_right; } else { __tmp = ((head)->sph_root)-> sp_entry.spe_right; (head)->sph_root = ((head)->sph_root )->sp_entry.spe_left; bounce_message_tree_SPLAY(head, elm) ; ((head)->sph_root)->sp_entry.spe_right = __tmp; } return (elm); } return (((void *)0)); } void bounce_message_tree_SPLAY (struct bounce_message_tree *head, struct bounce_message *elm ) { struct bounce_message __node, *__left, *__right, *__tmp; int __comp; (&__node)->sp_entry.spe_left = (&__node)-> sp_entry.spe_right = ((void *)0); __left = __right = &__node ; while ((__comp = (bounce_message_cmp)(elm, (head)->sph_root ))) { if (__comp < 0) { __tmp = ((head)->sph_root)-> sp_entry.spe_left; if (__tmp == ((void *)0)) break; if ((bounce_message_cmp )(elm, __tmp) < 0){ do { ((head)->sph_root)->sp_entry .spe_left = (__tmp)->sp_entry.spe_right; (__tmp)->sp_entry .spe_right = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->sp_entry.spe_left == ((void *)0)) break; } do { (__right)->sp_entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head) ->sph_root = ((head)->sph_root)->sp_entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root )->sp_entry.spe_right; if (__tmp == ((void *)0)) break; if ((bounce_message_cmp)(elm, __tmp) > 0){ do { ((head)-> sph_root)->sp_entry.spe_right = (__tmp)->sp_entry.spe_left ; (__tmp)->sp_entry.spe_left = (head)->sph_root; (head) ->sph_root = __tmp; } while (0); if (((head)->sph_root) ->sp_entry.spe_right == ((void *)0)) break; } do { (__left )->sp_entry.spe_right = (head)->sph_root; __left = (head )->sph_root; (head)->sph_root = ((head)->sph_root)-> sp_entry.spe_right; } while (0); } } do { (__left)->sp_entry .spe_right = ((head)->sph_root)->sp_entry.spe_left; (__right )->sp_entry.spe_left = ((head)->sph_root)->sp_entry. spe_right; ((head)->sph_root)->sp_entry.spe_left = (& __node)->sp_entry.spe_right; ((head)->sph_root)->sp_entry .spe_right = (&__node)->sp_entry.spe_left; } while (0) ; } void bounce_message_tree_SPLAY_MINMAX(struct bounce_message_tree *head, int __comp) { struct bounce_message __node, *__left, * __right, *__tmp; (&__node)->sp_entry.spe_left = (& __node)->sp_entry.spe_right = ((void *)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head )->sph_root)->sp_entry.spe_left; if (__tmp == ((void *) 0)) break; if (__comp < 0){ do { ((head)->sph_root)-> sp_entry.spe_left = (__tmp)->sp_entry.spe_right; (__tmp)-> sp_entry.spe_right = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->sp_entry .spe_left == ((void *)0)) break; } do { (__right)->sp_entry .spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->sp_entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->sp_entry.spe_right; if (__tmp == ((void *)0)) break ; if (__comp > 0) { do { ((head)->sph_root)->sp_entry .spe_right = (__tmp)->sp_entry.spe_left; (__tmp)->sp_entry .spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->sp_entry.spe_right == ((void *)0)) break; } do { (__left)->sp_entry.spe_right = (head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->sp_entry.spe_right; } while (0); } } do { (__left)->sp_entry.spe_right = ((head)-> sph_root)->sp_entry.spe_left; (__right)->sp_entry.spe_left = ((head)->sph_root)->sp_entry.spe_right; ((head)-> sph_root)->sp_entry.spe_left = (&__node)->sp_entry. spe_right; ((head)->sph_root)->sp_entry.spe_right = (& __node)->sp_entry.spe_left; } while (0); } | ||||
| 808 | bounce_message_cmp)struct bounce_message * bounce_message_tree_SPLAY_INSERT(struct bounce_message_tree *head, struct bounce_message *elm) { if ( ((head)->sph_root == ((void *)0))) { (elm)->sp_entry.spe_left = (elm)->sp_entry.spe_right = ((void *)0); } else { int __comp ; bounce_message_tree_SPLAY(head, elm); __comp = (bounce_message_cmp )(elm, (head)->sph_root); if(__comp < 0) { (elm)->sp_entry .spe_left = ((head)->sph_root)->sp_entry.spe_left; (elm )->sp_entry.spe_right = (head)->sph_root; ((head)->sph_root )->sp_entry.spe_left = ((void *)0); } else if (__comp > 0) { (elm)->sp_entry.spe_right = ((head)->sph_root)-> sp_entry.spe_right; (elm)->sp_entry.spe_left = (head)-> sph_root; ((head)->sph_root)->sp_entry.spe_right = ((void *)0); } else return ((head)->sph_root); } (head)->sph_root = (elm); return (((void *)0)); } struct bounce_message * bounce_message_tree_SPLAY_REMOVE (struct bounce_message_tree *head, struct bounce_message *elm ) { struct bounce_message *__tmp; if (((head)->sph_root == ((void *)0))) return (((void *)0)); bounce_message_tree_SPLAY (head, elm); if ((bounce_message_cmp)(elm, (head)->sph_root ) == 0) { if (((head)->sph_root)->sp_entry.spe_left == ( (void *)0)) { (head)->sph_root = ((head)->sph_root)-> sp_entry.spe_right; } else { __tmp = ((head)->sph_root)-> sp_entry.spe_right; (head)->sph_root = ((head)->sph_root )->sp_entry.spe_left; bounce_message_tree_SPLAY(head, elm) ; ((head)->sph_root)->sp_entry.spe_right = __tmp; } return (elm); } return (((void *)0)); } void bounce_message_tree_SPLAY (struct bounce_message_tree *head, struct bounce_message *elm ) { struct bounce_message __node, *__left, *__right, *__tmp; int __comp; (&__node)->sp_entry.spe_left = (&__node)-> sp_entry.spe_right = ((void *)0); __left = __right = &__node ; while ((__comp = (bounce_message_cmp)(elm, (head)->sph_root ))) { if (__comp < 0) { __tmp = ((head)->sph_root)-> sp_entry.spe_left; if (__tmp == ((void *)0)) break; if ((bounce_message_cmp )(elm, __tmp) < 0){ do { ((head)->sph_root)->sp_entry .spe_left = (__tmp)->sp_entry.spe_right; (__tmp)->sp_entry .spe_right = (head)->sph_root; (head)->sph_root = __tmp ; } while (0); if (((head)->sph_root)->sp_entry.spe_left == ((void *)0)) break; } do { (__right)->sp_entry.spe_left = (head)->sph_root; __right = (head)->sph_root; (head) ->sph_root = ((head)->sph_root)->sp_entry.spe_left; } while (0); } else if (__comp > 0) { __tmp = ((head)->sph_root )->sp_entry.spe_right; if (__tmp == ((void *)0)) break; if ((bounce_message_cmp)(elm, __tmp) > 0){ do { ((head)-> sph_root)->sp_entry.spe_right = (__tmp)->sp_entry.spe_left ; (__tmp)->sp_entry.spe_left = (head)->sph_root; (head) ->sph_root = __tmp; } while (0); if (((head)->sph_root) ->sp_entry.spe_right == ((void *)0)) break; } do { (__left )->sp_entry.spe_right = (head)->sph_root; __left = (head )->sph_root; (head)->sph_root = ((head)->sph_root)-> sp_entry.spe_right; } while (0); } } do { (__left)->sp_entry .spe_right = ((head)->sph_root)->sp_entry.spe_left; (__right )->sp_entry.spe_left = ((head)->sph_root)->sp_entry. spe_right; ((head)->sph_root)->sp_entry.spe_left = (& __node)->sp_entry.spe_right; ((head)->sph_root)->sp_entry .spe_right = (&__node)->sp_entry.spe_left; } while (0) ; } void bounce_message_tree_SPLAY_MINMAX(struct bounce_message_tree *head, int __comp) { struct bounce_message __node, *__left, * __right, *__tmp; (&__node)->sp_entry.spe_left = (& __node)->sp_entry.spe_right = ((void *)0); __left = __right = &__node; while (1) { if (__comp < 0) { __tmp = ((head )->sph_root)->sp_entry.spe_left; if (__tmp == ((void *) 0)) break; if (__comp < 0){ do { ((head)->sph_root)-> sp_entry.spe_left = (__tmp)->sp_entry.spe_right; (__tmp)-> sp_entry.spe_right = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->sp_entry .spe_left == ((void *)0)) break; } do { (__right)->sp_entry .spe_left = (head)->sph_root; __right = (head)->sph_root ; (head)->sph_root = ((head)->sph_root)->sp_entry.spe_left ; } while (0); } else if (__comp > 0) { __tmp = ((head)-> sph_root)->sp_entry.spe_right; if (__tmp == ((void *)0)) break ; if (__comp > 0) { do { ((head)->sph_root)->sp_entry .spe_right = (__tmp)->sp_entry.spe_left; (__tmp)->sp_entry .spe_left = (head)->sph_root; (head)->sph_root = __tmp; } while (0); if (((head)->sph_root)->sp_entry.spe_right == ((void *)0)) break; } do { (__left)->sp_entry.spe_right = (head)->sph_root; __left = (head)->sph_root; (head)-> sph_root = ((head)->sph_root)->sp_entry.spe_right; } while (0); } } do { (__left)->sp_entry.spe_right = ((head)-> sph_root)->sp_entry.spe_left; (__right)->sp_entry.spe_left = ((head)->sph_root)->sp_entry.spe_right; ((head)-> sph_root)->sp_entry.spe_left = (&__node)->sp_entry. spe_right; ((head)->sph_root)->sp_entry.spe_right = (& __node)->sp_entry.spe_left; } while (0); }; |