void post_mail_fopen_async(const char *sender, const char *recipient, int filter_class, int trace_flags, VSTRING *queue_id, void (*notify) (VSTREAM *, void *), void *context) { VSTREAM *stream; POST_MAIL_STATE *state; stream = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, NON_BLOCKING); state = (POST_MAIL_STATE *) mymalloc(sizeof(*state)); state->sender = mystrdup(sender); state->recipient = mystrdup(recipient); state->filter_class = filter_class; state->trace_flags = trace_flags; state->notify = notify; state->context = context; state->stream = stream; state->queue_id = queue_id; /* * To keep interfaces as simple as possible we report all errors via the * same interface as all successes. */ if (stream != 0) { event_enable_read(vstream_fileno(stream), post_mail_open_event, (void *) state); event_request_timer(post_mail_open_event, (void *) state, var_daemon_timeout); } else { event_request_timer(post_mail_open_event, (void *) state, 0); } }
VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient, int filter_class, int trace_flags, VSTRING *queue_id) { VSTREAM *stream; if ((stream = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING)) != 0) post_mail_init(stream, sender, recipient, filter_class, trace_flags, queue_id); return (stream); }
static void show_queue(void) { const char *errstr; char buf[VSTREAM_BUFSIZE]; VSTREAM *showq; int n; uid_t uid = getuid(); if (uid != 0 && uid != var_owner_uid && (errstr = check_user_acl_byuid(VAR_SHOWQ_ACL, var_showq_acl, uid)) != 0) msg_fatal_status(EX_NOPERM, "User %s(%ld) is not allowed to view the mail queue", errstr, (long) uid); /* * Connect to the show queue service. Terminate silently when piping into * a program that terminates early. */ if ((showq = mail_connect(MAIL_CLASS_PUBLIC, var_showq_service, BLOCKING)) != 0) { while ((n = vstream_fread(showq, buf, sizeof(buf))) > 0) { if (vstream_fwrite(VSTREAM_OUT, buf, n) != n || vstream_fflush(VSTREAM_OUT) != 0) { if (errno == EPIPE) break; msg_fatal("write error: %m"); } } if (vstream_fclose(showq) && errno != EPIPE) msg_warn("close: %m"); } /* * Don't assume that the mail system is down when the user has * insufficient permission to access the showq socket. */ else if (errno == EACCES) { msg_fatal_status(EX_SOFTWARE, "Connect to the %s %s service: %m", var_mail_name, var_showq_service); } /* * When the mail system is down, the superuser can still access the queue * directly. Just run the showq program in stand-alone mode. */ else if (geteuid() == 0) { ARGV *argv; int stat; msg_warn("Mail system is down -- accessing queue directly"); argv = argv_alloc(6); argv_add(argv, var_showq_service, "-u", "-S", (char *) 0); for (n = 0; n < msg_verbose; n++) argv_add(argv, "-v", (char *) 0); argv_terminate(argv); stat = mail_run_foreground(var_daemon_dir, argv->argv); argv_free(argv); if (stat != 0) msg_fatal_status(stat < 0 ? EX_OSERR : EX_SOFTWARE, "Error running %s/%s", var_daemon_dir, argv->argv[0]); } /* * When the mail system is down, unprivileged users are stuck, because by * design the mail system contains no set_uid programs. The only way for * an unprivileged user to cross protection boundaries is to talk to the * showq daemon. */ else { msg_fatal_status(EX_UNAVAILABLE, "Queue report unavailable - mail system is down"); } }
void qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOTIFY notify) { QMGR_TRANSPORT_ALLOC *alloc; /* * Sanity checks. */ if (transport->flags & QMGR_TRANSPORT_STAT_DEAD) msg_panic("qmgr_transport: dead transport: %s", transport->name); if (transport->flags & QMGR_TRANSPORT_STAT_RATE_LOCK) msg_panic("qmgr_transport: rate-locked transport: %s", transport->name); if (transport->pending >= QMGR_TRANSPORT_MAX_PEND) msg_panic("qmgr_transport: excess allocation: %s", transport->name); /* * When this message delivery transport is rate-limited, do not select it * again before the end of a message delivery transaction. */ if (transport->xport_rate_delay > 0) transport->flags |= QMGR_TRANSPORT_STAT_RATE_LOCK; /* * Connect to the well-known port for this delivery service, and wake up * when a process announces its availability. Allow only a limited number * of delivery process allocation attempts for this transport. In case of * problems, back off. Do not hose the system when it is in trouble * already. * * Use non-blocking connect(), so that Linux won't block the queue manager * until the delivery agent calls accept(). * * When the connection to delivery agent cannot be completed, notify the * event handler so that it can throttle the transport and defer the todo * queues, just like it does when communication fails *after* connection * completion. * * Before Postfix 2.4, the event handler was not invoked after connect() * error, and mail was not deferred. Because of this, mail would be stuck * in the active queue after triggering a "connection refused" condition. */ alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc)); alloc->transport = transport; alloc->notify = notify; transport->pending += 1; if ((alloc->stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, NON_BLOCKING)) == 0) { msg_warn("connect to transport %s/%s: %m", MAIL_CLASS_PRIVATE, transport->name); event_request_timer(qmgr_transport_event, (void *) alloc, 0); return; } #if (EVENTS_STYLE != EVENTS_STYLE_SELECT) && defined(CA_VSTREAM_CTL_DUPFD) #ifndef THRESHOLD_FD_WORKAROUND #define THRESHOLD_FD_WORKAROUND 128 #endif vstream_control(alloc->stream, CA_VSTREAM_CTL_DUPFD(THRESHOLD_FD_WORKAROUND), CA_VSTREAM_CTL_END); #endif event_enable_read(vstream_fileno(alloc->stream), qmgr_transport_event, (void *) alloc); /* * Guard against broken systems. */ event_request_timer(qmgr_transport_abort, (void *) alloc, var_daemon_timeout); }
static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender) { VSTRING *buffer = vstring_alloc(100); FORWARD_INFO *info; VSTREAM *cleanup; #define FORWARD_OPEN_RETURN(res) do { \ vstring_free(buffer); \ return (res); \ } while (0) /* * Contact the cleanup service and save the new mail queue id. Request * that the cleanup service bounces bad messages to the sender so that we * can avoid the trouble of bounce management. * * In case you wonder what kind of bounces, examples are "too many hops", * "message too large", perhaps some others. The reason not to bounce * ourselves is that we don't really know who the recipients are. */ cleanup = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, BLOCKING); if (cleanup == 0) { msg_warn("connect to %s/%s: %m", MAIL_CLASS_PUBLIC, var_cleanup_service); FORWARD_OPEN_RETURN(0); } close_on_exec(vstream_fileno(cleanup), CLOSE_ON_EXEC); if (attr_scan(cleanup, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_QUEUEID, buffer), ATTR_TYPE_END) != 1) { vstream_fclose(cleanup); FORWARD_OPEN_RETURN(0); } info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO)); info->cleanup = cleanup; info->queue_id = mystrdup(STR(buffer)); GETTIMEOFDAY(&info->posting_time); #define FORWARD_CLEANUP_FLAGS \ (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL \ | smtputf8_autodetect(MAIL_SRC_MASK_FORWARD) \ | ((request->smtputf8 & SMTPUTF8_FLAG_REQUESTED) ? \ CLEANUP_FLAG_SMTPUTF8 : 0)) attr_print(cleanup, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_FLAGS, FORWARD_CLEANUP_FLAGS), ATTR_TYPE_END); /* * Send initial message envelope information. For bounces, set the * designated sender: mailing list owner, posting user, whatever. */ rec_fprintf(cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(info->posting_time)); rec_fputs(cleanup, REC_TYPE_FROM, sender); /* * Don't send the original envelope ID or full/headers return mask if it * was reset due to mailing list expansion. */ if (request->dsn_ret) rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_DSN_RET, request->dsn_ret); if (request->dsn_envid && *(request->dsn_envid)) rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_DSN_ENVID, request->dsn_envid); /* * Zero-length attribute values are place holders for unavailable * attribute values. See qmgr_message.c. They are not meant to be * propagated to queue files. */ #define PASS_ATTR(fp, name, value) do { \ if ((value) && *(value)) \ rec_fprintf((fp), REC_TYPE_ATTR, "%s=%s", (name), (value)); \ } while (0) /* * XXX encapsulate these as one object. */ PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_NAME, request->client_name); PASS_ATTR(cleanup, MAIL_ATTR_LOG_CLIENT_ADDR, request->client_addr); PASS_ATTR(cleanup, MAIL_ATTR_LOG_PROTO_NAME, request->client_proto); PASS_ATTR(cleanup, MAIL_ATTR_LOG_HELO_NAME, request->client_helo); PASS_ATTR(cleanup, MAIL_ATTR_SASL_METHOD, request->sasl_method); PASS_ATTR(cleanup, MAIL_ATTR_SASL_USERNAME, request->sasl_username); PASS_ATTR(cleanup, MAIL_ATTR_SASL_SENDER, request->sasl_sender); PASS_ATTR(cleanup, MAIL_ATTR_LOG_IDENT, request->log_ident); PASS_ATTR(cleanup, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context); FORWARD_OPEN_RETURN(info); }