Bug Summary

File:src/usr.sbin/smtpd/smtpd/../bounce.c
Warning:line 662, column 7
Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name bounce.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.sbin/smtpd/smtpd/.. -D IO_TLS -D QUEUE_PROFILING -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/smtpd/smtpd/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/smtpd/smtpd/../bounce.c
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
34enum {
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
46struct 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
55struct 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
66struct 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
75SPLAY_HEAD(bounce_message_tree, bounce_message)struct bounce_message_tree { struct bounce_message *sph_root;
}
;
76static int bounce_message_cmp(const struct bounce_message *,
77 const struct bounce_message *);
78SPLAY_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
81static void bounce_drain(void);
82static void bounce_send(struct bounce_session *, const char *, ...)
83 __attribute__((__format__ (printf, 2, 3)));
84static int bounce_next_message(struct bounce_session *);
85static int bounce_next(struct bounce_session *);
86static void bounce_delivery(struct bounce_message *, int, const char *);
87static void bounce_status(struct bounce_session *, const char *, ...)
88 __attribute__((__format__ (printf, 2, 3)));
89static void bounce_io(struct io *, int, void *);
90static void bounce_timeout(int, short, void *);
91static void bounce_free(struct bounce_session *);
92static const char *action_str(const struct delivery_bounce *);
93
94static struct tree wait_fd;
95static struct bounce_message_tree messages;
96static TAILQ_HEAD(, bounce_message)struct { struct bounce_message *tqh_first; struct bounce_message
**tqh_last; }
pending;
97
98static int nmessage = 0;
99static int running = 0;
100static struct event ev_timer;
101
102static void
103bounce_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
116void
117bounce_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
198void
199bounce_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
231static void
232bounce_timeout(int fd, short ev, void *arg)
233{
234 log_debug("debug: bounce: timeout");
235
236 bounce_drain();
237}
238
239static void
240bounce_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
289static void
290bounce_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
308static const char *
309bounce_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
338const 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
342const 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
346const 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
351const char *notice_success =
352 " Your message was successfully delivered to these recipients.\r\n\r\n";
353
354const char *notice_relay =
355 " Your message was relayed to these recipients.\r\n\r\n";
356
357static int
358bounce_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
400static int
401bounce_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
596static void
597bounce_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 == IMSG_QUEUE_DELIVERY_TEMPFAIL) {
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
650static void
651bounce_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)
Although the value stored to 'len' is used in the enclosing expression, the value is never actually read from 'len'
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
681static void
682bounce_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
696static void
697bounce_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
772static int
773bounce_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
788static const char *
789action_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
807SPLAY_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); }
;