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); }
/* 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; uint64_t evpid; uint32_t msgid; 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_CTL_SMTP_SESSION: if (env->sc_flags & (SMTPD_SMTP_PAUSED | SMTPD_EXITING)) { m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); return; } m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1, &c->euid, sizeof(c->euid)); return; case IMSG_CTL_GET_DIGEST: if (c->euid) goto badcred; digest.timestamp = time(NULL); m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest); return; case IMSG_CTL_GET_STATS: if (c->euid) goto badcred; kvp = imsg->data; if (! stat_backend->iter(&kvp->iter, &key, &val)) kvp->iter = NULL; else { (void)strlcpy(kvp->key, key, sizeof kvp->key); kvp->val = val; } m_compose(p, IMSG_CTL_GET_STATS, 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); control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_TRACE_ENABLE: 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); control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_TRACE_DISABLE: 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); control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PROFILE_ENABLE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); profiling |= v; control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_PROFILE_DISABLE: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose)) goto badcred; memcpy(&v, imsg->data, sizeof(v)); profiling &= ~v; control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); 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_pony, 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_pony, 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_pony, 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_pony, imsg); return; case IMSG_CTL_SHOW_STATUS: if (c->euid) goto badcred; m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags, sizeof(env->sc_flags)); 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_pony, imsg->hdr.type, c->id, 0, -1); m_add_sockaddr(p_pony, (struct sockaddr *)&ss); m_add_string(p_pony, (char *)imsg->data + sizeof(ss)); m_close(p_pony); 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_CTL_UPDATE_TABLE: if (c->euid) goto badcred; /* table name too long */ len = strlen(imsg->data); if (len >= LINE_MAX) goto invalid; m_forward(p_lka, imsg); m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); return; case IMSG_CTL_DISCOVER_EVPID: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid) goto invalid; memmove(&evpid, imsg->data, sizeof evpid); m_create(p_queue, imsg->hdr.type, c->id, 0, -1); m_add_evpid(p_queue, evpid); m_close(p_queue); return; case IMSG_CTL_DISCOVER_MSGID: case IMSG_CTL_UNCORRUPT_MSGID: if (c->euid) goto badcred; if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid) goto invalid; memmove(&msgid, imsg->data, sizeof msgid); m_create(p_queue, imsg->hdr.type, c->id, 0, -1); m_add_msgid(p_queue, msgid); m_close(p_queue); 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); }
static void mta_connect(struct mta_session *s) { struct sockaddr_storage ss; struct sockaddr *sa; int portno; const char *schema = "smtp+tls://"; if (s->helo == NULL) { if (s->relay->helotable && s->route->src->sa) { m_create(p_lka, IMSG_LKA_HELO, 0, 0, -1); m_add_id(p_lka, s->id); m_add_string(p_lka, s->relay->helotable); m_add_sockaddr(p_lka, s->route->src->sa); m_close(p_lka); tree_xset(&wait_helo, s->id, s); s->flags |= MTA_WAIT; return; } else if (s->relay->heloname) s->helo = xstrdup(s->relay->heloname, "mta_connect"); else s->helo = xstrdup(env->sc_hostname, "mta_connect"); } io_clear(&s->io); iobuf_clear(&s->iobuf); s->use_smtps = s->use_starttls = s->use_smtp_tls = 0; switch (s->attempt) { case 0: if (s->flags & MTA_FORCE_SMTPS) s->use_smtps = 1; /* smtps */ else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL)) s->use_starttls = 1; /* tls, tls+smtps */ else if (!(s->flags & MTA_FORCE_PLAIN)) s->use_smtp_tls = 1; break; case 1: if (s->flags & MTA_FORCE_ANYSSL) { s->use_smtps = 1; /* tls+smtps */ break; } default: mta_free(s); return; } portno = s->use_smtps ? 465 : 25; /* Override with relay-specified port */ if (s->relay->port) portno = s->relay->port; memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len); sa = (struct sockaddr *)&ss; if (sa->sa_family == AF_INET) ((struct sockaddr_in *)sa)->sin_port = htons(portno); else if (sa->sa_family == AF_INET6) ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); s->attempt += 1; if (s->use_smtp_tls) schema = "smtp+tls://"; else if (s->use_starttls) schema = "tls://"; else if (s->use_smtps) schema = "smtps://"; else if (s->flags & MTA_LMTP) schema = "lmtp://"; else schema = "smtp://"; log_info("smtp-out: Connecting to %s%s:%d (%s) on session" " %016"PRIx64"...", schema, sa_to_text(s->route->dst->sa), portno, s->route->dst->ptrname, s->id); mta_enter_state(s, MTA_INIT); iobuf_xinit(&s->iobuf, 0, 0, "mta_connect"); io_init(&s->io, -1, s, mta_io, &s->iobuf); io_set_timeout(&s->io, 300000); if (io_connect(&s->io, sa, s->route->src->sa) == -1) { /* * This error is most likely a "no route", * so there is no need to try again. */ log_debug("debug: mta: io_connect failed: %s", s->io.error); if (errno == EADDRNOTAVAIL) mta_source_error(s->relay, s->route, s->io.error); else mta_error(s, "Connection failed: %s", s->io.error); mta_free(s); } }
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)); }
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); } }
void mta_session(struct mta_relay *relay, struct mta_route *route) { struct mta_session *s; struct timeval tv; mta_session_init(); s = xcalloc(1, sizeof *s, "mta_session"); s->id = generate_uid(); s->relay = relay; s->route = route; s->io.sock = -1; if (relay->flags & RELAY_SSL && relay->flags & RELAY_AUTH) s->flags |= MTA_USE_AUTH; if (relay->pki_name) s->flags |= MTA_USE_CERT; if (relay->flags & RELAY_LMTP) s->flags |= MTA_LMTP; switch (relay->flags & (RELAY_SSL|RELAY_TLS_OPTIONAL)) { case RELAY_SSL: s->flags |= MTA_FORCE_ANYSSL; s->flags |= MTA_WANT_SECURE; break; case RELAY_SMTPS: s->flags |= MTA_FORCE_SMTPS; s->flags |= MTA_WANT_SECURE; break; case RELAY_STARTTLS: s->flags |= MTA_FORCE_TLS; s->flags |= MTA_WANT_SECURE; break; case RELAY_TLS_OPTIONAL: /* do not force anything, try tls then smtp */ break; default: s->flags |= MTA_FORCE_PLAIN; } if (relay->flags & RELAY_BACKUP) s->flags &= ~MTA_FORCE_PLAIN; log_debug("debug: mta: %p: spawned for relay %s", s, mta_relay_to_text(relay)); stat_increment("mta.session", 1); if (route->dst->ptrname || route->dst->lastptrquery) { /* We want to delay the connection since to always notify * the relay asynchronously. */ tv.tv_sec = 0; tv.tv_usec = 0; evtimer_set(&s->io.ev, mta_start, s); evtimer_add(&s->io.ev, &tv); } else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) { m_create(p_lka, IMSG_MTA_DNS_PTR, 0, 0, -1); m_add_id(p_lka, s->id); m_add_sockaddr(p_lka, s->route->dst->sa); m_close(p_lka); tree_xset(&wait_ptr, s->id, s); s->flags |= MTA_WAIT; } }