Bug Summary

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