1*3ef9cbf7Sgilles /* 2*3ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 3*3ef9cbf7Sgilles * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 4*3ef9cbf7Sgilles * 5*3ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any 6*3ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above 7*3ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies. 8*3ef9cbf7Sgilles * 9*3ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10*3ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11*3ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12*3ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13*3ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14*3ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15*3ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16*3ef9cbf7Sgilles */ 17*3ef9cbf7Sgilles 18*3ef9cbf7Sgilles #include <sys/types.h> 19*3ef9cbf7Sgilles #include <sys/queue.h> 20*3ef9cbf7Sgilles #include <sys/tree.h> 21*3ef9cbf7Sgilles #include <sys/param.h> 22*3ef9cbf7Sgilles #include <sys/socket.h> 23*3ef9cbf7Sgilles 24*3ef9cbf7Sgilles #include <netinet/in.h> 25*3ef9cbf7Sgilles #include <arpa/inet.h> 26*3ef9cbf7Sgilles 27*3ef9cbf7Sgilles #include <err.h> 28*3ef9cbf7Sgilles #include <errno.h> 29*3ef9cbf7Sgilles #include <event.h> 30*3ef9cbf7Sgilles #include <fcntl.h> 31*3ef9cbf7Sgilles #include <pwd.h> 32*3ef9cbf7Sgilles #include <signal.h> 33*3ef9cbf7Sgilles #include <stdio.h> 34*3ef9cbf7Sgilles #include <stdint.h> 35*3ef9cbf7Sgilles #include <stdlib.h> 36*3ef9cbf7Sgilles #include <string.h> 37*3ef9cbf7Sgilles #include <unistd.h> 38*3ef9cbf7Sgilles 39*3ef9cbf7Sgilles #include "smtpd.h" 40*3ef9cbf7Sgilles 41*3ef9cbf7Sgilles __dead void mta_shutdown(void); 42*3ef9cbf7Sgilles void mta_sig_handler(int, short, void *); 43*3ef9cbf7Sgilles void mta_dispatch_parent(int, short, void *); 44*3ef9cbf7Sgilles void mta_dispatch_queue(int, short, void *); 45*3ef9cbf7Sgilles void mta_setup_events(struct smtpd *); 46*3ef9cbf7Sgilles void mta_disable_events(struct smtpd *); 47*3ef9cbf7Sgilles void mta_timeout(int, short, void *); 48*3ef9cbf7Sgilles void mta_write(int, short, void *); 49*3ef9cbf7Sgilles int mta_connect(struct batch *); 50*3ef9cbf7Sgilles void mta_read_handler(struct bufferevent *, void *); 51*3ef9cbf7Sgilles void mta_write_handler(struct bufferevent *, void *); 52*3ef9cbf7Sgilles void mta_error_handler(struct bufferevent *, short, void *); 53*3ef9cbf7Sgilles int mta_reply_handler(struct bufferevent *, void *); 54*3ef9cbf7Sgilles void mta_batch_update_queue(struct batch *); 55*3ef9cbf7Sgilles 56*3ef9cbf7Sgilles void 57*3ef9cbf7Sgilles mta_sig_handler(int sig, short event, void *p) 58*3ef9cbf7Sgilles { 59*3ef9cbf7Sgilles switch (sig) { 60*3ef9cbf7Sgilles case SIGINT: 61*3ef9cbf7Sgilles case SIGTERM: 62*3ef9cbf7Sgilles mta_shutdown(); 63*3ef9cbf7Sgilles break; 64*3ef9cbf7Sgilles default: 65*3ef9cbf7Sgilles fatalx("mta_sig_handler: unexpected signal"); 66*3ef9cbf7Sgilles } 67*3ef9cbf7Sgilles } 68*3ef9cbf7Sgilles 69*3ef9cbf7Sgilles void 70*3ef9cbf7Sgilles mta_dispatch_parent(int sig, short event, void *p) 71*3ef9cbf7Sgilles { 72*3ef9cbf7Sgilles struct smtpd *env = p; 73*3ef9cbf7Sgilles struct imsgbuf *ibuf; 74*3ef9cbf7Sgilles struct imsg imsg; 75*3ef9cbf7Sgilles ssize_t n; 76*3ef9cbf7Sgilles 77*3ef9cbf7Sgilles ibuf = env->sc_ibufs[PROC_PARENT]; 78*3ef9cbf7Sgilles switch (event) { 79*3ef9cbf7Sgilles case EV_READ: 80*3ef9cbf7Sgilles if ((n = imsg_read(ibuf)) == -1) 81*3ef9cbf7Sgilles fatal("imsg_read_error"); 82*3ef9cbf7Sgilles if (n == 0) { 83*3ef9cbf7Sgilles /* this pipe is dead, so remove the event handler */ 84*3ef9cbf7Sgilles event_del(&ibuf->ev); 85*3ef9cbf7Sgilles event_loopexit(NULL); 86*3ef9cbf7Sgilles return; 87*3ef9cbf7Sgilles } 88*3ef9cbf7Sgilles break; 89*3ef9cbf7Sgilles case EV_WRITE: 90*3ef9cbf7Sgilles if (msgbuf_write(&ibuf->w) == -1) 91*3ef9cbf7Sgilles fatal("msgbuf_write"); 92*3ef9cbf7Sgilles imsg_event_add(ibuf); 93*3ef9cbf7Sgilles return; 94*3ef9cbf7Sgilles default: 95*3ef9cbf7Sgilles fatalx("unknown event"); 96*3ef9cbf7Sgilles } 97*3ef9cbf7Sgilles 98*3ef9cbf7Sgilles for (;;) { 99*3ef9cbf7Sgilles if ((n = imsg_get(ibuf, &imsg)) == -1) 100*3ef9cbf7Sgilles fatal("parent_dispatch_mta: imsg_read error"); 101*3ef9cbf7Sgilles if (n == 0) 102*3ef9cbf7Sgilles break; 103*3ef9cbf7Sgilles 104*3ef9cbf7Sgilles switch (imsg.hdr.type) { 105*3ef9cbf7Sgilles default: 106*3ef9cbf7Sgilles log_debug("parent_dispatch_mta: unexpected imsg %d", 107*3ef9cbf7Sgilles imsg.hdr.type); 108*3ef9cbf7Sgilles break; 109*3ef9cbf7Sgilles } 110*3ef9cbf7Sgilles imsg_free(&imsg); 111*3ef9cbf7Sgilles } 112*3ef9cbf7Sgilles imsg_event_add(ibuf); 113*3ef9cbf7Sgilles } 114*3ef9cbf7Sgilles 115*3ef9cbf7Sgilles void 116*3ef9cbf7Sgilles mta_dispatch_queue(int sig, short event, void *p) 117*3ef9cbf7Sgilles { 118*3ef9cbf7Sgilles struct smtpd *env = p; 119*3ef9cbf7Sgilles struct imsgbuf *ibuf; 120*3ef9cbf7Sgilles struct imsg imsg; 121*3ef9cbf7Sgilles ssize_t n; 122*3ef9cbf7Sgilles 123*3ef9cbf7Sgilles ibuf = env->sc_ibufs[PROC_QUEUE]; 124*3ef9cbf7Sgilles switch (event) { 125*3ef9cbf7Sgilles case EV_READ: 126*3ef9cbf7Sgilles if ((n = imsg_read(ibuf)) == -1) 127*3ef9cbf7Sgilles fatal("imsg_read_error"); 128*3ef9cbf7Sgilles if (n == 0) { 129*3ef9cbf7Sgilles /* this pipe is dead, so remove the event handler */ 130*3ef9cbf7Sgilles event_del(&ibuf->ev); 131*3ef9cbf7Sgilles event_loopexit(NULL); 132*3ef9cbf7Sgilles return; 133*3ef9cbf7Sgilles } 134*3ef9cbf7Sgilles break; 135*3ef9cbf7Sgilles case EV_WRITE: 136*3ef9cbf7Sgilles if (msgbuf_write(&ibuf->w) == -1) 137*3ef9cbf7Sgilles fatal("msgbuf_write"); 138*3ef9cbf7Sgilles imsg_event_add(ibuf); 139*3ef9cbf7Sgilles return; 140*3ef9cbf7Sgilles default: 141*3ef9cbf7Sgilles fatalx("unknown event"); 142*3ef9cbf7Sgilles } 143*3ef9cbf7Sgilles 144*3ef9cbf7Sgilles for (;;) { 145*3ef9cbf7Sgilles if ((n = imsg_get(ibuf, &imsg)) == -1) 146*3ef9cbf7Sgilles fatal("parent_dispatch_mta: imsg_read error"); 147*3ef9cbf7Sgilles if (n == 0) 148*3ef9cbf7Sgilles break; 149*3ef9cbf7Sgilles 150*3ef9cbf7Sgilles switch (imsg.hdr.type) { 151*3ef9cbf7Sgilles case IMSG_CREATE_BATCH: { 152*3ef9cbf7Sgilles struct batch *batchp; 153*3ef9cbf7Sgilles 154*3ef9cbf7Sgilles batchp = calloc(1, sizeof (struct batch)); 155*3ef9cbf7Sgilles if (batchp == NULL) 156*3ef9cbf7Sgilles err(1, "calloc"); 157*3ef9cbf7Sgilles 158*3ef9cbf7Sgilles *batchp = *(struct batch *)imsg.data; 159*3ef9cbf7Sgilles batchp->ss_off = 0; 160*3ef9cbf7Sgilles batchp->env = env; 161*3ef9cbf7Sgilles batchp->flags = 0; 162*3ef9cbf7Sgilles 163*3ef9cbf7Sgilles TAILQ_INIT(&batchp->messages); 164*3ef9cbf7Sgilles SPLAY_INSERT(batchtree, &env->batch_queue, batchp); 165*3ef9cbf7Sgilles 166*3ef9cbf7Sgilles break; 167*3ef9cbf7Sgilles } 168*3ef9cbf7Sgilles case IMSG_BATCH_APPEND: { 169*3ef9cbf7Sgilles struct batch *batchp; 170*3ef9cbf7Sgilles struct message *messagep; 171*3ef9cbf7Sgilles 172*3ef9cbf7Sgilles messagep = calloc(1, sizeof (struct message)); 173*3ef9cbf7Sgilles if (messagep == NULL) 174*3ef9cbf7Sgilles fatal("calloc"); 175*3ef9cbf7Sgilles 176*3ef9cbf7Sgilles *messagep = *(struct message *)imsg.data; 177*3ef9cbf7Sgilles 178*3ef9cbf7Sgilles batchp = batch_by_id(env, messagep->batch_id); 179*3ef9cbf7Sgilles if (batchp == NULL) 180*3ef9cbf7Sgilles errx(1, "%s: internal inconsistency.", __func__); 181*3ef9cbf7Sgilles 182*3ef9cbf7Sgilles TAILQ_INSERT_TAIL(&batchp->messages, messagep, entry); 183*3ef9cbf7Sgilles 184*3ef9cbf7Sgilles break; 185*3ef9cbf7Sgilles } 186*3ef9cbf7Sgilles case IMSG_BATCH_CLOSE: { 187*3ef9cbf7Sgilles struct batch *batchp; 188*3ef9cbf7Sgilles 189*3ef9cbf7Sgilles batchp = (struct batch *)imsg.data; 190*3ef9cbf7Sgilles batchp = batch_by_id(env, batchp->id); 191*3ef9cbf7Sgilles if (batchp == NULL) 192*3ef9cbf7Sgilles errx(1, "%s: internal inconsistency.", __func__); 193*3ef9cbf7Sgilles 194*3ef9cbf7Sgilles batchp->flags |= F_BATCH_COMPLETE; 195*3ef9cbf7Sgilles 196*3ef9cbf7Sgilles while (! mta_connect(batchp)) { 197*3ef9cbf7Sgilles if (batchp->ss_off == batchp->ss_cnt) { 198*3ef9cbf7Sgilles break; 199*3ef9cbf7Sgilles } 200*3ef9cbf7Sgilles } 201*3ef9cbf7Sgilles break; 202*3ef9cbf7Sgilles } 203*3ef9cbf7Sgilles case IMSG_QUEUE_MESSAGE_FD: { 204*3ef9cbf7Sgilles struct batch *batchp; 205*3ef9cbf7Sgilles int fd; 206*3ef9cbf7Sgilles 207*3ef9cbf7Sgilles if ((fd = imsg_get_fd(ibuf, &imsg)) == -1) { 208*3ef9cbf7Sgilles /* NEEDS_FIX - unsure yet how it must be handled */ 209*3ef9cbf7Sgilles errx(1, "imsg_get_fd"); 210*3ef9cbf7Sgilles } 211*3ef9cbf7Sgilles 212*3ef9cbf7Sgilles batchp = (struct batch *)imsg.data; 213*3ef9cbf7Sgilles batchp = batch_by_id(env, batchp->id); 214*3ef9cbf7Sgilles 215*3ef9cbf7Sgilles if ((batchp->messagefp = fdopen(fd, "r")) == NULL) 216*3ef9cbf7Sgilles err(1, "fdopen"); 217*3ef9cbf7Sgilles 218*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "DATA\r\n"); 219*3ef9cbf7Sgilles 220*3ef9cbf7Sgilles bufferevent_enable(batchp->bev, EV_WRITE|EV_READ); 221*3ef9cbf7Sgilles break; 222*3ef9cbf7Sgilles } 223*3ef9cbf7Sgilles default: 224*3ef9cbf7Sgilles log_debug("parent_dispatch_mta: unexpected imsg %d", 225*3ef9cbf7Sgilles imsg.hdr.type); 226*3ef9cbf7Sgilles break; 227*3ef9cbf7Sgilles } 228*3ef9cbf7Sgilles imsg_free(&imsg); 229*3ef9cbf7Sgilles } 230*3ef9cbf7Sgilles imsg_event_add(ibuf); 231*3ef9cbf7Sgilles } 232*3ef9cbf7Sgilles 233*3ef9cbf7Sgilles void 234*3ef9cbf7Sgilles mta_shutdown(void) 235*3ef9cbf7Sgilles { 236*3ef9cbf7Sgilles log_info("mail transfer agent exiting"); 237*3ef9cbf7Sgilles _exit(0); 238*3ef9cbf7Sgilles } 239*3ef9cbf7Sgilles 240*3ef9cbf7Sgilles void 241*3ef9cbf7Sgilles mta_setup_events(struct smtpd *env) 242*3ef9cbf7Sgilles { 243*3ef9cbf7Sgilles struct timeval tv; 244*3ef9cbf7Sgilles 245*3ef9cbf7Sgilles evtimer_set(&env->sc_ev, mta_timeout, env); 246*3ef9cbf7Sgilles tv.tv_sec = 3; 247*3ef9cbf7Sgilles tv.tv_usec = 0; 248*3ef9cbf7Sgilles evtimer_add(&env->sc_ev, &tv); 249*3ef9cbf7Sgilles } 250*3ef9cbf7Sgilles 251*3ef9cbf7Sgilles void 252*3ef9cbf7Sgilles mta_disable_events(struct smtpd *env) 253*3ef9cbf7Sgilles { 254*3ef9cbf7Sgilles evtimer_del(&env->sc_ev); 255*3ef9cbf7Sgilles } 256*3ef9cbf7Sgilles 257*3ef9cbf7Sgilles void 258*3ef9cbf7Sgilles mta_timeout(int fd, short event, void *p) 259*3ef9cbf7Sgilles { 260*3ef9cbf7Sgilles struct smtpd *env = p; 261*3ef9cbf7Sgilles struct timeval tv; 262*3ef9cbf7Sgilles 263*3ef9cbf7Sgilles tv.tv_sec = 3; 264*3ef9cbf7Sgilles tv.tv_usec = 0; 265*3ef9cbf7Sgilles evtimer_add(&env->sc_ev, &tv); 266*3ef9cbf7Sgilles } 267*3ef9cbf7Sgilles 268*3ef9cbf7Sgilles pid_t 269*3ef9cbf7Sgilles mta(struct smtpd *env) 270*3ef9cbf7Sgilles { 271*3ef9cbf7Sgilles pid_t pid; 272*3ef9cbf7Sgilles 273*3ef9cbf7Sgilles struct passwd *pw; 274*3ef9cbf7Sgilles struct event ev_sigint; 275*3ef9cbf7Sgilles struct event ev_sigterm; 276*3ef9cbf7Sgilles 277*3ef9cbf7Sgilles struct peer peers[] = { 278*3ef9cbf7Sgilles { PROC_QUEUE, mta_dispatch_queue } 279*3ef9cbf7Sgilles }; 280*3ef9cbf7Sgilles 281*3ef9cbf7Sgilles switch (pid = fork()) { 282*3ef9cbf7Sgilles case -1: 283*3ef9cbf7Sgilles fatal("mta: cannot fork"); 284*3ef9cbf7Sgilles case 0: 285*3ef9cbf7Sgilles break; 286*3ef9cbf7Sgilles default: 287*3ef9cbf7Sgilles return (pid); 288*3ef9cbf7Sgilles } 289*3ef9cbf7Sgilles 290*3ef9cbf7Sgilles purge_config(env, PURGE_EVERYTHING); 291*3ef9cbf7Sgilles 292*3ef9cbf7Sgilles pw = env->sc_pw; 293*3ef9cbf7Sgilles #ifndef DEBUG 294*3ef9cbf7Sgilles if (chroot(pw->pw_dir) == -1) 295*3ef9cbf7Sgilles fatal("mta: chroot"); 296*3ef9cbf7Sgilles if (chdir("/") == -1) 297*3ef9cbf7Sgilles fatal("mta: chdir(\"/\")"); 298*3ef9cbf7Sgilles #else 299*3ef9cbf7Sgilles #warning disabling privilege revocation and chroot in DEBUG MODE 300*3ef9cbf7Sgilles #endif 301*3ef9cbf7Sgilles 302*3ef9cbf7Sgilles setproctitle("mail transfer agent"); 303*3ef9cbf7Sgilles smtpd_process = PROC_MTA; 304*3ef9cbf7Sgilles 305*3ef9cbf7Sgilles #ifndef DEBUG 306*3ef9cbf7Sgilles if (setgroups(1, &pw->pw_gid) || 307*3ef9cbf7Sgilles setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 308*3ef9cbf7Sgilles setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 309*3ef9cbf7Sgilles fatal("mta: cannot drop privileges"); 310*3ef9cbf7Sgilles #endif 311*3ef9cbf7Sgilles 312*3ef9cbf7Sgilles event_init(); 313*3ef9cbf7Sgilles 314*3ef9cbf7Sgilles signal_set(&ev_sigint, SIGINT, mta_sig_handler, env); 315*3ef9cbf7Sgilles signal_set(&ev_sigterm, SIGTERM, mta_sig_handler, env); 316*3ef9cbf7Sgilles signal_add(&ev_sigint, NULL); 317*3ef9cbf7Sgilles signal_add(&ev_sigterm, NULL); 318*3ef9cbf7Sgilles signal(SIGPIPE, SIG_IGN); 319*3ef9cbf7Sgilles signal(SIGHUP, SIG_IGN); 320*3ef9cbf7Sgilles 321*3ef9cbf7Sgilles config_peers(env, peers, 1); 322*3ef9cbf7Sgilles 323*3ef9cbf7Sgilles SPLAY_INIT(&env->batch_queue); 324*3ef9cbf7Sgilles 325*3ef9cbf7Sgilles mta_setup_events(env); 326*3ef9cbf7Sgilles event_dispatch(); 327*3ef9cbf7Sgilles mta_shutdown(); 328*3ef9cbf7Sgilles 329*3ef9cbf7Sgilles return (0); 330*3ef9cbf7Sgilles } 331*3ef9cbf7Sgilles 332*3ef9cbf7Sgilles /* shamelessly ripped usr.sbin/relayd/check_tcp.c ;) */ 333*3ef9cbf7Sgilles int 334*3ef9cbf7Sgilles mta_connect(struct batch *batchp) 335*3ef9cbf7Sgilles { 336*3ef9cbf7Sgilles int s; 337*3ef9cbf7Sgilles int type; 338*3ef9cbf7Sgilles struct linger lng; 339*3ef9cbf7Sgilles struct sockaddr_in ssin; 340*3ef9cbf7Sgilles struct sockaddr_in6 ssin6; 341*3ef9cbf7Sgilles 342*3ef9cbf7Sgilles if ((s = socket(batchp->ss[batchp->ss_off].ss_family, SOCK_STREAM, 0)) == -1) { 343*3ef9cbf7Sgilles goto bad; 344*3ef9cbf7Sgilles } 345*3ef9cbf7Sgilles 346*3ef9cbf7Sgilles bzero(&lng, sizeof(lng)); 347*3ef9cbf7Sgilles if (setsockopt(s, SOL_SOCKET, SO_LINGER, &lng, sizeof (lng)) == -1) { 348*3ef9cbf7Sgilles goto bad; 349*3ef9cbf7Sgilles } 350*3ef9cbf7Sgilles 351*3ef9cbf7Sgilles type = 1; 352*3ef9cbf7Sgilles if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &type, sizeof (type)) == -1) { 353*3ef9cbf7Sgilles goto bad; 354*3ef9cbf7Sgilles } 355*3ef9cbf7Sgilles 356*3ef9cbf7Sgilles if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { 357*3ef9cbf7Sgilles goto bad; 358*3ef9cbf7Sgilles } 359*3ef9cbf7Sgilles 360*3ef9cbf7Sgilles if (batchp->ss[batchp->ss_off].ss_family == PF_INET) { 361*3ef9cbf7Sgilles ssin = *(struct sockaddr_in *)&batchp->ss[batchp->ss_off]; 362*3ef9cbf7Sgilles if (connect(s, (struct sockaddr *)&ssin, sizeof(struct sockaddr_in)) == -1) { 363*3ef9cbf7Sgilles if (errno != EINPROGRESS) { 364*3ef9cbf7Sgilles goto bad; 365*3ef9cbf7Sgilles } 366*3ef9cbf7Sgilles } 367*3ef9cbf7Sgilles } 368*3ef9cbf7Sgilles 369*3ef9cbf7Sgilles if (batchp->ss[batchp->ss_off].ss_family == PF_INET6) { 370*3ef9cbf7Sgilles ssin6 = *(struct sockaddr_in6 *)&batchp->ss[batchp->ss_off]; 371*3ef9cbf7Sgilles if (connect(s, (struct sockaddr *)&ssin6, sizeof(struct sockaddr_in6)) == -1) { 372*3ef9cbf7Sgilles if (errno != EINPROGRESS) { 373*3ef9cbf7Sgilles goto bad; 374*3ef9cbf7Sgilles } 375*3ef9cbf7Sgilles } 376*3ef9cbf7Sgilles } 377*3ef9cbf7Sgilles 378*3ef9cbf7Sgilles batchp->tv.tv_sec = SMTPD_CONNECT_TIMEOUT; 379*3ef9cbf7Sgilles batchp->tv.tv_usec = 0; 380*3ef9cbf7Sgilles batchp->peerfd = s; 381*3ef9cbf7Sgilles event_set(&batchp->ev, s, EV_TIMEOUT|EV_WRITE, mta_write, batchp); 382*3ef9cbf7Sgilles event_add(&batchp->ev, &batchp->tv); 383*3ef9cbf7Sgilles 384*3ef9cbf7Sgilles return 1; 385*3ef9cbf7Sgilles 386*3ef9cbf7Sgilles bad: 387*3ef9cbf7Sgilles batchp->ss_off++; 388*3ef9cbf7Sgilles close(s); 389*3ef9cbf7Sgilles return 0; 390*3ef9cbf7Sgilles } 391*3ef9cbf7Sgilles 392*3ef9cbf7Sgilles void 393*3ef9cbf7Sgilles mta_write(int s, short event, void *arg) 394*3ef9cbf7Sgilles { 395*3ef9cbf7Sgilles struct batch *batchp = arg; 396*3ef9cbf7Sgilles int ret; 397*3ef9cbf7Sgilles 398*3ef9cbf7Sgilles if (event == EV_TIMEOUT) { 399*3ef9cbf7Sgilles batchp->ss_off++; 400*3ef9cbf7Sgilles close(s); 401*3ef9cbf7Sgilles if (batchp->bev) { 402*3ef9cbf7Sgilles bufferevent_free(batchp->bev); 403*3ef9cbf7Sgilles batchp->bev = NULL; 404*3ef9cbf7Sgilles } 405*3ef9cbf7Sgilles strlcpy(batchp->errorline, "connection timed-out.", STRLEN); 406*3ef9cbf7Sgilles 407*3ef9cbf7Sgilles ret = 0; 408*3ef9cbf7Sgilles while (batchp->ss_off < batchp->ss_cnt && 409*3ef9cbf7Sgilles (ret = mta_connect(batchp)) == 0) { 410*3ef9cbf7Sgilles continue; 411*3ef9cbf7Sgilles } 412*3ef9cbf7Sgilles if (ret) 413*3ef9cbf7Sgilles return; 414*3ef9cbf7Sgilles 415*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 416*3ef9cbf7Sgilles return; 417*3ef9cbf7Sgilles } 418*3ef9cbf7Sgilles 419*3ef9cbf7Sgilles batchp->bev = bufferevent_new(s, mta_read_handler, mta_write_handler, 420*3ef9cbf7Sgilles mta_error_handler, batchp); 421*3ef9cbf7Sgilles 422*3ef9cbf7Sgilles if (batchp->bev == NULL) { 423*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 424*3ef9cbf7Sgilles close(s); 425*3ef9cbf7Sgilles return; 426*3ef9cbf7Sgilles } 427*3ef9cbf7Sgilles 428*3ef9cbf7Sgilles bufferevent_enable(batchp->bev, EV_READ|EV_WRITE); 429*3ef9cbf7Sgilles } 430*3ef9cbf7Sgilles 431*3ef9cbf7Sgilles void 432*3ef9cbf7Sgilles mta_read_handler(struct bufferevent *bev, void *arg) 433*3ef9cbf7Sgilles { 434*3ef9cbf7Sgilles while (mta_reply_handler(bev, arg)) 435*3ef9cbf7Sgilles ; 436*3ef9cbf7Sgilles } 437*3ef9cbf7Sgilles 438*3ef9cbf7Sgilles int 439*3ef9cbf7Sgilles mta_reply_handler(struct bufferevent *bev, void *arg) 440*3ef9cbf7Sgilles { 441*3ef9cbf7Sgilles struct batch *batchp = arg; 442*3ef9cbf7Sgilles struct smtpd *env = batchp->env; 443*3ef9cbf7Sgilles struct message *messagep = NULL; 444*3ef9cbf7Sgilles char *line; 445*3ef9cbf7Sgilles int i; 446*3ef9cbf7Sgilles int code; 447*3ef9cbf7Sgilles #define F_ISINFO 0x1 448*3ef9cbf7Sgilles #define F_ISPROTOERROR 0x2 449*3ef9cbf7Sgilles char codebuf[4]; 450*3ef9cbf7Sgilles char *errstr; 451*3ef9cbf7Sgilles int flags = 0; 452*3ef9cbf7Sgilles 453*3ef9cbf7Sgilles line = evbuffer_readline(bev->input); 454*3ef9cbf7Sgilles if (line == NULL) { 455*3ef9cbf7Sgilles bufferevent_enable(bev, EV_READ|EV_WRITE); 456*3ef9cbf7Sgilles return 0; 457*3ef9cbf7Sgilles } 458*3ef9cbf7Sgilles 459*3ef9cbf7Sgilles line[strcspn(line, "\r")] = '\0'; 460*3ef9cbf7Sgilles 461*3ef9cbf7Sgilles bufferevent_enable(bev, EV_READ|EV_WRITE); 462*3ef9cbf7Sgilles 463*3ef9cbf7Sgilles log_debug("remote server sent: [%s]", line); 464*3ef9cbf7Sgilles 465*3ef9cbf7Sgilles strlcpy(codebuf, line, sizeof codebuf); 466*3ef9cbf7Sgilles code = strtonum(codebuf, 0, UINT16_MAX, (const char **)&errstr); 467*3ef9cbf7Sgilles if (errstr || code < 100) { 468*3ef9cbf7Sgilles /* Server sent invalid line, protocol error */ 469*3ef9cbf7Sgilles batchp->status |= S_BATCH_PERMFAILURE; 470*3ef9cbf7Sgilles strlcpy(batchp->errorline, line, STRLEN); 471*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 472*3ef9cbf7Sgilles return 0; 473*3ef9cbf7Sgilles } 474*3ef9cbf7Sgilles 475*3ef9cbf7Sgilles if (line[3] == '-') { 476*3ef9cbf7Sgilles return 1; 477*3ef9cbf7Sgilles } 478*3ef9cbf7Sgilles 479*3ef9cbf7Sgilles switch (code) { 480*3ef9cbf7Sgilles case 250: 481*3ef9cbf7Sgilles if (batchp->state == S_DONE) { 482*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 483*3ef9cbf7Sgilles return 0; 484*3ef9cbf7Sgilles } 485*3ef9cbf7Sgilles break; 486*3ef9cbf7Sgilles 487*3ef9cbf7Sgilles case 220: 488*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "EHLO %s\r\n", env->sc_hostname); 489*3ef9cbf7Sgilles batchp->state = S_GREETED; 490*3ef9cbf7Sgilles return 1; 491*3ef9cbf7Sgilles 492*3ef9cbf7Sgilles case 421: 493*3ef9cbf7Sgilles case 450: 494*3ef9cbf7Sgilles case 451: 495*3ef9cbf7Sgilles batchp->status |= S_BATCH_TEMPFAILURE; 496*3ef9cbf7Sgilles strlcpy(batchp->errorline, line, STRLEN); 497*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 498*3ef9cbf7Sgilles return 0; 499*3ef9cbf7Sgilles 500*3ef9cbf7Sgilles /* The following codes are state dependant and will cause 501*3ef9cbf7Sgilles * a batch rejection if returned at the wrong state. 502*3ef9cbf7Sgilles */ 503*3ef9cbf7Sgilles case 530: 504*3ef9cbf7Sgilles case 550: 505*3ef9cbf7Sgilles if (batchp->state == S_RCPT) { 506*3ef9cbf7Sgilles batchp->messagep->status = (S_MESSAGE_REJECTED|S_MESSAGE_PERMFAILURE); 507*3ef9cbf7Sgilles strlcpy(batchp->messagep->session_errorline, line, STRLEN); 508*3ef9cbf7Sgilles break; 509*3ef9cbf7Sgilles } 510*3ef9cbf7Sgilles case 354: 511*3ef9cbf7Sgilles if (batchp->state == S_RCPT && batchp->messagep == NULL) { 512*3ef9cbf7Sgilles batchp->state = S_DATA; 513*3ef9cbf7Sgilles break; 514*3ef9cbf7Sgilles } 515*3ef9cbf7Sgilles 516*3ef9cbf7Sgilles case 221: 517*3ef9cbf7Sgilles if (batchp->state == S_DONE) { 518*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 519*3ef9cbf7Sgilles return 0; 520*3ef9cbf7Sgilles } 521*3ef9cbf7Sgilles 522*3ef9cbf7Sgilles case 552: 523*3ef9cbf7Sgilles case 553: 524*3ef9cbf7Sgilles flags |= F_ISPROTOERROR; 525*3ef9cbf7Sgilles default: 526*3ef9cbf7Sgilles /* Server sent code we know nothing about, error */ 527*3ef9cbf7Sgilles if (!(flags & F_ISPROTOERROR)) 528*3ef9cbf7Sgilles log_debug("Ouch, SMTP session returned unhandled %d status.", code); 529*3ef9cbf7Sgilles 530*3ef9cbf7Sgilles batchp->status |= S_BATCH_PERMFAILURE; 531*3ef9cbf7Sgilles strlcpy(batchp->errorline, line, STRLEN); 532*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 533*3ef9cbf7Sgilles return 0; 534*3ef9cbf7Sgilles } 535*3ef9cbf7Sgilles 536*3ef9cbf7Sgilles 537*3ef9cbf7Sgilles switch (batchp->state) { 538*3ef9cbf7Sgilles case S_GREETED: { 539*3ef9cbf7Sgilles char *user; 540*3ef9cbf7Sgilles char *domain; 541*3ef9cbf7Sgilles 542*3ef9cbf7Sgilles if (batchp->type & T_DAEMON_BATCH) { 543*3ef9cbf7Sgilles user = "MAILER-DAEMON"; 544*3ef9cbf7Sgilles domain = env->sc_hostname; 545*3ef9cbf7Sgilles } 546*3ef9cbf7Sgilles else { 547*3ef9cbf7Sgilles messagep = TAILQ_FIRST(&batchp->messages); 548*3ef9cbf7Sgilles user = messagep->sender.user; 549*3ef9cbf7Sgilles domain = messagep->sender.domain; 550*3ef9cbf7Sgilles } 551*3ef9cbf7Sgilles 552*3ef9cbf7Sgilles if (user[0] == '\0' && domain[0] == '\0') 553*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "MAIL FROM:<>\r\n"); 554*3ef9cbf7Sgilles else 555*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "MAIL FROM:<%s@%s>\r\n", user, domain); 556*3ef9cbf7Sgilles batchp->state = S_MAIL; 557*3ef9cbf7Sgilles 558*3ef9cbf7Sgilles break; 559*3ef9cbf7Sgilles } 560*3ef9cbf7Sgilles 561*3ef9cbf7Sgilles case S_MAIL: 562*3ef9cbf7Sgilles batchp->state = S_RCPT; 563*3ef9cbf7Sgilles 564*3ef9cbf7Sgilles case S_RCPT: { 565*3ef9cbf7Sgilles char *user; 566*3ef9cbf7Sgilles char *domain; 567*3ef9cbf7Sgilles 568*3ef9cbf7Sgilles /* Is this the first RCPT ? */ 569*3ef9cbf7Sgilles if (batchp->messagep == NULL) 570*3ef9cbf7Sgilles messagep = TAILQ_FIRST(&batchp->messages); 571*3ef9cbf7Sgilles else { 572*3ef9cbf7Sgilles /* We already had a RCPT, mark is as accepted and 573*3ef9cbf7Sgilles * fetch next one from queue if we aren't dealing 574*3ef9cbf7Sgilles * with a daemon batch. 575*3ef9cbf7Sgilles */ 576*3ef9cbf7Sgilles if (batchp->type & T_DAEMON_BATCH) 577*3ef9cbf7Sgilles messagep = NULL; 578*3ef9cbf7Sgilles else { 579*3ef9cbf7Sgilles messagep = batchp->messagep; 580*3ef9cbf7Sgilles if ((messagep->status & S_MESSAGE_REJECTED) == 0) 581*3ef9cbf7Sgilles messagep->status = S_MESSAGE_ACCEPTED; 582*3ef9cbf7Sgilles messagep = TAILQ_NEXT(batchp->messagep, entry); 583*3ef9cbf7Sgilles } 584*3ef9cbf7Sgilles } 585*3ef9cbf7Sgilles batchp->messagep = messagep; 586*3ef9cbf7Sgilles 587*3ef9cbf7Sgilles if (messagep) { 588*3ef9cbf7Sgilles if (batchp->type & T_DAEMON_BATCH) { 589*3ef9cbf7Sgilles user = messagep->sender.user; 590*3ef9cbf7Sgilles domain = messagep->sender.domain; 591*3ef9cbf7Sgilles } 592*3ef9cbf7Sgilles else { 593*3ef9cbf7Sgilles user = messagep->recipient.user; 594*3ef9cbf7Sgilles domain = messagep->recipient.domain; 595*3ef9cbf7Sgilles } 596*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "RCPT TO:<%s@%s>\r\n", user, domain); 597*3ef9cbf7Sgilles } 598*3ef9cbf7Sgilles else { 599*3ef9cbf7Sgilles /* Do we have at least one accepted recipient ? */ 600*3ef9cbf7Sgilles if ((batchp->type & T_DAEMON_BATCH) == 0) { 601*3ef9cbf7Sgilles TAILQ_FOREACH(messagep, &batchp->messages, entry) { 602*3ef9cbf7Sgilles if (messagep->status & S_MESSAGE_ACCEPTED) 603*3ef9cbf7Sgilles break; 604*3ef9cbf7Sgilles } 605*3ef9cbf7Sgilles if (messagep == NULL) { 606*3ef9cbf7Sgilles batchp->status |= S_BATCH_PERMFAILURE; 607*3ef9cbf7Sgilles mta_batch_update_queue(batchp); 608*3ef9cbf7Sgilles return 0; 609*3ef9cbf7Sgilles } 610*3ef9cbf7Sgilles } 611*3ef9cbf7Sgilles 612*3ef9cbf7Sgilles bufferevent_disable(batchp->bev, EV_WRITE|EV_READ); 613*3ef9cbf7Sgilles imsg_compose(env->sc_ibufs[PROC_QUEUE], IMSG_QUEUE_MESSAGE_FD, 614*3ef9cbf7Sgilles 0, 0, -1, batchp, sizeof(*batchp)); 615*3ef9cbf7Sgilles } 616*3ef9cbf7Sgilles break; 617*3ef9cbf7Sgilles } 618*3ef9cbf7Sgilles 619*3ef9cbf7Sgilles case S_DATA: { 620*3ef9cbf7Sgilles bufferevent_enable(batchp->bev, EV_READ|EV_WRITE); 621*3ef9cbf7Sgilles 622*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 623*3ef9cbf7Sgilles "Received: from %s (%s [%s])\r\n" 624*3ef9cbf7Sgilles "\tby %s with ESMTP id %s\r\n", 625*3ef9cbf7Sgilles "localhost", "localhost", "127.0.0.1", 626*3ef9cbf7Sgilles "", batchp->message_id); 627*3ef9cbf7Sgilles 628*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "X-OpenSMTPD: experiment\r\n"); 629*3ef9cbf7Sgilles 630*3ef9cbf7Sgilles if (batchp->type & T_DAEMON_BATCH) { 631*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 632*3ef9cbf7Sgilles "Hi !\r\n\r\n" 633*3ef9cbf7Sgilles "This is the MAILER-DAEMON, please DO NOT REPLY to this e-mail it is\r\n" 634*3ef9cbf7Sgilles "just a notification to let you know that an error has occured.\r\n\r\n"); 635*3ef9cbf7Sgilles 636*3ef9cbf7Sgilles if (batchp->status & S_BATCH_PERMFAILURE) { 637*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 638*3ef9cbf7Sgilles "You ran into a PERMANENT FAILURE, which means that the e-mail can't\r\n" 639*3ef9cbf7Sgilles "be delivered to the remote host no matter how much I'll try.\r\n\r\n" 640*3ef9cbf7Sgilles "Diagnostic:\r\n" 641*3ef9cbf7Sgilles "%s\r\n\r\n", batchp->errorline); 642*3ef9cbf7Sgilles } 643*3ef9cbf7Sgilles 644*3ef9cbf7Sgilles if (batchp->status & S_BATCH_TEMPFAILURE) { 645*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 646*3ef9cbf7Sgilles "You ran into a TEMPORARY FAILURE, which means that the e-mail can't\r\n" 647*3ef9cbf7Sgilles "be delivered right now, but could be deliberable at a later time. I\r\n" 648*3ef9cbf7Sgilles "will attempt until it succeeds for the next four days, then let you\r\n" 649*3ef9cbf7Sgilles "know if it didn't work out.\r\n" 650*3ef9cbf7Sgilles "Diagnostic:\r\n" 651*3ef9cbf7Sgilles "%s\r\n\r\n", batchp->errorline); 652*3ef9cbf7Sgilles } 653*3ef9cbf7Sgilles 654*3ef9cbf7Sgilles i = 0; 655*3ef9cbf7Sgilles TAILQ_FOREACH(messagep, &batchp->messages, entry) { 656*3ef9cbf7Sgilles if (messagep->status & S_MESSAGE_TEMPFAILURE) { 657*3ef9cbf7Sgilles if (i == 0) { 658*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 659*3ef9cbf7Sgilles "The following recipients caused a temporary failure:\r\n"); 660*3ef9cbf7Sgilles ++i; 661*3ef9cbf7Sgilles } 662*3ef9cbf7Sgilles 663*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 664*3ef9cbf7Sgilles "\t<%s@%s>:\r\n%s\r\n\r\n", 665*3ef9cbf7Sgilles messagep->recipient.user, messagep->recipient.domain, messagep->session_errorline); 666*3ef9cbf7Sgilles } 667*3ef9cbf7Sgilles } 668*3ef9cbf7Sgilles 669*3ef9cbf7Sgilles i = 0; 670*3ef9cbf7Sgilles TAILQ_FOREACH(messagep, &batchp->messages, entry) { 671*3ef9cbf7Sgilles if (messagep->status & S_MESSAGE_PERMFAILURE) { 672*3ef9cbf7Sgilles if (i == 0) { 673*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 674*3ef9cbf7Sgilles "The following recipients caused a permanent failure:\r\n"); 675*3ef9cbf7Sgilles ++i; 676*3ef9cbf7Sgilles } 677*3ef9cbf7Sgilles 678*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 679*3ef9cbf7Sgilles "\t<%s@%s>:\r\n%s\r\n\r\n", 680*3ef9cbf7Sgilles messagep->recipient.user, messagep->recipient.domain, messagep->session_errorline); 681*3ef9cbf7Sgilles } 682*3ef9cbf7Sgilles } 683*3ef9cbf7Sgilles 684*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, 685*3ef9cbf7Sgilles "Below is a copy of the original message:\r\n\r\n"); 686*3ef9cbf7Sgilles } 687*3ef9cbf7Sgilles 688*3ef9cbf7Sgilles break; 689*3ef9cbf7Sgilles } 690*3ef9cbf7Sgilles case S_DONE: 691*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "QUIT\r\n"); 692*3ef9cbf7Sgilles batchp->state = S_QUIT; 693*3ef9cbf7Sgilles break; 694*3ef9cbf7Sgilles 695*3ef9cbf7Sgilles default: 696*3ef9cbf7Sgilles log_info("unknown command: %d", batchp->state); 697*3ef9cbf7Sgilles } 698*3ef9cbf7Sgilles 699*3ef9cbf7Sgilles return 1; 700*3ef9cbf7Sgilles } 701*3ef9cbf7Sgilles 702*3ef9cbf7Sgilles void 703*3ef9cbf7Sgilles mta_write_handler(struct bufferevent *bev, void *arg) 704*3ef9cbf7Sgilles { 705*3ef9cbf7Sgilles struct batch *batchp = arg; 706*3ef9cbf7Sgilles char *buf, *lbuf; 707*3ef9cbf7Sgilles size_t len; 708*3ef9cbf7Sgilles 709*3ef9cbf7Sgilles if (batchp->state == S_QUIT) { 710*3ef9cbf7Sgilles bufferevent_disable(bev, EV_READ|EV_WRITE); 711*3ef9cbf7Sgilles close(batchp->peerfd); 712*3ef9cbf7Sgilles return; 713*3ef9cbf7Sgilles } 714*3ef9cbf7Sgilles 715*3ef9cbf7Sgilles /* Progressively fill the output buffer with data */ 716*3ef9cbf7Sgilles if (batchp->state == S_DATA) { 717*3ef9cbf7Sgilles 718*3ef9cbf7Sgilles lbuf = NULL; 719*3ef9cbf7Sgilles if ((buf = fgetln(batchp->messagefp, &len))) { 720*3ef9cbf7Sgilles if (buf[len - 1] == '\n') 721*3ef9cbf7Sgilles buf[len - 1] = '\0'; 722*3ef9cbf7Sgilles else { 723*3ef9cbf7Sgilles if ((lbuf = malloc(len + 1)) == NULL) 724*3ef9cbf7Sgilles err(1, "malloc"); 725*3ef9cbf7Sgilles memcpy(lbuf, buf, len); 726*3ef9cbf7Sgilles lbuf[len] = '\0'; 727*3ef9cbf7Sgilles buf = lbuf; 728*3ef9cbf7Sgilles } 729*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, "%s\r\n", buf); 730*3ef9cbf7Sgilles free(lbuf); 731*3ef9cbf7Sgilles } 732*3ef9cbf7Sgilles else { 733*3ef9cbf7Sgilles evbuffer_add_printf(batchp->bev->output, ".\r\n"); 734*3ef9cbf7Sgilles batchp->state = S_DONE; 735*3ef9cbf7Sgilles fclose(batchp->messagefp); 736*3ef9cbf7Sgilles batchp->messagefp = NULL; 737*3ef9cbf7Sgilles } 738*3ef9cbf7Sgilles } 739*3ef9cbf7Sgilles bufferevent_enable(batchp->bev, EV_READ|EV_WRITE); 740*3ef9cbf7Sgilles } 741*3ef9cbf7Sgilles 742*3ef9cbf7Sgilles void 743*3ef9cbf7Sgilles mta_error_handler(struct bufferevent *bev, short error, void *arg) 744*3ef9cbf7Sgilles { 745*3ef9cbf7Sgilles struct batch *batchp = arg; 746*3ef9cbf7Sgilles if (error & (EVBUFFER_TIMEOUT|EVBUFFER_EOF)) { 747*3ef9cbf7Sgilles bufferevent_disable(bev, EV_READ|EV_WRITE); 748*3ef9cbf7Sgilles close(batchp->peerfd); 749*3ef9cbf7Sgilles return; 750*3ef9cbf7Sgilles } 751*3ef9cbf7Sgilles } 752*3ef9cbf7Sgilles 753*3ef9cbf7Sgilles void 754*3ef9cbf7Sgilles mta_batch_update_queue(struct batch *batchp) 755*3ef9cbf7Sgilles { 756*3ef9cbf7Sgilles struct smtpd *env = batchp->env; 757*3ef9cbf7Sgilles struct message *messagep; 758*3ef9cbf7Sgilles 759*3ef9cbf7Sgilles while ((messagep = TAILQ_FIRST(&batchp->messages)) != NULL) { 760*3ef9cbf7Sgilles 761*3ef9cbf7Sgilles if (batchp->status & S_BATCH_PERMFAILURE) { 762*3ef9cbf7Sgilles messagep->status |= S_MESSAGE_PERMFAILURE; 763*3ef9cbf7Sgilles } 764*3ef9cbf7Sgilles 765*3ef9cbf7Sgilles if (batchp->status & S_BATCH_TEMPFAILURE) { 766*3ef9cbf7Sgilles if (messagep->status != S_MESSAGE_PERMFAILURE) 767*3ef9cbf7Sgilles messagep->status |= S_MESSAGE_TEMPFAILURE; 768*3ef9cbf7Sgilles } 769*3ef9cbf7Sgilles 770*3ef9cbf7Sgilles imsg_compose(env->sc_ibufs[PROC_QUEUE], 771*3ef9cbf7Sgilles IMSG_QUEUE_MESSAGE_UPDATE, 0, 0, -1, messagep, 772*3ef9cbf7Sgilles sizeof(struct message)); 773*3ef9cbf7Sgilles TAILQ_REMOVE(&batchp->messages, messagep, entry); 774*3ef9cbf7Sgilles free(messagep); 775*3ef9cbf7Sgilles } 776*3ef9cbf7Sgilles 777*3ef9cbf7Sgilles SPLAY_REMOVE(batchtree, &env->batch_queue, batchp); 778*3ef9cbf7Sgilles 779*3ef9cbf7Sgilles if (batchp->messagefp) 780*3ef9cbf7Sgilles fclose(batchp->messagefp); 781*3ef9cbf7Sgilles 782*3ef9cbf7Sgilles if (batchp->bev) 783*3ef9cbf7Sgilles bufferevent_free(batchp->bev); 784*3ef9cbf7Sgilles 785*3ef9cbf7Sgilles free(batchp); 786*3ef9cbf7Sgilles } 787