static void dns_dispatch_host(struct asr_result *ar, void *arg) { struct dns_session *s; struct dns_lookup *lookup = arg; struct addrinfo *ai; s = lookup->session; for (ai = ar->ar_addrinfo; ai; ai = ai->ai_next) { s->mxfound++; m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_sockaddr(s->p, ai->ai_addr); m_add_int(s->p, lookup->preference); m_close(s->p); } free(lookup); if (ar->ar_addrinfo) asr_freeaddrinfo(ar->ar_addrinfo); if (ar->ar_gai_errno) s->error = ar->ar_gai_errno; if (--s->refcount) return; m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, s->mxfound ? DNS_OK : DNS_ENOTFOUND); m_close(s->p); free(s); }
static void filter_send_response(struct filter_session *s) { log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" %s filter_send_response() -> %d, %d, %s", filter_name, s->id, query_to_str(s->qtype), s->response.status, s->response.code, s->response.line); tree_xpop(&queries, s->qid); m_create(&fi.p, IMSG_FILTER_RESPONSE, 0, 0, -1); m_add_id(&fi.p, s->qid); m_add_int(&fi.p, s->qtype); if (s->qtype == QUERY_EOM) m_add_u32(&fi.p, s->datalen); m_add_int(&fi.p, s->response.status); m_add_int(&fi.p, s->response.code); if (s->response.line) { m_add_string(&fi.p, s->response.line); free(s->response.line); s->response.line = NULL; } m_close(&fi.p); s->qid = 0; s->response.ready = 0; }
static void dns_dispatch_mx_preference(struct asr_result *ar, void *arg) { struct dns_session *s = arg; struct unpack pack; struct dns_header h; struct dns_query q; struct dns_rr rr; char buf[512]; int error; if (ar->ar_h_errno) { if (ar->ar_rcode == NXDOMAIN) error = DNS_ENONAME; else if (ar->ar_h_errno == NO_RECOVERY || ar->ar_h_errno == NO_DATA) error = DNS_EINVAL; else error = DNS_RETRY; } else { error = DNS_ENOTFOUND; unpack_init(&pack, ar->ar_data, ar->ar_datalen); if (unpack_header(&pack, &h) != -1 && unpack_query(&pack, &q) != -1) { for (; h.ancount; h.ancount--) { if (unpack_rr(&pack, &rr) == -1) break; if (rr.rr_type != T_MX) continue; print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); buf[strlen(buf) - 1] = '\0'; if (!strcasecmp(s->name, buf)) { error = DNS_OK; break; } } } } free(ar->ar_data); m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, error); if (error == DNS_OK) m_add_int(s->p, rr.rr.mx.preference); m_close(s->p); free(s); }
static void dns_dispatch_mx(struct asr_result *ar, void *arg) { struct dns_session *s = arg; struct unpack pack; struct dns_header h; struct dns_query q; struct dns_rr rr; char buf[512]; size_t found; if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) { m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); m_add_id(s->p, s->reqid); if (ar->ar_rcode == NXDOMAIN) m_add_int(s->p, DNS_ENONAME); else if (ar->ar_h_errno == NO_RECOVERY) m_add_int(s->p, DNS_EINVAL); else m_add_int(s->p, DNS_RETRY); m_close(s->p); free(s); free(ar->ar_data); return; } found = 0; unpack_init(&pack, ar->ar_data, ar->ar_datalen); if (unpack_header(&pack, &h) == -1 || unpack_query(&pack, &q) == -1) return; for (; h.ancount; h.ancount--) { if (unpack_rr(&pack, &rr) == -1) break; if (rr.rr_type != T_MX) continue; print_dname(rr.rr.mx.exchange, buf, sizeof(buf)); buf[strlen(buf) - 1] = '\0'; dns_lookup_host(s, buf, rr.rr.mx.preference); found++; } free(ar->ar_data); /* fallback to host if no MX is found. */ if (found == 0) dns_lookup_host(s, s->name, 0); }
static void filter_response(uint64_t qid, int status, int code, const char *line, int notify) { struct filter_query *q; q = tree_xpop(&queries, qid); free(q); m_create(&fi.p, IMSG_FILTER_RESPONSE, 0, 0, -1); m_add_id(&fi.p, qid); m_add_int(&fi.p, status); m_add_int(&fi.p, code); m_add_int(&fi.p, notify); if (line) m_add_string(&fi.p, line); m_close(&fi.p); }
void queue_permfail(uint64_t evpid, const char *reason, enum enhanced_status_code code) { m_create(p_queue, IMSG_DELIVERY_PERMFAIL, 0, 0, -1); m_add_evpid(p_queue, evpid); m_add_string(p_queue, reason); m_add_int(p_queue, (int)code); m_close(p_queue); }
static void control_broadcast_verbose(int msg, int v) { m_create(p_lka, msg, 0, 0, -1); m_add_int(p_lka, v); m_close(p_lka); m_create(p_pony, msg, 0, 0, -1); m_add_int(p_pony, v); m_close(p_pony); m_create(p_queue, msg, 0, 0, -1); m_add_int(p_queue, v); m_close(p_queue); m_create(p_ca, msg, 0, 0, -1); m_add_int(p_ca, v); m_close(p_ca); m_create(p_scheduler, msg, 0, 0, -1); m_add_int(p_scheduler, v); m_close(p_scheduler); m_create(p_parent, msg, 0, 0, -1); m_add_int(p_parent, v); m_close(p_parent); }
static void dns_dispatch_ptr(struct asr_result *ar, void *arg) { struct dns_session *s = arg; /* The error code could be more precise, but we don't currently care */ m_create(s->p, s->type, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, ar->ar_gai_errno ? DNS_ENOTFOUND : DNS_OK); if (ar->ar_gai_errno == 0) m_add_string(s->p, s->name); m_close(s->p); free(s); }
static void queue_imsg(struct mproc *p, struct imsg *imsg) { struct delivery_bounce bounce; struct bounce_req_msg *req_bounce; struct envelope evp; struct msg m; const char *reason; uint64_t reqid, evpid, holdq; uint32_t msgid; time_t nexttry; int fd, mta_ext, ret, v, flags, code; memset(&bounce, 0, sizeof(struct delivery_bounce)); if (p->proc == PROC_PONY) { switch (imsg->hdr.type) { case IMSG_SMTP_MESSAGE_CREATE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_end(&m); ret = queue_message_create(&msgid); m_create(p, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1); m_add_id(p, reqid); if (ret == 0) m_add_int(p, 0); else { m_add_int(p, 1); m_add_msgid(p, msgid); } m_close(p); return; case IMSG_SMTP_MESSAGE_ROLLBACK: m_msg(&m, imsg); m_get_msgid(&m, &msgid); m_end(&m); queue_message_delete(msgid); m_create(p_scheduler, IMSG_QUEUE_MESSAGE_ROLLBACK, 0, 0, -1); m_add_msgid(p_scheduler, msgid); m_close(p_scheduler); return; case IMSG_SMTP_MESSAGE_COMMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); ret = queue_message_commit(msgid); m_create(p, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, (ret == 0) ? 0 : 1); m_close(p); if (ret) { m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1); m_add_msgid(p_scheduler, msgid); m_close(p_scheduler); } return; case IMSG_SMTP_MESSAGE_OPEN: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); fd = queue_message_fd_rw(msgid); m_create(p, IMSG_SMTP_MESSAGE_OPEN, 0, 0, fd); m_add_id(p, reqid); m_add_int(p, (fd == -1) ? 0 : 1); m_close(p); return; case IMSG_QUEUE_SMTP_SESSION: bounce_fd(imsg->fd); return; } } if (p->proc == PROC_LKA) { switch (imsg->hdr.type) { case IMSG_LKA_ENVELOPE_SUBMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_envelope(&m, &evp); m_end(&m); if (evp.id == 0) log_warnx("warn: imsg_queue_submit_envelope: evpid=0"); if (evpid_to_msgid(evp.id) == 0) log_warnx("warn: imsg_queue_submit_envelope: msgid=0, " "evpid=%016"PRIx64, evp.id); ret = queue_envelope_create(&evp); m_create(p_pony, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); m_add_id(p_pony, reqid); if (ret == 0) m_add_int(p_pony, 0); else { m_add_int(p_pony, 1); m_add_evpid(p_pony, evp.id); } m_close(p_pony); if (ret) { m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_SUBMIT, 0, 0, -1); m_add_envelope(p_scheduler, &evp); m_close(p_scheduler); } return; case IMSG_LKA_ENVELOPE_COMMIT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_end(&m); m_create(p_pony, IMSG_QUEUE_ENVELOPE_COMMIT, 0, 0, -1); m_add_id(p_pony, reqid); m_add_int(p_pony, 1); m_close(p_pony); return; } } if (p->proc == PROC_SCHEDULER) { switch (imsg->hdr.type) { case IMSG_SCHED_ENVELOPE_REMOVE: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); /* already removed by scheduler */ if (queue_envelope_load(evpid, &evp) == 0) return; queue_log(&evp, "Remove", "Removed by administrator"); queue_envelope_delete(evpid); return; case IMSG_SCHED_ENVELOPE_EXPIRE: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); /* already removed by scheduler*/ if (queue_envelope_load(evpid, &evp) == 0) return; bounce.type = B_ERROR; envelope_set_errormsg(&evp, "Envelope expired"); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, ESC_DELIVERY_TIME_EXPIRED); queue_bounce(&evp, &bounce); queue_log(&evp, "Expire", "Envelope expired"); queue_envelope_delete(evpid); return; case IMSG_SCHED_ENVELOPE_BOUNCE: req_bounce = imsg->data; evpid = req_bounce->evpid; if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: bounce: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 0); /* not in-flight */ m_close(p_scheduler); return; } queue_bounce(&evp, &req_bounce->bounce); evp.lastbounce = req_bounce->timestamp; if (!queue_envelope_update(&evp)) log_warnx("warn: could not update envelope %016"PRIx64, evpid); return; case IMSG_SCHED_ENVELOPE_DELIVER: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: deliver: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } evp.lasttry = time(NULL); m_create(p_pony, IMSG_QUEUE_DELIVER, 0, 0, -1); m_add_envelope(p_pony, &evp); m_close(p_pony); return; case IMSG_SCHED_ENVELOPE_INJECT: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); bounce_add(evpid); return; case IMSG_SCHED_ENVELOPE_TRANSFER: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } evp.lasttry = time(NULL); m_create(p_pony, IMSG_QUEUE_TRANSFER, 0, 0, -1); m_add_envelope(p_pony, &evp); m_close(p_pony); return; case IMSG_CTL_LIST_ENVELOPES: if (imsg->hdr.len == sizeof imsg->hdr) { m_forward(p_control, imsg); return; } m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_int(&m, &flags); m_get_time(&m, &nexttry); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) return; /* Envelope is gone, drop it */ /* * XXX consistency: The envelope might already be on * its way back to the scheduler. We need to detect * this properly and report that state. */ evp.flags |= flags; /* In the past if running or runnable */ evp.nexttry = nexttry; if (flags & EF_INFLIGHT) { /* * Not exactly correct but pretty close: The * value is not recorded on the envelope unless * a tempfail occurs. */ evp.lasttry = nexttry; } m_compose(p_control, IMSG_CTL_LIST_ENVELOPES, imsg->hdr.peerid, 0, -1, &evp, sizeof evp); return; } } if (p->proc == PROC_PONY) { switch (imsg->hdr.type) { case IMSG_MDA_OPEN_MESSAGE: case IMSG_MTA_OPEN_MESSAGE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_msgid(&m, &msgid); m_end(&m); fd = queue_message_fd_r(msgid); m_create(p, imsg->hdr.type, 0, 0, fd); m_add_id(p, reqid); m_close(p); return; case IMSG_MDA_DELIVERY_OK: case IMSG_MTA_DELIVERY_OK: m_msg(&m, imsg); m_get_evpid(&m, &evpid); if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK) m_get_int(&m, &mta_ext); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warn("queue: dsn: failed to load envelope"); return; } if (evp.dsn_notify & DSN_SUCCESS) { bounce.type = B_DSN; bounce.dsn_ret = evp.dsn_ret; if (imsg->hdr.type == IMSG_MDA_DELIVERY_OK) queue_bounce(&evp, &bounce); else if (imsg->hdr.type == IMSG_MTA_DELIVERY_OK && (mta_ext & MTA_EXT_DSN) == 0) { bounce.mta_without_dsn = 1; queue_bounce(&evp, &bounce); } } queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_OK, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_TEMPFAIL: case IMSG_MTA_DELIVERY_TEMPFAIL: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: tempfail: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } envelope_set_errormsg(&evp, "%s", reason); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, code); evp.retry++; if (!queue_envelope_update(&evp)) log_warnx("warn: could not update envelope %016"PRIx64, evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_TEMPFAIL, 0, 0, -1); m_add_envelope(p_scheduler, &evp); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_PERMFAIL: case IMSG_MTA_DELIVERY_PERMFAIL: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_get_string(&m, &reason); m_get_int(&m, &code); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: permfail: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } bounce.type = B_ERROR; envelope_set_errormsg(&evp, "%s", reason); envelope_set_esc_class(&evp, ESC_STATUS_PERMFAIL); envelope_set_esc_code(&evp, code); queue_bounce(&evp, &bounce); queue_envelope_delete(evpid); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_PERMFAIL, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_close(p_scheduler); return; case IMSG_MDA_DELIVERY_LOOP: case IMSG_MTA_DELIVERY_LOOP: m_msg(&m, imsg); m_get_evpid(&m, &evpid); m_end(&m); if (queue_envelope_load(evpid, &evp) == 0) { log_warnx("queue: loop: failed to load envelope"); m_create(p_scheduler, IMSG_QUEUE_ENVELOPE_REMOVE, 0, 0, -1); m_add_evpid(p_scheduler, evpid); m_add_u32(p_scheduler, 1); /* in-flight */ m_close(p_scheduler); return; } envelope_set_errormsg(&evp, "%s", "Loop detected"); envelope_set_esc_class(&evp, ESC_STATUS_TEMPFAIL); envelope_set_esc_code(&evp, ESC_ROUTING_LOOP_DETECTED); bounce.type = B_ERROR; queue_bounce(&evp, &bounce); queue_envelope_delete(evp.id); m_create(p_scheduler, IMSG_QUEUE_DELIVERY_LOOP, 0, 0, -1); m_add_evpid(p_scheduler, evp.id); m_close(p_scheduler); return; case IMSG_MTA_DELIVERY_HOLD: case IMSG_MDA_DELIVERY_HOLD: imsg->hdr.type = IMSG_QUEUE_HOLDQ_HOLD; m_forward(p_scheduler, imsg); return; case IMSG_MTA_SCHEDULE: imsg->hdr.type = IMSG_QUEUE_ENVELOPE_SCHEDULE; m_forward(p_scheduler, imsg); return; case IMSG_MTA_HOLDQ_RELEASE: case IMSG_MDA_HOLDQ_RELEASE: m_msg(&m, imsg); m_get_id(&m, &holdq); m_get_int(&m, &v); m_end(&m); m_create(p_scheduler, IMSG_QUEUE_HOLDQ_RELEASE, 0, 0, -1); if (imsg->hdr.type == IMSG_MTA_HOLDQ_RELEASE) m_add_int(p_scheduler, D_MTA); else m_add_int(p_scheduler, D_MDA); m_add_id(p_scheduler, holdq); m_add_int(p_scheduler, v); m_close(p_scheduler); return; } } if (p->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_CTL_PAUSE_MDA: case IMSG_CTL_PAUSE_MTA: case IMSG_CTL_RESUME_MDA: case IMSG_CTL_RESUME_MTA: m_forward(p_scheduler, imsg); return; } } if (p->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CTL_VERBOSE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); log_verbose(v); m_forward(p_scheduler, imsg); return; case IMSG_CTL_PROFILE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); profiling = v; return; } } errx(1, "queue_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }
static void filter_dispatch(struct mproc *p, struct imsg *imsg) { struct filter_connect q_connect; struct mailaddr maddr; struct msg m; const char *line; uint32_t v; uint64_t id, qid; int status, event, hook; log_debug("debug: %s: imsg %i", filter_name, imsg->hdr.type); switch (imsg->hdr.type) { case IMSG_FILTER_REGISTER: m_msg(&m, imsg); m_get_u32(&m, &v); m_end(&m); if (v != FILTER_API_VERSION) errx(1, "API version mismatch"); m_create(p, IMSG_FILTER_REGISTER, 0, 0, -1); m_add_int(p, fi.hooks); m_add_int(p, fi.flags); m_close(p); break; case IMSG_FILTER_EVENT: m_msg(&m, imsg); m_get_id(&m, &id); m_get_int(&m, &event); m_end(&m); filter_dispatch_event(id, event); break; case IMSG_FILTER_QUERY: m_msg(&m, imsg); m_get_id(&m, &id); m_get_id(&m, &qid); m_get_int(&m, &hook); tree_xset(&queries, qid, NULL); switch(hook) { case HOOK_CONNECT: m_get_sockaddr(&m, (struct sockaddr*)&q_connect.local); m_get_sockaddr(&m, (struct sockaddr*)&q_connect.remote); m_get_string(&m, &q_connect.hostname); m_end(&m); filter_dispatch_connect(id, qid, &q_connect); break; case HOOK_HELO: m_get_string(&m, &line); m_end(&m); filter_dispatch_helo(id, qid, line); break; case HOOK_MAIL: m_get_mailaddr(&m, &maddr); m_end(&m); filter_dispatch_mail(id, qid, &maddr); break; case HOOK_RCPT: m_get_mailaddr(&m, &maddr); m_end(&m); filter_dispatch_rcpt(id, qid, &maddr); break; case HOOK_DATA: m_end(&m); filter_dispatch_data(id, qid); break; case HOOK_EOM: m_end(&m); filter_dispatch_eom(id, qid); break; default: errx(1, "bad query hook: %d", hook); } break; case IMSG_FILTER_NOTIFY: m_msg(&m, imsg); m_get_id(&m, &qid); m_get_int(&m, &status); m_end(&m); filter_dispatch_notify(qid, status); break; case IMSG_FILTER_DATA: m_msg(&m, imsg); m_get_id(&m, &id); m_get_string(&m, &line); m_end(&m); filter_dispatch_dataline(id, line); break; } }
static void lka_imsg(struct mproc *p, struct imsg *imsg) { struct rule *rule; struct table *table; void *tmp; int ret; const char *key, *val; struct ssl *ssl; struct iovec iov[3]; static struct dict *ssl_dict; static struct dict *tables_dict; static struct table *table_last; static struct ca_vrfy_req_msg *req_ca_vrfy_smtp = NULL; static struct ca_vrfy_req_msg *req_ca_vrfy_mta = NULL; struct ca_vrfy_req_msg *req_ca_vrfy_chain; struct ca_vrfy_resp_msg resp_ca_vrfy; struct ca_cert_req_msg *req_ca_cert; struct ca_cert_resp_msg resp_ca_cert; struct sockaddr_storage ss; struct userinfo userinfo; struct addrname addrname; struct envelope evp; struct msg m; union lookup lk; char buf[SMTPD_MAXLINESIZE]; const char *tablename, *username, *password, *label; uint64_t reqid; size_t i; int v; if (imsg->hdr.type == IMSG_DNS_HOST || imsg->hdr.type == IMSG_DNS_PTR || imsg->hdr.type == IMSG_DNS_MX || imsg->hdr.type == IMSG_DNS_MX_PREFERENCE) { dns_imsg(p, imsg); return; } if (p->proc == PROC_SMTP) { switch (imsg->hdr.type) { case IMSG_LKA_EXPAND_RCPT: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_envelope(&m, &evp); m_end(&m); lka_session(reqid, &evp); return; case IMSG_LKA_SSL_INIT: req_ca_cert = imsg->data; resp_ca_cert.reqid = req_ca_cert->reqid; ssl = dict_get(env->sc_ssl_dict, req_ca_cert->name); if (ssl == NULL) { resp_ca_cert.status = CA_FAIL; m_compose(p, IMSG_LKA_SSL_INIT, 0, 0, -1, &resp_ca_cert, sizeof(resp_ca_cert)); return; } resp_ca_cert.status = CA_OK; resp_ca_cert.cert_len = ssl->ssl_cert_len; resp_ca_cert.key_len = ssl->ssl_key_len; iov[0].iov_base = &resp_ca_cert; iov[0].iov_len = sizeof(resp_ca_cert); iov[1].iov_base = ssl->ssl_cert; iov[1].iov_len = ssl->ssl_cert_len; iov[2].iov_base = ssl->ssl_key; iov[2].iov_len = ssl->ssl_key_len; m_composev(p, IMSG_LKA_SSL_INIT, 0, 0, -1, iov, nitems(iov)); return; case IMSG_LKA_SSL_VERIFY_CERT: req_ca_vrfy_smtp = xmemdup(imsg->data, sizeof *req_ca_vrfy_smtp, "lka:ca_vrfy"); if (req_ca_vrfy_smtp == NULL) fatal(NULL); req_ca_vrfy_smtp->cert = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_smtp, req_ca_vrfy_smtp->cert_len, "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert = xcalloc(req_ca_vrfy_smtp->n_chain, sizeof (unsigned char *), "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert_len = xcalloc(req_ca_vrfy_smtp->n_chain, sizeof (off_t), "lka:ca_vrfy"); return; case IMSG_LKA_SSL_VERIFY_CHAIN: if (req_ca_vrfy_smtp == NULL) fatalx("lka:ca_vrfy: chain without a certificate"); req_ca_vrfy_chain = imsg->data; req_ca_vrfy_smtp->chain_cert[req_ca_vrfy_smtp->chain_offset] = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy"); req_ca_vrfy_smtp->chain_cert_len[req_ca_vrfy_smtp->chain_offset] = req_ca_vrfy_chain->cert_len; req_ca_vrfy_smtp->chain_offset++; return; case IMSG_LKA_SSL_VERIFY: if (req_ca_vrfy_smtp == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); resp_ca_vrfy.reqid = req_ca_vrfy_smtp->reqid; if (! lka_X509_verify(req_ca_vrfy_smtp, CA_FILE, NULL)) resp_ca_vrfy.status = CA_FAIL; else resp_ca_vrfy.status = CA_OK; m_compose(p, IMSG_LKA_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy, sizeof resp_ca_vrfy); for (i = 0; i < req_ca_vrfy_smtp->n_chain; ++i) free(req_ca_vrfy_smtp->chain_cert[i]); free(req_ca_vrfy_smtp->chain_cert); free(req_ca_vrfy_smtp->chain_cert_len); free(req_ca_vrfy_smtp->cert); free(req_ca_vrfy_smtp); return; case IMSG_LKA_AUTHENTICATE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_string(&m, &username); m_get_string(&m, &password); m_end(&m); if (!tablename[0]) { m_create(p_parent, IMSG_LKA_AUTHENTICATE, 0, 0, -1); m_add_id(p_parent, reqid); m_add_string(p_parent, username); m_add_string(p_parent, password); m_close(p_parent); return; } ret = lka_authenticate(tablename, username, password); m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, ret); m_close(p); return; } } if (p->proc == PROC_MDA) { switch (imsg->hdr.type) { case IMSG_LKA_USERINFO: m_msg(&m, imsg); m_get_string(&m, &tablename); m_get_string(&m, &username); m_end(&m); ret = lka_userinfo(tablename, username, &userinfo); m_create(p, IMSG_LKA_USERINFO, 0, 0, -1); m_add_string(p, tablename); m_add_string(p, username); m_add_int(p, ret); if (ret == LKA_OK) m_add_data(p, &userinfo, sizeof(userinfo)); m_close(p); return; } } if (p->proc == PROC_MTA) { switch (imsg->hdr.type) { case IMSG_LKA_SSL_INIT: req_ca_cert = imsg->data; resp_ca_cert.reqid = req_ca_cert->reqid; ssl = dict_get(env->sc_ssl_dict, req_ca_cert->name); if (ssl == NULL) { resp_ca_cert.status = CA_FAIL; m_compose(p, IMSG_LKA_SSL_INIT, 0, 0, -1, &resp_ca_cert, sizeof(resp_ca_cert)); return; } resp_ca_cert.status = CA_OK; resp_ca_cert.cert_len = ssl->ssl_cert_len; resp_ca_cert.key_len = ssl->ssl_key_len; iov[0].iov_base = &resp_ca_cert; iov[0].iov_len = sizeof(resp_ca_cert); iov[1].iov_base = ssl->ssl_cert; iov[1].iov_len = ssl->ssl_cert_len; iov[2].iov_base = ssl->ssl_key; iov[2].iov_len = ssl->ssl_key_len; m_composev(p, IMSG_LKA_SSL_INIT, 0, 0, -1, iov, nitems(iov)); return; case IMSG_LKA_SSL_VERIFY_CERT: req_ca_vrfy_mta = xmemdup(imsg->data, sizeof *req_ca_vrfy_mta, "lka:ca_vrfy"); if (req_ca_vrfy_mta == NULL) fatal(NULL); req_ca_vrfy_mta->cert = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_mta, req_ca_vrfy_mta->cert_len, "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert = xcalloc(req_ca_vrfy_mta->n_chain, sizeof (unsigned char *), "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert_len = xcalloc(req_ca_vrfy_mta->n_chain, sizeof (off_t), "lka:ca_vrfy"); return; case IMSG_LKA_SSL_VERIFY_CHAIN: if (req_ca_vrfy_mta == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); req_ca_vrfy_chain = imsg->data; req_ca_vrfy_mta->chain_cert[req_ca_vrfy_mta->chain_offset] = xmemdup((char *)imsg->data + sizeof *req_ca_vrfy_chain, req_ca_vrfy_chain->cert_len, "lka:ca_vrfy"); req_ca_vrfy_mta->chain_cert_len[req_ca_vrfy_mta->chain_offset] = req_ca_vrfy_chain->cert_len; req_ca_vrfy_mta->chain_offset++; return; case IMSG_LKA_SSL_VERIFY: if (req_ca_vrfy_mta == NULL) fatalx("lka:ca_vrfy: verify without a certificate"); resp_ca_vrfy.reqid = req_ca_vrfy_mta->reqid; if (! lka_X509_verify(req_ca_vrfy_mta, CA_FILE, NULL)) resp_ca_vrfy.status = CA_FAIL; else resp_ca_vrfy.status = CA_OK; m_compose(p, IMSG_LKA_SSL_VERIFY, 0, 0, -1, &resp_ca_vrfy, sizeof resp_ca_vrfy); for (i = 0; i < req_ca_vrfy_mta->n_chain; ++i) free(req_ca_vrfy_mta->chain_cert[i]); free(req_ca_vrfy_mta->chain_cert); free(req_ca_vrfy_mta->chain_cert_len); free(req_ca_vrfy_mta->cert); free(req_ca_vrfy_mta); return; case IMSG_LKA_SECRET: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_string(&m, &label); m_end(&m); lka_credentials(tablename, label, buf, sizeof(buf)); m_create(p, IMSG_LKA_SECRET, 0, 0, -1); m_add_id(p, reqid); m_add_string(p, buf); m_close(p); return; case IMSG_LKA_SOURCE: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); table = table_find(tablename, NULL); m_create(p, IMSG_LKA_SOURCE, 0, 0, -1); m_add_id(p, reqid); if (table == NULL) { log_warn("warn: source address table %s missing", tablename); m_add_int(p, LKA_TEMPFAIL); } else { ret = table_fetch(table, K_SOURCE, &lk); if (ret == -1) m_add_int(p, LKA_TEMPFAIL); else if (ret == 0) m_add_int(p, LKA_PERMFAIL); else { m_add_int(p, LKA_OK); m_add_sockaddr(p, (struct sockaddr *)&lk.source.addr); } } m_close(p); return; case IMSG_LKA_HELO: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &tablename); m_get_sockaddr(&m, (struct sockaddr *)&ss); m_end(&m); ret = lka_addrname(tablename, (struct sockaddr*)&ss, &addrname); m_create(p, IMSG_LKA_HELO, 0, 0, -1); m_add_id(p, reqid); m_add_int(p, ret); if (ret == LKA_OK) m_add_string(p, addrname.name); m_close(p); return; } } if (p->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CONF_START: env->sc_rules_reload = xcalloc(1, sizeof *env->sc_rules, "lka:sc_rules_reload"); tables_dict = xcalloc(1, sizeof *tables_dict, "lka:tables_dict"); ssl_dict = calloc(1, sizeof *ssl_dict); if (ssl_dict == NULL) fatal(NULL); dict_init(ssl_dict); dict_init(tables_dict); TAILQ_INIT(env->sc_rules_reload); return; case IMSG_CONF_SSL: ssl = calloc(1, sizeof *ssl); if (ssl == NULL) fatal(NULL); *ssl = *(struct ssl *)imsg->data; ssl->ssl_cert = xstrdup((char *)imsg->data + sizeof *ssl, "smtp:ssl_cert"); ssl->ssl_key = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len, "smtp:ssl_key"); if (ssl->ssl_dhparams_len) { ssl->ssl_dhparams = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len + ssl->ssl_key_len, "smtp:ssl_dhparams"); } if (ssl->ssl_ca_len) { ssl->ssl_ca = xstrdup((char *)imsg->data + sizeof *ssl + ssl->ssl_cert_len + ssl->ssl_key_len + ssl->ssl_dhparams_len, "smtp:ssl_ca"); } dict_set(ssl_dict, ssl->ssl_name, ssl); return; case IMSG_CONF_RULE: rule = xmemdup(imsg->data, sizeof *rule, "lka:rule"); TAILQ_INSERT_TAIL(env->sc_rules_reload, rule, r_entry); return; case IMSG_CONF_TABLE: table_last = table = xmemdup(imsg->data, sizeof *table, "lka:table"); dict_init(&table->t_dict); dict_set(tables_dict, table->t_name, table); return; case IMSG_CONF_RULE_SOURCE: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_sources = table_find(imsg->data, NULL); if (rule->r_sources == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_SENDER: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_senders = table_find(imsg->data, NULL); if (rule->r_senders == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_DESTINATION: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_destination = table_find(imsg->data, NULL); if (rule->r_destination == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_MAPPING: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_mapping = table_find(imsg->data, NULL); if (rule->r_mapping == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_RULE_USERS: rule = TAILQ_LAST(env->sc_rules_reload, rulelist); tmp = env->sc_tables_dict; env->sc_tables_dict = tables_dict; rule->r_userbase = table_find(imsg->data, NULL); if (rule->r_userbase == NULL) fatalx("lka: tables inconsistency"); env->sc_tables_dict = tmp; return; case IMSG_CONF_TABLE_CONTENT: table = table_last; if (table == NULL) fatalx("lka: tables inconsistency"); key = imsg->data; if (table->t_type == T_HASH) val = key + strlen(key) + 1; else val = NULL; dict_set(&table->t_dict, key, val ? xstrdup(val, "lka:dict_set") : NULL); return; case IMSG_CONF_END: if (env->sc_rules) purge_config(PURGE_RULES); if (env->sc_tables_dict) { table_close_all(); purge_config(PURGE_TABLES); } env->sc_rules = env->sc_rules_reload; env->sc_ssl_dict = ssl_dict; env->sc_tables_dict = tables_dict; if (verbose & TRACE_TABLES) table_dump_all(); table_open_all(); ssl_dict = NULL; table_last = NULL; tables_dict = NULL; /* Start fulfilling requests */ mproc_enable(p_mda); mproc_enable(p_mta); mproc_enable(p_smtp); return; case IMSG_CTL_VERBOSE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); log_verbose(v); return; case IMSG_CTL_PROFILE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); profiling = v; return; case IMSG_PARENT_FORWARD_OPEN: lka_session_forward_reply(imsg->data, imsg->fd); return; case IMSG_LKA_AUTHENTICATE: m_forward(p_smtp, imsg); return; } } if (p->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_LKA_UPDATE_TABLE: table = table_find(imsg->data, NULL); if (table == NULL) { log_warnx("warn: Lookup table not found: " "\"%s\"", (char *)imsg->data); return; } table_update(table); return; } } errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }
static void lka_resume(struct lka_session *lks) { struct envelope *ep; struct expandnode *xn; if (lks->error) goto error; /* pop next node and expand it */ while ((xn = TAILQ_FIRST(&lks->nodes))) { TAILQ_REMOVE(&lks->nodes, xn, tq_entry); lka_expand(lks, xn->rule, xn); if (lks->flags & F_WAITING) return; if (lks->error) goto error; } /* delivery list is empty, reject */ if (TAILQ_FIRST(&lks->deliverylist) == NULL) { log_trace(TRACE_EXPAND, "expand: lka_done: expanded to empty " "delivery list"); lks->error = LKA_PERMFAIL; } error: if (lks->error) { m_create(p_smtp, IMSG_LKA_EXPAND_RCPT, 0, 0, -1); m_add_id(p_smtp, lks->id); m_add_int(p_smtp, lks->error); if (lks->errormsg) m_add_string(p_smtp, lks->errormsg); else { if (lks->error == LKA_PERMFAIL) m_add_string(p_smtp, "550 Invalid recipient"); else if (lks->error == LKA_TEMPFAIL) m_add_string(p_smtp, "451 Temporary failure"); } m_close(p_smtp); while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { TAILQ_REMOVE(&lks->deliverylist, ep, entry); free(ep); } } else { /* Process the delivery list and submit envelopes to queue */ while ((ep = TAILQ_FIRST(&lks->deliverylist)) != NULL) { TAILQ_REMOVE(&lks->deliverylist, ep, entry); m_create(p_queue, IMSG_QUEUE_SUBMIT_ENVELOPE, 0, 0, -1); m_add_id(p_queue, lks->id); m_add_envelope(p_queue, ep); m_close(p_queue); free(ep); } m_create(p_queue, IMSG_QUEUE_COMMIT_ENVELOPES, 0, 0, -1); m_add_id(p_queue, lks->id); m_close(p_queue); } expand_clear(&lks->expand); tree_xpop(&sessions, lks->id); free(lks); }
/* ARGSUSED */ static void control_dispatch_ext(struct mproc *p, struct imsg *imsg) { struct sockaddr_storage ss; struct ctl_conn *c; int v; struct stat_kv *kvp; char *key; struct stat_value val; size_t len; c = p->data; if (imsg == NULL) { control_close(c); return; } if (imsg->hdr.peerid != IMSG_VERSION) { m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0); return; } switch (imsg->hdr.type) { case IMSG_SMTP_ENQUEUE_FD: if (env->sc_flags & (SMTPD_SMTP_PAUSED | SMTPD_EXITING)) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } m_compose(p_smtp, IMSG_SMTP_ENQUEUE_FD, c->id, 0, -1, &c->euid, sizeof(c->euid)); return; case IMSG_STATS: if (c->euid) goto badcred; m_compose(p, IMSG_STATS, 0, 0, -1, NULL, 0); return; case IMSG_DIGEST: if (c->euid) goto badcred; digest.timestamp = time(NULL); m_compose(p, IMSG_DIGEST, 0, 0, -1, &digest, sizeof digest); return; case IMSG_STATS_GET: if (c->euid) goto badcred; kvp = imsg->data; if (! stat_backend->iter(&kvp->iter, &key, &val)) kvp->iter = NULL; else { strlcpy(kvp->key, key, sizeof kvp->key); kvp->val = val; } m_compose(p, IMSG_STATS_GET, 0, 0, -1, kvp, sizeof *kvp); return; case IMSG_CTL_SHUTDOWN: /* NEEDS_FIX */ log_debug("debug: received shutdown request"); if (c->euid) goto badcred; if (env->sc_flags & SMTPD_EXITING) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } env->sc_flags |= SMTPD_EXITING; m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); m_compose(p_parent, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0); return; case IMSG_CTL_VERBOSE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); verbose = v; log_verbose(verbose); m_create(p_parent, IMSG_CTL_VERBOSE, 0, 0, -1); m_add_int(p_parent, verbose); m_close(p_parent); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_TRACE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); verbose |= v; log_verbose(verbose); m_create(p_parent, IMSG_CTL_TRACE, 0, 0, -1); m_add_int(p_parent, v); m_close(p_parent); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_UNTRACE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); verbose &= ~v; log_verbose(verbose); m_create(p_parent, IMSG_CTL_UNTRACE, 0, 0, -1); m_add_int(p_parent, v); m_close(p_parent); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PROFILE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); profiling |= v; m_create(p_parent, IMSG_CTL_PROFILE, 0, 0, -1); m_add_int(p_parent, v); m_close(p_parent); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_UNPROFILE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); profiling &= ~v; m_create(p_parent, IMSG_CTL_UNPROFILE, 0, 0, -1); m_add_int(p_parent, v); m_close(p_parent); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PAUSE_EVP: if (c->euid) goto badcred; imsg->hdr.peerid = c->id; m_forward(p_scheduler, imsg); return; case IMSG_CTL_PAUSE_MDA: if (c->euid) goto badcred; if (env->sc_flags & SMTPD_MDA_PAUSED) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: mda paused"); env->sc_flags |= SMTPD_MDA_PAUSED; m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PAUSE_MTA: if (c->euid) goto badcred; if (env->sc_flags & SMTPD_MTA_PAUSED) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: mta paused"); env->sc_flags |= SMTPD_MTA_PAUSED; m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PAUSE_SMTP: if (c->euid) goto badcred; if (env->sc_flags & SMTPD_SMTP_PAUSED) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: smtp paused"); env->sc_flags |= SMTPD_SMTP_PAUSED; m_compose(p_smtp, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_RESUME_EVP: if (c->euid) goto badcred; imsg->hdr.peerid = c->id; m_forward(p_scheduler, imsg); return; case IMSG_CTL_RESUME_MDA: if (c->euid) goto badcred; if (! (env->sc_flags & SMTPD_MDA_PAUSED)) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: mda resumed"); env->sc_flags &= ~SMTPD_MDA_PAUSED; m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_RESUME_MTA: if (c->euid) goto badcred; if (!(env->sc_flags & SMTPD_MTA_PAUSED)) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: mta resumed"); env->sc_flags &= ~SMTPD_MTA_PAUSED; m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_RESUME_SMTP: if (c->euid) goto badcred; if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } log_info("info: smtp resumed"); env->sc_flags &= ~SMTPD_SMTP_PAUSED; m_forward(p_smtp, imsg); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_RESUME_ROUTE: if (c->euid) goto badcred; m_forward(p_mta, imsg); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_LIST_MESSAGES: if (c->euid) goto badcred; m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1, imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); return; case IMSG_CTL_LIST_ENVELOPES: if (c->euid) goto badcred; m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1, imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); return; case IMSG_CTL_MTA_SHOW_HOSTS: case IMSG_CTL_MTA_SHOW_RELAYS: case IMSG_CTL_MTA_SHOW_ROUTES: case IMSG_CTL_MTA_SHOW_HOSTSTATS: case IMSG_CTL_MTA_SHOW_BLOCK: if (c->euid) goto badcred; imsg->hdr.peerid = c->id; m_forward(p_mta, imsg); return; case IMSG_CTL_MTA_BLOCK: case IMSG_CTL_MTA_UNBLOCK: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss)) goto invalid; memmove(&ss, imsg->data, sizeof(ss)); m_create(p_mta, imsg->hdr.type, c->id, 0, -1); m_add_sockaddr(p_mta, (struct sockaddr *)&ss); m_add_string(p_mta, (char *)imsg->data + sizeof(ss)); m_close(p_mta); return; case IMSG_CTL_SCHEDULE: if (c->euid) goto badcred; imsg->hdr.peerid = c->id; m_forward(p_scheduler, imsg); return; case IMSG_CTL_REMOVE: if (c->euid) goto badcred; imsg->hdr.peerid = c->id; m_forward(p_scheduler, imsg); return; case IMSG_LKA_UPDATE_TABLE: if (c->euid) goto badcred; /* table name too long */ len = strlen(imsg->data); if (len >= SMTPD_MAXLINESIZE) goto invalid; m_forward(p_lka, imsg); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; default: log_debug("debug: control_dispatch_ext: " "error handling %s imsg", imsg_to_str(imsg->hdr.type)); return; } badcred: invalid: m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); }
void dns_imsg(struct mproc *p, struct imsg *imsg) { struct sockaddr_storage ss; struct dns_session *s; struct sockaddr *sa; struct asr_query *as; struct msg m; const char *domain, *mx, *host; socklen_t sl; s = xcalloc(1, sizeof *s, "dns_imsg"); s->type = imsg->hdr.type; s->p = p; m_msg(&m, imsg); m_get_id(&m, &s->reqid); switch (s->type) { case IMSG_MTA_DNS_HOST: m_get_string(&m, &host); m_end(&m); dns_lookup_host(s, host, -1); return; case IMSG_MTA_DNS_PTR: case IMSG_SMTP_DNS_PTR: sa = (struct sockaddr *)&ss; m_get_sockaddr(&m, sa); m_end(&m); as = getnameinfo_async(sa, SA_LEN(sa), s->name, sizeof(s->name), NULL, 0, 0, NULL); event_asr_run(as, dns_dispatch_ptr, s); return; case IMSG_MTA_DNS_MX: m_get_string(&m, &domain); m_end(&m); (void)strlcpy(s->name, domain, sizeof(s->name)); sa = (struct sockaddr *)&ss; sl = sizeof(ss); if (domainname_is_addr(domain, sa, &sl)) { m_create(s->p, IMSG_MTA_DNS_HOST, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_sockaddr(s->p, sa); m_add_int(s->p, -1); m_close(s->p); m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, DNS_OK); m_close(s->p); free(s); return; } as = res_query_async(s->name, C_IN, T_MX, NULL); if (as == NULL) { log_warn("warn: req_query_async: %s", s->name); m_create(s->p, IMSG_MTA_DNS_HOST_END, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, DNS_EINVAL); m_close(s->p); free(s); return; } event_asr_run(as, dns_dispatch_mx, s); return; case IMSG_MTA_DNS_MX_PREFERENCE: m_get_string(&m, &domain); m_get_string(&m, &mx); m_end(&m); (void)strlcpy(s->name, mx, sizeof(s->name)); as = res_query_async(domain, C_IN, T_MX, NULL); if (as == NULL) { m_create(s->p, IMSG_MTA_DNS_MX_PREFERENCE, 0, 0, -1); m_add_id(s->p, s->reqid); m_add_int(s->p, DNS_ENOTFOUND); m_close(s->p); free(s); return; } event_asr_run(as, dns_dispatch_mx_preference, s); return; default: log_warnx("warn: bad dns request %d", s->type); fatal(NULL); } }
static void filter_dispatch(struct mproc *p, struct imsg *imsg) { struct filter_session *s; struct filter_connect q_connect; struct mailaddr maddr; struct msg m; const char *line, *name; uint32_t v, datalen; uint64_t id, qid; int status, type; int fds[2], fdin, fdout; log_trace(TRACE_FILTERS, "filter-api:%s imsg %s", filter_name, filterimsg_to_str(imsg->hdr.type)); switch (imsg->hdr.type) { case IMSG_FILTER_REGISTER: m_msg(&m, imsg); m_get_u32(&m, &v); m_get_string(&m, &name); filter_name = strdup(name); m_end(&m); if (v != FILTER_API_VERSION) { log_warnx("warn: filter-api:%s API mismatch", filter_name); fatalx("filter-api: exiting"); } m_create(p, IMSG_FILTER_REGISTER, 0, 0, -1); m_add_int(p, fi.hooks); m_add_int(p, fi.flags); m_close(p); break; case IMSG_FILTER_EVENT: m_msg(&m, imsg); m_get_id(&m, &id); m_get_int(&m, &type); m_end(&m); switch (type) { case EVENT_CONNECT: s = xcalloc(1, sizeof(*s), "filter_dispatch"); s->id = id; s->pipe.iev.sock = -1; s->pipe.oev.sock = -1; tree_xset(&sessions, id, s); break; case EVENT_DISCONNECT: filter_dispatch_disconnect(id); s = tree_xpop(&sessions, id); free(s); break; case EVENT_RESET: filter_dispatch_reset(id); break; case EVENT_COMMIT: filter_dispatch_commit(id); break; case EVENT_ROLLBACK: filter_dispatch_rollback(id); break; default: log_warnx("warn: filter-api:%s bad event %d", filter_name, type); fatalx("filter-api: exiting"); } break; case IMSG_FILTER_QUERY: m_msg(&m, imsg); m_get_id(&m, &id); m_get_id(&m, &qid); m_get_int(&m, &type); switch(type) { case QUERY_CONNECT: m_get_sockaddr(&m, (struct sockaddr*)&q_connect.local); m_get_sockaddr(&m, (struct sockaddr*)&q_connect.remote); m_get_string(&m, &q_connect.hostname); m_end(&m); filter_register_query(id, qid, type); filter_dispatch_connect(id, &q_connect); break; case QUERY_HELO: m_get_string(&m, &line); m_end(&m); filter_register_query(id, qid, type); filter_dispatch_helo(id, line); break; case QUERY_MAIL: m_get_mailaddr(&m, &maddr); m_end(&m); filter_register_query(id, qid, type); filter_dispatch_mail(id, &maddr); break; case QUERY_RCPT: m_get_mailaddr(&m, &maddr); m_end(&m); filter_register_query(id, qid, type); filter_dispatch_rcpt(id, &maddr); break; case QUERY_DATA: m_end(&m); filter_register_query(id, qid, type); filter_dispatch_data(id); break; case QUERY_EOM: m_get_u32(&m, &datalen); m_end(&m); filter_register_query(id, qid, type); filter_dispatch_eom(id, datalen); break; default: log_warnx("warn: filter-api:%s bad query %d", filter_name, type); fatalx("filter-api: exiting"); } break; case IMSG_FILTER_PIPE: m_msg(&m, imsg); m_get_id(&m, &id); m_end(&m); fdout = imsg->fd; fdin = -1; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fds) == -1) { log_warn("warn: filter-api:%s socketpair", filter_name); close(fdout); } else { s = tree_xget(&sessions, id); s->pipe.eom_called = 0; s->pipe.error = 0; s->pipe.idatalen = 0; s->pipe.odatalen = 0; iobuf_init(&s->pipe.obuf, 0, 0); io_init(&s->pipe.oev, fdout, s, filter_io_out, &s->pipe.obuf); io_set_write(&s->pipe.oev); iobuf_init(&s->pipe.ibuf, 0, 0); io_init(&s->pipe.iev, fds[0], s, filter_io_in, &s->pipe.ibuf); io_set_read(&s->pipe.iev); fdin = fds[1]; } log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" tx pipe %d -> %d", filter_name, id, fdin, fdout); m_create(&fi.p, IMSG_FILTER_PIPE, 0, 0, fdin); m_add_id(&fi.p, id); m_close(&fi.p); break; } }
void ca_imsg(struct mproc *p, struct imsg *imsg) { RSA *rsa; const void *from = NULL; unsigned char *to = NULL; struct msg m; const char *pkiname; size_t flen, tlen, padding; struct pki *pki; int ret = 0; uint64_t id; int v; if (p->proc == PROC_PARENT) { switch (imsg->hdr.type) { case IMSG_CONF_START: return; case IMSG_CONF_END: ca_init(); /* Start fulfilling requests */ mproc_enable(p_pony); return; } } if (p->proc == PROC_CONTROL) { switch (imsg->hdr.type) { case IMSG_CTL_VERBOSE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); log_verbose(v); return; case IMSG_CTL_PROFILE: m_msg(&m, imsg); m_get_int(&m, &v); m_end(&m); profiling = v; return; } } if (p->proc == PROC_PONY) { switch (imsg->hdr.type) { case IMSG_CA_PRIVENC: case IMSG_CA_PRIVDEC: m_msg(&m, imsg); m_get_id(&m, &id); m_get_string(&m, &pkiname); m_get_data(&m, &from, &flen); m_get_size(&m, &tlen); m_get_size(&m, &padding); m_end(&m); pki = dict_get(env->sc_pki_dict, pkiname); if (pki == NULL || pki->pki_pkey == NULL || (rsa = EVP_PKEY_get1_RSA(pki->pki_pkey)) == NULL) fatalx("ca_imsg: invalid pki"); if ((to = calloc(1, tlen)) == NULL) fatalx("ca_imsg: calloc"); switch (imsg->hdr.type) { case IMSG_CA_PRIVENC: ret = RSA_private_encrypt(flen, from, to, rsa, padding); break; case IMSG_CA_PRIVDEC: ret = RSA_private_decrypt(flen, from, to, rsa, padding); break; } m_create(p, imsg->hdr.type, 0, 0, -1); m_add_id(p, id); m_add_int(p, ret); if (ret > 0) m_add_data(p, to, (size_t)ret); m_close(p); free(to); RSA_free(rsa); return; } } errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); }