pax_global_header00006660000000000000000000000064135524050520014513gustar00rootroot0000000000000052 comment=36cc59596e7aa7d5dfb404f78e8711dc77277a82 OpenSMTPD-extras-6.6.0/000077500000000000000000000000001355240505200145415ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/Makefile.am000066400000000000000000000000721355240505200165740ustar00rootroot00000000000000SUBDIRS = openbsd-compat extras ACLOCAL_AMFLAGS = -I m4 OpenSMTPD-extras-6.6.0/README.md000066400000000000000000000015671355240505200160310ustar00rootroot00000000000000# OpenSMTPD-extras This is the mirror of the official repository of OpenSMTPD addons # THE PROTOCOL USED BY THESE ADD-ONS IS GOING TO BE REPLACED SOON. # THIS REPOSITORY IS IN MAINTENANCE, ONLY ACCEPTING BUG FIXES AND MINOR UPDATES. # NO NEW ADD-ONS WILL BE ACCEPTED UNTIL CURRENT ONE ARE REIMPLEMENTED. # How to install First, you need to clone the repository: $ git clone https://github.com/OpenSMTPD/OpenSMTPD-extras.git Secondly, you need to bootstrap the repository, some dependencies (autotools, libtool) may be needed: $ sh bootstrap Then, you need to configure what add-ons you want to install, for example: $ ./configure --libexecdir=/usr/libexec/opensmtpd --with-table-mysql --with-filter-stub --with-queue-ram Finally build and install: $ make # make install The addons will be installed in /usr/libexec/opensmtpd where OpenSMTPD can find them. OpenSMTPD-extras-6.6.0/api/000077500000000000000000000000001355240505200153125ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/api/dict.c000066400000000000000000000122721355240505200164050ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Gilles Chehade * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include struct dictentry { SPLAY_ENTRY(dictentry) entry; const char *key; void *data; }; static int dictentry_cmp(struct dictentry *, struct dictentry *); SPLAY_PROTOTYPE(_dict, dictentry, entry, dictentry_cmp); int dict_check(struct dict *d, const char *k) { struct dictentry key; key.key = k; return (SPLAY_FIND(_dict, &d->dict, &key) != NULL); } static inline struct dictentry * dict_alloc(const char *k, void *data) { struct dictentry *e; size_t s = strlen(k) + 1; void *t; if ((e = malloc(sizeof(*e) + s)) == NULL) return NULL; e->key = t = (char*)(e) + sizeof(*e); e->data = data; memmove(t, k, s); return (e); } void * dict_set(struct dict *d, const char *k, void *data) { struct dictentry *entry, key; char *old; key.key = k; if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) { if ((entry = dict_alloc(k, data)) == NULL) err(1, "dict_set: malloc"); SPLAY_INSERT(_dict, &d->dict, entry); old = NULL; d->count += 1; } else { old = entry->data; entry->data = data; } return (old); } void dict_xset(struct dict *d, const char * k, void *data) { struct dictentry *entry; if ((entry = dict_alloc(k, data)) == NULL) err(1, "dict_xset: malloc"); if (SPLAY_INSERT(_dict, &d->dict, entry)) errx(1, "dict_xset(%p, %s)", d, k); d->count += 1; } void * dict_get(struct dict *d, const char *k) { struct dictentry key, *entry; key.key = k; if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) return (NULL); return (entry->data); } void * dict_xget(struct dict *d, const char *k) { struct dictentry key, *entry; key.key = k; if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) errx(1, "dict_xget(%p, %s)", d, k); return (entry->data); } void * dict_pop(struct dict *d, const char *k) { struct dictentry key, *entry; void *data; key.key = k; if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) return (NULL); data = entry->data; SPLAY_REMOVE(_dict, &d->dict, entry); free(entry); d->count -= 1; return (data); } void * dict_xpop(struct dict *d, const char *k) { struct dictentry key, *entry; void *data; key.key = k; if ((entry = SPLAY_FIND(_dict, &d->dict, &key)) == NULL) errx(1, "dict_xpop(%p, %s)", d, k); data = entry->data; SPLAY_REMOVE(_dict, &d->dict, entry); free(entry); d->count -= 1; return (data); } int dict_poproot(struct dict *d, void **data) { struct dictentry *entry; entry = SPLAY_ROOT(&d->dict); if (entry == NULL) return (0); if (data) *data = entry->data; SPLAY_REMOVE(_dict, &d->dict, entry); free(entry); d->count -= 1; return (1); } int dict_root(struct dict *d, const char **k, void **data) { struct dictentry *entry; entry = SPLAY_ROOT(&d->dict); if (entry == NULL) return (0); if (k) *k = entry->key; if (data) *data = entry->data; return (1); } int dict_iter(struct dict *d, void **hdl, const char **k, void **data) { struct dictentry *curr = *hdl; if (curr == NULL) curr = SPLAY_MIN(_dict, &d->dict); else curr = SPLAY_NEXT(_dict, &d->dict, curr); if (curr) { *hdl = curr; if (k) *k = curr->key; if (data) *data = curr->data; return (1); } return (0); } int dict_iterfrom(struct dict *d, void **hdl, const char *kfrom, const char **k, void **data) { struct dictentry *curr = *hdl, key; if (curr == NULL) { if (kfrom == NULL) curr = SPLAY_MIN(_dict, &d->dict); else { key.key = kfrom; curr = SPLAY_FIND(_dict, &d->dict, &key); if (curr == NULL) { SPLAY_INSERT(_dict, &d->dict, &key); curr = SPLAY_NEXT(_dict, &d->dict, &key); SPLAY_REMOVE(_dict, &d->dict, &key); } } } else curr = SPLAY_NEXT(_dict, &d->dict, curr); if (curr) { *hdl = curr; if (k) *k = curr->key; if (data) *data = curr->data; return (1); } return (0); } void dict_merge(struct dict *dst, struct dict *src) { struct dictentry *entry; while (!SPLAY_EMPTY(&src->dict)) { entry = SPLAY_ROOT(&src->dict); SPLAY_REMOVE(_dict, &src->dict, entry); if (SPLAY_INSERT(_dict, &dst->dict, entry)) errx(1, "dict_merge: duplicate"); } dst->count += src->count; src->count = 0; } static int dictentry_cmp(struct dictentry *a, struct dictentry *b) { return strcmp(a->key, b->key); } SPLAY_GENERATE(_dict, dictentry, entry, dictentry_cmp); OpenSMTPD-extras-6.6.0/api/filter_api.c000066400000000000000000000717631355240505200176120ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Eric Faurot * Copyright (c) 2011 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FILTER_HIWAT 65536 static struct tree queries; static struct tree sessions; struct filter_session { uint64_t id; uint64_t qid; int qtype; size_t datalen; int tx; struct { int eom_called; int error; struct io iev; struct iobuf ibuf; size_t idatalen; struct io oev; struct iobuf obuf; size_t odatalen; } pipe; struct { int ready; int status; int code; char *line; } response; void *usession; void *utx; void *data_buffer; void (*data_buffer_cb)(uint64_t, FILE *, void *); struct rfc2822_parser rfc2822_parser; struct dict headers_replace; struct dict headers_add; }; struct filter_timer { struct event ev; uint64_t id; void (*cb)(uint64_t, void *); void *arg; }; static int register_done; static const char *filter_name; static struct filter_internals { struct mproc p; uint32_t flags; uid_t uid; gid_t gid; const char *rootpath; struct { int (*connect)(uint64_t, struct filter_connect *); int (*helo)(uint64_t, const char *); int (*mail)(uint64_t, struct mailaddr *); int (*rcpt)(uint64_t, struct mailaddr *); int (*data)(uint64_t); void (*msg_line)(uint64_t, const char *); void (*msg_start)(uint64_t); int (*msg_end)(uint64_t, size_t); void (*disconnect)(uint64_t); void (*reset)(uint64_t); void *(*session_alloc)(uint64_t); void (*session_free)(void *); void *(*tx_alloc)(uint64_t); void (*tx_free)(void *); void (*tx_begin)(uint64_t); void (*tx_commit)(uint64_t); void (*tx_rollback)(uint64_t); } cb; int data_buffered; } fi; static void filter_api_init(void); static void filter_response(struct filter_session *, int, int, const char *); static void filter_send_response(struct filter_session *); static void filter_register_query(uint64_t, uint64_t, int); static void filter_dispatch(struct mproc *, struct imsg *); static void filter_dispatch_data(uint64_t); static void filter_dispatch_msg_line(uint64_t, const char *); static void filter_dispatch_msg_start(uint64_t); static void filter_dispatch_msg_end(uint64_t, size_t); static void filter_dispatch_connect(uint64_t, struct filter_connect *); static void filter_dispatch_helo(uint64_t, const char *); static void filter_dispatch_mail(uint64_t, struct mailaddr *); static void filter_dispatch_rcpt(uint64_t, struct mailaddr *); static void filter_dispatch_reset(uint64_t); static void filter_dispatch_tx_begin(uint64_t); static void filter_dispatch_tx_commit(uint64_t); static void filter_dispatch_tx_rollback(uint64_t); static void filter_dispatch_disconnect(uint64_t); static void filter_trigger_eom(struct filter_session *); static void filter_io_in(struct io *, int); static void filter_io_out(struct io *, int); static const char *filterimsg_to_str(int); static const char *query_to_str(int); static const char *event_to_str(int); static void data_buffered_setup(struct filter_session *); static void data_buffered_release(struct filter_session *); static void data_buffered_stream_process(uint64_t, FILE *, void *); static void filter_response(struct filter_session *s, int status, int code, const char *line) { log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" %s filter_response(%d, %d, %s)", filter_name, s->id, query_to_str(s->qtype), status, code, line); s->response.ready = 1; s->response.status = status; s->response.code = code; if (line) s->response.line = strdup(line); else s->response.line = NULL; /* eom is special, as the reponse has to be deferred until the pipe is all flushed */ if (s->qtype == QUERY_EOM) { /* wait for the obuf to drain */ if (iobuf_queued(&s->pipe.obuf)) return; if (s->pipe.oev.sock != -1) { io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); } filter_trigger_eom(s); } else filter_send_response(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 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 type; int fds[2], fdin, fdout; #ifdef EXPERIMENTAL log_warnx("filter is EXPERIMENTAL and NOT meant to be used in production."); #endif if (imsg == NULL) { log_trace(TRACE_FILTERS, "filter-api:%s server closed", filter_name); exit(0); } 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); /* all hooks for now */ m_add_int(p, ~0); 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 = calloc(1, sizeof(*s)); if (s == NULL) fatal("filter_dispatch"); s->id = id; s->pipe.iev.sock = -1; s->pipe.oev.sock = -1; tree_xset(&sessions, id, s); if (fi.cb.session_alloc) s->usession = fi.cb.session_alloc(id); break; case EVENT_DISCONNECT: filter_dispatch_disconnect(id); s = tree_xpop(&sessions, id); if (fi.cb.session_free && s->usession) fi.cb.session_free(s->usession); free(s); break; case EVENT_RESET: filter_dispatch_reset(id); break; case EVENT_TX_BEGIN: filter_dispatch_tx_begin(id); break; case EVENT_TX_COMMIT: filter_dispatch_tx_commit(id); break; case EVENT_TX_ROLLBACK: filter_dispatch_tx_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_msg_end(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 (fdout == -1) { log_warnx("warn: %016"PRIx64" failed to receive pipe", id); } else 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); if (fdin != -1) filter_dispatch_msg_start(id); break; } } static void filter_register_query(uint64_t id, uint64_t qid, int type) { struct filter_session *s; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" %s", filter_name, id, query_to_str(type)); s = tree_xget(&sessions, id); if (s->qid) { log_warnx("warn: filter-api:%s query already in progress", filter_name); fatalx("filter-api: exiting"); } s->qid = qid; s->qtype = type; s->response.ready = 0; tree_xset(&queries, qid, s); } static void filter_dispatch_connect(uint64_t id, struct filter_connect *conn) { if (fi.cb.connect) fi.cb.connect(id, conn); else filter_api_accept(id); } static void filter_dispatch_helo(uint64_t id, const char *helo) { if (fi.cb.helo) fi.cb.helo(id, helo); else filter_api_accept(id); } static void filter_dispatch_mail(uint64_t id, struct mailaddr *mail) { if (fi.cb.mail) fi.cb.mail(id, mail); else filter_api_accept(id); } static void filter_dispatch_rcpt(uint64_t id, struct mailaddr *rcpt) { if (fi.cb.rcpt) fi.cb.rcpt(id, rcpt); else filter_api_accept(id); } static void filter_dispatch_data(uint64_t id) { if (fi.cb.data) fi.cb.data(id); else filter_api_accept(id); } static void filter_dispatch_reset(uint64_t id) { if (fi.cb.reset) fi.cb.reset(id); } static void filter_dispatch_tx_begin(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); if (s->tx) fatalx("tx-begin: session %016"PRIx64" in transaction", id); s->tx = 1; if (fi.cb.tx_alloc) s->utx = fi.cb.tx_alloc(id); if (fi.cb.tx_begin) fi.cb.tx_begin(id); } static void filter_dispatch_tx_commit(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); if (s->tx == 0) fatalx("tx-commit: session %016"PRIx64" not in transaction", id); s->tx = 0; io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); if (fi.cb.tx_commit) fi.cb.tx_commit(id); if (fi.cb.tx_free && s->utx) { fi.cb.tx_free(s->utx); s->utx = NULL; } if (s->data_buffer) data_buffered_release(s); } static void filter_dispatch_tx_rollback(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); if (s->tx == 0) fatalx("tx-rollback: session %016"PRIx64" not in transaction", id); s->tx = 0; io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); if (fi.cb.tx_rollback) fi.cb.tx_rollback(id); if (fi.cb.tx_free && s->utx) { fi.cb.tx_free(s->utx); s->utx = NULL; } if (s->data_buffer) data_buffered_release(s); } static void filter_dispatch_disconnect(uint64_t id) { if (fi.cb.disconnect) fi.cb.disconnect(id); } static void filter_dispatch_msg_line(uint64_t id, const char *data) { if (fi.cb.msg_line) fi.cb.msg_line(id, data); else filter_api_writeln(id, data); } static void filter_dispatch_msg_start(uint64_t id) { struct filter_session *s; if (fi.data_buffered) { s = tree_xget(&sessions, id); data_buffered_setup(s); } if (fi.cb.msg_start) fi.cb.msg_start(id); } static void filter_dispatch_msg_end(uint64_t id, size_t datalen) { struct filter_session *s; s = tree_xget(&sessions, id); s->datalen = datalen; filter_trigger_eom(s); } static void filter_trigger_eom(struct filter_session *s) { log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_trigger_eom(%d, %d, %zu, %zu, %zu)", filter_name, s->id, s->pipe.iev.sock, s->pipe.oev.sock, s->datalen, s->pipe.idatalen, s->pipe.odatalen); /* This is called when * - EOM query is first received * - input data is closed * - output has been written */ /* input not done yet, or EOM query not received */ if (s->pipe.iev.sock != -1 || s->qid == 0) return; if (s->pipe.error) goto fail; /* if size don't match, error out */ if (s->pipe.idatalen != s->datalen) { log_trace(TRACE_FILTERS, "filter-api:%s tx datalen mismatch: %zu/%zu", filter_name, s->pipe.idatalen, s->datalen); s->pipe.error = 1; goto fail; } /* if we didn't send the eom to the user do it now */ if (!s->pipe.eom_called) { s->pipe.eom_called = 1; if (fi.cb.msg_end) fi.cb.msg_end(s->id, s->datalen); else filter_api_accept(s->id); return; } if (s->pipe.error) goto fail; /* wait for the output socket to be closed */ if (s->pipe.oev.sock != -1) return; s->datalen = s->pipe.odatalen; filter_send_response(s); fail: /* XXX */ return; } static void filter_io_in(struct io *io, int evt) { struct filter_session *s = io->arg; char *line; size_t len; log_trace(TRACE_FILTERS, "filter-api:%s filter_io_in(%p, %s)", filter_name, s, io_strevent(evt)); switch (evt) { case IO_DATAIN: nextline: line = iobuf_getline(&s->pipe.ibuf, &len); if ((line == NULL && iobuf_len(&s->pipe.ibuf) >= LINE_MAX) || (line && len >= LINE_MAX)) { s->pipe.error = 1; break; } /* No complete line received */ if (line == NULL) { iobuf_normalize(&s->pipe.ibuf); /* flow control */ if (iobuf_queued(&s->pipe.obuf) >= FILTER_HIWAT) io_pause(&s->pipe.iev, IO_PAUSE_IN); return; } s->pipe.idatalen += len + 1; /* XXX warning: do not clear io from this call! */ if (s->data_buffer) { /* XXX handle errors somehow */ fprintf(s->data_buffer, "%s\n", line); } filter_dispatch_msg_line(s->id, line); goto nextline; case IO_DISCONNECTED: if (iobuf_len(&s->pipe.ibuf)) { log_warn("warn: filter-api:%s %016"PRIx64" incomplete input", filter_name, s->id); } log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" input done (%zu bytes)", filter_name, s->id, s->pipe.idatalen); break; default: log_warn("warn: filter-api:%s %016"PRIx64": unexpected io event %d on data pipe", filter_name, s->id, evt); s->pipe.error = 1; } if (s->pipe.error) { io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); } io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); filter_trigger_eom(s); } static void filter_io_out(struct io *io, int evt) { struct filter_session *s = io->arg; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_io_out(%s)", filter_name, s->id, io_strevent(evt)); switch (evt) { case IO_TIMEOUT: case IO_DISCONNECTED: case IO_ERROR: log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" io error on output pipe", filter_name, s->id); s->pipe.error = 1; break; case IO_LOWAT: /* flow control */ if (s->pipe.iev.sock != -1 && s->pipe.iev.flags & IO_PAUSE_IN) { io_resume(&s->pipe.iev, IO_PAUSE_IN); return; } /* if the input is done and there is a response we are done */ if (s->pipe.iev.sock == -1 && s->response.ready) break; /* just wait for more data to send or feed through callback */ if (s->data_buffer_cb) s->data_buffer_cb(s->id, s->data_buffer, s); return; default: fatalx("filter_io_out()"); } io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); if (s->pipe.error) { io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); } filter_trigger_eom(s); } #define CASE(x) case x : return #x static const char * filterimsg_to_str(int imsg) { switch (imsg) { CASE(IMSG_FILTER_REGISTER); CASE(IMSG_FILTER_EVENT); CASE(IMSG_FILTER_QUERY); CASE(IMSG_FILTER_PIPE); CASE(IMSG_FILTER_RESPONSE); default: return ("IMSG_FILTER_???"); } } static const char * query_to_str(int query) { switch (query) { CASE(QUERY_CONNECT); CASE(QUERY_HELO); CASE(QUERY_MAIL); CASE(QUERY_RCPT); CASE(QUERY_DATA); CASE(QUERY_EOM); CASE(QUERY_DATALINE); default: return ("QUERY_???"); } } static const char * event_to_str(int event) { switch (event) { CASE(EVENT_CONNECT); CASE(EVENT_RESET); CASE(EVENT_DISCONNECT); CASE(EVENT_TX_BEGIN); CASE(EVENT_TX_COMMIT); CASE(EVENT_TX_ROLLBACK); default: return ("EVENT_???"); } } /* * These functions are called from mproc.c */ enum smtp_proc_type smtpd_process; const char * proc_name(enum smtp_proc_type proc) { if (proc == PROC_FILTER) return (filter_name); return ("filter"); } const char * imsg_to_str(int imsg) { static char buf[32]; snprintf(buf, sizeof(buf), "%d", imsg); return (buf); } /* * These functions are callable by filters */ void filter_api_session_allocator(void *(*f)(uint64_t)) { fi.cb.session_alloc = f; } void filter_api_session_destructor(void (*f)(void *)) { fi.cb.session_free = f; } void * filter_api_session(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); return s->usession; } void filter_api_transaction_allocator(void *(*f)(uint64_t)) { fi.cb.tx_alloc = f; } void filter_api_transaction_destructor(void (*f)(void *)) { fi.cb.tx_free = f; } void * filter_api_transaction(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); return s->utx; } void filter_api_setugid(uid_t uid, gid_t gid) { filter_api_init(); if (!uid) { log_warn("warn: filter-api:%s can't set uid 0", filter_name); fatalx("filter-api: exiting"); } if (!gid) { log_warn("warn: filter-api:%s can't set gid 0", filter_name); fatalx("filter-api: exiting"); } fi.uid = uid; fi.gid = gid; } void filter_api_no_chroot(void) { filter_api_init(); fi.rootpath = NULL; } void filter_api_set_chroot(const char *rootpath) { filter_api_init(); fi.rootpath = rootpath; } static void filter_api_init(void) { extern const char *__progname; struct passwd *pw; static int init = 0; if (init) return; init = 1; smtpd_process = PROC_FILTER; filter_name = __progname; pw = getpwnam(SMTPD_USER); if (pw == NULL) { log_warn("warn: filter-api:%s getpwnam", filter_name); fatalx("filter-api: exiting"); } tree_init(&queries); tree_init(&sessions); event_init(); memset(&fi, 0, sizeof(fi)); fi.p.proc = PROC_PONY; fi.p.name = "filter"; fi.p.handler = filter_dispatch; fi.uid = pw->pw_uid; fi.gid = pw->pw_gid; fi.rootpath = PATH_CHROOT; mproc_init(&fi.p, 0); } void filter_api_on_connect(int(*cb)(uint64_t, struct filter_connect *)) { filter_api_init(); fi.cb.connect = cb; } void filter_api_on_helo(int(*cb)(uint64_t, const char *)) { filter_api_init(); fi.cb.helo = cb; } void filter_api_on_mail(int(*cb)(uint64_t, struct mailaddr *)) { filter_api_init(); fi.cb.mail = cb; } void filter_api_on_rcpt(int(*cb)(uint64_t, struct mailaddr *)) { filter_api_init(); fi.cb.rcpt = cb; } void filter_api_on_data(int(*cb)(uint64_t)) { filter_api_init(); fi.cb.data = cb; } void filter_api_on_msg_line(void(*cb)(uint64_t, const char *)) { filter_api_init(); fi.cb.msg_line = cb; } void filter_api_on_msg_start(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.msg_start = cb; } void filter_api_on_msg_end(int(*cb)(uint64_t, size_t)) { filter_api_init(); fi.cb.msg_end = cb; } void filter_api_on_reset(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.reset = cb; } void filter_api_on_disconnect(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.disconnect = cb; } void filter_api_on_tx_begin(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.tx_begin = cb; } void filter_api_on_tx_commit(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.tx_commit = cb; } void filter_api_on_tx_rollback(void(*cb)(uint64_t)) { filter_api_init(); fi.cb.tx_rollback = cb; } void filter_api_loop(void) { if (register_done) { log_warnx("warn: filter-api:%s filter_api_loop() already called", filter_name); fatalx("filter-api: exiting"); } filter_api_init(); register_done = 1; mproc_enable(&fi.p); if (fi.rootpath) { if (chroot(fi.rootpath) == -1) { log_warn("warn: filter-api:%s chroot", filter_name); fatalx("filter-api: exiting"); } if (chdir("/") == -1) { log_warn("warn: filter-api:%s chdir", filter_name); fatalx("filter-api: exiting"); } } if (setgroups(1, &fi.gid) || setresgid(fi.gid, fi.gid, fi.gid) || setresuid(fi.uid, fi.uid, fi.uid)) { log_warn("warn: filter-api:%s cannot drop privileges", filter_name); fatalx("filter-api: exiting"); } /* we must ignore SIGPIPE otherwise we might die when a data pipe goes away */ signal(SIGPIPE, SIG_IGN); if (event_dispatch() < 0) { log_warn("warn: filter-api:%s event_dispatch", filter_name); fatalx("filter-api: exiting"); } } int filter_api_accept(uint64_t id) { struct filter_session *s; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_api_accept()", filter_name, id); s = tree_xget(&sessions, id); filter_response(s, FILTER_OK, 0, NULL); return (1); } int filter_api_reject(uint64_t id, enum filter_status status) { struct filter_session *s; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_api_reject(%d)", filter_name, id, status); s = tree_xget(&sessions, id); /* This is NOT an acceptable status for a failure */ if (status == FILTER_OK) status = FILTER_FAIL; filter_response(s, status, 0, NULL); return (1); } int filter_api_reject_code(uint64_t id, enum filter_status status, uint32_t code, const char *line) { struct filter_session *s; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_api_reject_code(%d, %u, %s)", filter_name, id, status, code, line); s = tree_xget(&sessions, id); /* This is NOT an acceptable status for a failure */ if (status == FILTER_OK) status = FILTER_FAIL; filter_response(s, status, code, line); return (1); } void filter_api_writeln(uint64_t id, const char *line) { struct filter_session *s; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_api_writeln(%s)", filter_name, id, line); s = tree_xget(&sessions, id); if (s->pipe.oev.sock == -1) { log_warnx("warn: session %016"PRIx64": write out of sequence", id); return; } s->pipe.odatalen += strlen(line) + 1; iobuf_fqueue(&s->pipe.obuf, "%s\n", line); io_reload(&s->pipe.oev); } void filter_api_printf(uint64_t id, const char *fmt, ...) { struct filter_session *s; va_list ap; int len; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_api_printf(%s)", filter_name, id, fmt); s = tree_xget(&sessions, id); if (s->pipe.oev.sock == -1) { log_warnx("warn: session %016"PRIx64": write out of sequence", id); return; } va_start(ap, fmt); len = iobuf_vfqueue(&s->pipe.obuf, fmt, ap); iobuf_fqueue(&s->pipe.obuf, "\n"); va_end(ap); s->pipe.odatalen += len + 1; io_reload(&s->pipe.oev); } static void filter_api_timer_cb(int fd, short evt, void *arg) { struct filter_timer *ft = arg; ft->cb(ft->id, ft->arg); free(ft); } void filter_api_timer(uint64_t id, uint32_t tmo, void (*cb)(uint64_t, void *), void *arg) { struct filter_timer *ft = xcalloc(1, sizeof(struct filter_timer), "filter_api_timer"); struct timeval tv = { tmo / 1000, (tmo % 1000) * 1000 }; ft->id = id; ft->cb = cb; ft->arg = arg; evtimer_set(&ft->ev, filter_api_timer_cb, ft); evtimer_add(&ft->ev, &tv); } const char * filter_api_sockaddr_to_text(const struct sockaddr *sa) { static char buf[NI_MAXHOST]; if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST)) return ("(unknown)"); else return (buf); } const char * filter_api_mailaddr_to_text(const struct mailaddr *maddr) { static char buffer[LINE_MAX]; strlcpy(buffer, maddr->user, sizeof buffer); if (maddr->domain[0] == '\0') return (buffer); strlcat(buffer, "@", sizeof buffer); if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) return (NULL); return (buffer); } /* X X X */ static void data_buffered_stream_process(uint64_t id, FILE *fp, void *arg) { struct filter_session *s; size_t sz; ssize_t len; char *line = NULL; s = tree_xget(&sessions, id); errno = 0; if ((len = getline(&line, &sz, fp)) == -1) { if (errno) { filter_api_reject_code(id, FILTER_FAIL, 421, "Internal Server Error"); return; } filter_api_accept(id); return; } line[strcspn(line, "\n")] = '\0'; rfc2822_parser_feed(&s->rfc2822_parser, line); free(line); /* XXX - should be driven by parser_feed */ if (1) io_callback(&s->pipe.oev, IO_LOWAT); } static void default_header_callback(const struct rfc2822_header *hdr, void *arg) { struct filter_session *s = arg; struct rfc2822_line *l; int i = 0; TAILQ_FOREACH(l, &hdr->lines, next) { if (i++ == 0) { filter_api_printf(s->id, "%s: %s", hdr->name, l->buffer + 1); continue; } filter_api_printf(s->id, "%s", l->buffer); } } static void default_body_callback(const char *line, void *arg) { struct filter_session *s = arg; filter_api_writeln(s->id, line); } static void header_remove_callback(const struct rfc2822_header *hdr, void *arg) { } static void header_replace_callback(const struct rfc2822_header *hdr, void *arg) { struct filter_session *s = arg; char *value; char *key; key = xstrdup(hdr->name, "header_replace_callback"); lowercase(key, key, strlen(key)+1); value = dict_xget(&s->headers_replace, key); filter_api_printf(s->id, "%s: %s", hdr->name, value); free(key); } static void header_eoh_callback(void *arg) { struct filter_session *s = arg; void *iter; const char *key; void *data; iter = NULL; while (dict_iter(&s->headers_add, &iter, &key, &data)) filter_api_printf(s->id, "%s: %s", key, (char *)data); } void data_buffered_setup(struct filter_session *s) { FILE *fp; int fd; char pathname[] = "/tmp/XXXXXXXXXX"; fd = mkstemp(pathname); if (fd == -1) return; fp = fdopen(fd, "w+b"); if (fp == NULL) { close(fd); return; } unlink(pathname); s->data_buffer = fp; s->data_buffer_cb = data_buffered_stream_process; rfc2822_parser_init(&s->rfc2822_parser); rfc2822_parser_reset(&s->rfc2822_parser); rfc2822_header_default_callback(&s->rfc2822_parser, default_header_callback, s); rfc2822_body_callback(&s->rfc2822_parser, default_body_callback, s); rfc2822_eoh_callback(&s->rfc2822_parser, header_eoh_callback, s); dict_init(&s->headers_replace); dict_init(&s->headers_add); } static void data_buffered_release(struct filter_session *s) { void *data; rfc2822_parser_release(&s->rfc2822_parser); if (s->data_buffer) { fclose(s->data_buffer); s->data_buffer = NULL; } while (dict_poproot(&s->headers_replace, &data)) free(data); while (dict_poproot(&s->headers_add, &data)) free(data); } void filter_api_data_buffered(void) { fi.data_buffered = 1; } void filter_api_data_buffered_stream(uint64_t id) { struct filter_session *s; s = tree_xget(&sessions, id); if (s->data_buffer) fseek(s->data_buffer, 0, 0); io_callback(&s->pipe.oev, IO_LOWAT); } void filter_api_header_remove(uint64_t id, const char *header) { struct filter_session *s; s = tree_xget(&sessions, id); rfc2822_header_callback(&s->rfc2822_parser, header, header_remove_callback, s); } void filter_api_header_replace(uint64_t id, const char *header, const char *fmt, ...) { struct filter_session *s; char *key; char *buffer = NULL; va_list ap; s = tree_xget(&sessions, id); va_start(ap, fmt); vasprintf(&buffer, fmt, ap); va_end(ap); key = xstrdup(header, "filter_api_header_replace"); lowercase(key, key, strlen(key)+1); dict_set(&s->headers_replace, header, buffer); free(key); rfc2822_header_callback(&s->rfc2822_parser, header, header_replace_callback, s); } void filter_api_header_add(uint64_t id, const char *header, const char *fmt, ...) { struct filter_session *s; char *key; char *buffer = NULL; va_list ap; s = tree_xget(&sessions, id); va_start(ap, fmt); vasprintf(&buffer, fmt, ap); va_end(ap); key = xstrdup(header, "filter_api_header_replace"); lowercase(key, key, strlen(key)+1); dict_set(&s->headers_add, header, buffer); free(key); } OpenSMTPD-extras-6.6.0/api/iobuf.c000066400000000000000000000174131355240505200165700ustar00rootroot00000000000000/* $OpenBSD: iobuf.c,v 1.9 2015/12/14 10:22:12 jung Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #ifdef IO_SSL #include #include #endif #include "iobuf.h" #define IOBUF_MAX 65536 #define IOBUFQ_MIN 4096 struct ioqbuf *ioqbuf_alloc(struct iobuf *, size_t); void iobuf_drain(struct iobuf *, size_t); int iobuf_init(struct iobuf *io, size_t size, size_t max) { memset(io, 0, sizeof *io); if (max == 0) max = IOBUF_MAX; if (size == 0) size = max; if (size > max) return (-1); if ((io->buf = malloc(size)) == NULL) return (-1); io->size = size; io->max = max; return (0); } void iobuf_clear(struct iobuf *io) { struct ioqbuf *q; free(io->buf); while ((q = io->outq)) { io->outq = q->next; free(q); } memset(io, 0, sizeof (*io)); } void iobuf_drain(struct iobuf *io, size_t n) { struct ioqbuf *q; size_t left = n; while ((q = io->outq) && left) { if ((q->wpos - q->rpos) > left) { q->rpos += left; left = 0; } else { left -= q->wpos - q->rpos; io->outq = q->next; free(q); } } io->queued -= (n - left); if (io->outq == NULL) io->outqlast = NULL; } int iobuf_extend(struct iobuf *io, size_t n) { char *t; if (n > io->max) return (-1); if (io->max - io->size < n) return (-1); t = realloc(io->buf, io->size + n); if (t == NULL) return (-1); io->size += n; io->buf = t; return (0); } size_t iobuf_left(struct iobuf *io) { return io->size - io->wpos; } size_t iobuf_space(struct iobuf *io) { return io->size - (io->wpos - io->rpos); } size_t iobuf_len(struct iobuf *io) { return io->wpos - io->rpos; } char * iobuf_data(struct iobuf *io) { return io->buf + io->rpos; } void iobuf_drop(struct iobuf *io, size_t n) { if (n >= iobuf_len(io)) { io->rpos = io->wpos = 0; return; } io->rpos += n; } char * iobuf_getline(struct iobuf *iobuf, size_t *rlen) { char *buf; size_t len, i; buf = iobuf_data(iobuf); len = iobuf_len(iobuf); for (i = 0; i + 1 <= len; i++) if (buf[i] == '\n') { /* Note: the returned address points into the iobuf * buffer. We NUL-end it for convenience, and discard * the data from the iobuf, so that the caller doesn't * have to do it. The data remains "valid" as long * as the iobuf does not overwrite it, that is until * the next call to iobuf_normalize() or iobuf_extend(). */ iobuf_drop(iobuf, i + 1); len = (i && buf[i - 1] == '\r') ? i - 1 : i; buf[len] = '\0'; if (rlen) *rlen = len; return (buf); } return (NULL); } void iobuf_normalize(struct iobuf *io) { if (io->rpos == 0) return; if (io->rpos == io->wpos) { io->rpos = io->wpos = 0; return; } memmove(io->buf, io->buf + io->rpos, io->wpos - io->rpos); io->wpos -= io->rpos; io->rpos = 0; } ssize_t iobuf_read(struct iobuf *io, int fd) { ssize_t n; n = read(fd, io->buf + io->wpos, iobuf_left(io)); if (n == -1) { /* XXX is this really what we want? */ if (errno == EAGAIN || errno == EINTR) return (IOBUF_WANT_READ); return (IOBUF_ERROR); } if (n == 0) return (IOBUF_CLOSED); io->wpos += n; return (n); } struct ioqbuf * ioqbuf_alloc(struct iobuf *io, size_t len) { struct ioqbuf *q; if (len < IOBUFQ_MIN) len = IOBUFQ_MIN; if ((q = malloc(sizeof(*q) + len)) == NULL) return (NULL); q->rpos = 0; q->wpos = 0; q->size = len; q->next = NULL; q->buf = (char *)(q) + sizeof(*q); if (io->outqlast == NULL) io->outq = q; else io->outqlast->next = q; io->outqlast = q; return (q); } size_t iobuf_queued(struct iobuf *io) { return io->queued; } void * iobuf_reserve(struct iobuf *io, size_t len) { struct ioqbuf *q; void *r; if (len == 0) return (NULL); if (((q = io->outqlast) == NULL) || q->size - q->wpos <= len) { if ((q = ioqbuf_alloc(io, len)) == NULL) return (NULL); } r = q->buf + q->wpos; q->wpos += len; io->queued += len; return (r); } int iobuf_queue(struct iobuf *io, const void *data, size_t len) { void *buf; if (len == 0) return (0); if ((buf = iobuf_reserve(io, len)) == NULL) return (-1); memmove(buf, data, len); return (len); } int iobuf_queuev(struct iobuf *io, const struct iovec *iov, int iovcnt) { int i; size_t len = 0; char *buf; for (i = 0; i < iovcnt; i++) len += iov[i].iov_len; if ((buf = iobuf_reserve(io, len)) == NULL) return (-1); for (i = 0; i < iovcnt; i++) { if (iov[i].iov_len == 0) continue; memmove(buf, iov[i].iov_base, iov[i].iov_len); buf += iov[i].iov_len; } return (0); } int iobuf_fqueue(struct iobuf *io, const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = iobuf_vfqueue(io, fmt, ap); va_end(ap); return (len); } int iobuf_vfqueue(struct iobuf *io, const char *fmt, va_list ap) { char *buf; int len; len = vasprintf(&buf, fmt, ap); if (len == -1) return (-1); len = iobuf_queue(io, buf, len); free(buf); return (len); } ssize_t iobuf_write(struct iobuf *io, int fd) { struct iovec iov[IOV_MAX]; struct ioqbuf *q; int i; ssize_t n; i = 0; for (q = io->outq; q ; q = q->next) { if (i >= IOV_MAX) break; iov[i].iov_base = q->buf + q->rpos; iov[i].iov_len = q->wpos - q->rpos; i++; } n = writev(fd, iov, i); if (n == -1) { if (errno == EAGAIN || errno == EINTR) return (IOBUF_WANT_WRITE); if (errno == EPIPE) return (IOBUF_CLOSED); return (IOBUF_ERROR); } iobuf_drain(io, n); return (n); } int iobuf_flush(struct iobuf *io, int fd) { ssize_t s; while (io->queued) if ((s = iobuf_write(io, fd)) < 0) return (s); return (0); } #ifdef IO_SSL int iobuf_flush_ssl(struct iobuf *io, void *ssl) { ssize_t s; while (io->queued) if ((s = iobuf_write_ssl(io, ssl)) < 0) return (s); return (0); } ssize_t iobuf_write_ssl(struct iobuf *io, void *ssl) { struct ioqbuf *q; int r; ssize_t n; q = io->outq; n = SSL_write(ssl, q->buf + q->rpos, q->wpos - q->rpos); if (n <= 0) { switch ((r = SSL_get_error(ssl, n))) { case SSL_ERROR_WANT_READ: return (IOBUF_WANT_READ); case SSL_ERROR_WANT_WRITE: return (IOBUF_WANT_WRITE); case SSL_ERROR_ZERO_RETURN: /* connection closed */ return (IOBUF_CLOSED); case SSL_ERROR_SYSCALL: if (ERR_peek_last_error()) return (IOBUF_SSLERROR); if (r == 0) errno = EPIPE; return (IOBUF_ERROR); default: return (IOBUF_SSLERROR); } } iobuf_drain(io, n); return (n); } ssize_t iobuf_read_ssl(struct iobuf *io, void *ssl) { ssize_t n; int r; n = SSL_read(ssl, io->buf + io->wpos, iobuf_left(io)); if (n < 0) { switch ((r = SSL_get_error(ssl, n))) { case SSL_ERROR_WANT_READ: return (IOBUF_WANT_READ); case SSL_ERROR_WANT_WRITE: return (IOBUF_WANT_WRITE); case SSL_ERROR_SYSCALL: if (ERR_peek_last_error()) return (IOBUF_SSLERROR); if (r == 0) errno = EPIPE; return (IOBUF_ERROR); default: return (IOBUF_SSLERROR); } } else if (n == 0) return (IOBUF_CLOSED); io->wpos += n; return (n); } #endif /* IO_SSL */ OpenSMTPD-extras-6.6.0/api/iobuf.h000066400000000000000000000043321355240505200165710ustar00rootroot00000000000000/* $OpenBSD: iobuf.h,v 1.4 2015/01/20 17:37:54 deraadt Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include struct ioqbuf { struct ioqbuf *next; char *buf; size_t size; size_t wpos; size_t rpos; }; struct iobuf { char *buf; size_t max; size_t size; size_t wpos; size_t rpos; size_t queued; struct ioqbuf *outq; struct ioqbuf *outqlast; }; #define IOBUF_WANT_READ -1 #define IOBUF_WANT_WRITE -2 #define IOBUF_CLOSED -3 #define IOBUF_ERROR -4 #define IOBUF_SSLERROR -5 int iobuf_init(struct iobuf *, size_t, size_t); void iobuf_clear(struct iobuf *); int iobuf_extend(struct iobuf *, size_t); void iobuf_normalize(struct iobuf *); void iobuf_drop(struct iobuf *, size_t); size_t iobuf_space(struct iobuf *); size_t iobuf_len(struct iobuf *); size_t iobuf_left(struct iobuf *); char *iobuf_data(struct iobuf *); char *iobuf_getline(struct iobuf *, size_t *); ssize_t iobuf_read(struct iobuf *, int); ssize_t iobuf_read_ssl(struct iobuf *, void *); size_t iobuf_queued(struct iobuf *); void* iobuf_reserve(struct iobuf *, size_t); int iobuf_queue(struct iobuf *, const void*, size_t); int iobuf_queuev(struct iobuf *, const struct iovec *, int); int iobuf_fqueue(struct iobuf *, const char *, ...); int iobuf_vfqueue(struct iobuf *, const char *, va_list); int iobuf_flush(struct iobuf *, int); int iobuf_flush_ssl(struct iobuf *, void *); ssize_t iobuf_write(struct iobuf *, int); ssize_t iobuf_write_ssl(struct iobuf *, void *); OpenSMTPD-extras-6.6.0/api/ioev.c000066400000000000000000000434531355240505200164310ustar00rootroot00000000000000/* $OpenBSD: ioev.c,v 1.26 2016/05/16 21:43:16 millert Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include "ioev.h" #include "iobuf.h" #ifdef IO_SSL #include #include #endif enum { IO_STATE_NONE, IO_STATE_CONNECT, IO_STATE_CONNECT_SSL, IO_STATE_ACCEPT_SSL, IO_STATE_UP, IO_STATE_MAX, }; const char* io_strflags(int); const char* io_evstr(short); void _io_init(void); void io_hold(struct io *); void io_release(struct io *); void io_callback(struct io*, int); void io_dispatch(int, short, void *); void io_dispatch_connect(int, short, void *); size_t io_pending(struct io *); size_t io_queued(struct io*); void io_reset(struct io *, short, void (*)(int, short, void*)); void io_frame_enter(const char *, struct io *, int); void io_frame_leave(struct io *); #ifdef IO_SSL void ssl_error(const char *); /* XXX external */ static const char* io_ssl_error(void); void io_dispatch_accept_ssl(int, short, void *); void io_dispatch_connect_ssl(int, short, void *); void io_dispatch_read_ssl(int, short, void *); void io_dispatch_write_ssl(int, short, void *); void io_reload_ssl(struct io *io); #endif static struct io *current = NULL; static uint64_t frame = 0; static int _io_debug = 0; #define io_debug(args...) do { if (_io_debug) printf(args); } while(0) const char* io_strio(struct io *io) { static char buf[128]; char ssl[128]; ssl[0] = '\0'; #ifdef IO_SSL if (io->ssl) { (void)snprintf(ssl, sizeof ssl, " ssl=%s:%s:%d", SSL_get_version(io->ssl), SSL_get_cipher_name(io->ssl), SSL_get_cipher_bits(io->ssl, NULL)); } #endif if (io->iobuf == NULL) (void)snprintf(buf, sizeof buf, "", io, io->sock, io->timeout, io_strflags(io->flags), ssl); else (void)snprintf(buf, sizeof buf, "", io, io->sock, io->timeout, io_strflags(io->flags), ssl, io_pending(io), io_queued(io)); return (buf); } #define CASE(x) case x : return #x const char* io_strevent(int evt) { static char buf[32]; switch (evt) { CASE(IO_CONNECTED); CASE(IO_TLSREADY); CASE(IO_TLSVERIFIED); CASE(IO_DATAIN); CASE(IO_LOWAT); CASE(IO_DISCONNECTED); CASE(IO_TIMEOUT); CASE(IO_ERROR); default: (void)snprintf(buf, sizeof(buf), "IO_? %d", evt); return buf; } } void io_set_nonblocking(int fd) { int flags; if ((flags = fcntl(fd, F_GETFL)) == -1) err(1, "io_set_blocking:fcntl(F_GETFL)"); flags |= O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) err(1, "io_set_blocking:fcntl(F_SETFL)"); } void io_set_nolinger(int fd) { struct linger l; memset(&l, 0, sizeof(l)); if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) == -1) err(1, "io_set_linger:setsockopt()"); } /* * Event framing must not rely on an io pointer to refer to the "same" io * throughout the frame, because this is not always the case: * * 1) enter(addr0) -> free(addr0) -> leave(addr0) = SEGV * 2) enter(addr0) -> free(addr0) -> malloc == addr0 -> leave(addr0) = BAD! * * In both case, the problem is that the io is freed in the callback, so * the pointer becomes invalid. If that happens, the user is required to * call io_clear, so we can adapt the frame state there. */ void io_frame_enter(const char *where, struct io *io, int ev) { io_debug("\n=== %" PRIu64 " ===\n" "io_frame_enter(%s, %s, %s)\n", frame, where, io_evstr(ev), io_strio(io)); if (current) errx(1, "io_frame_enter: interleaved frames"); current = io; io_hold(io); } void io_frame_leave(struct io *io) { io_debug("io_frame_leave(%" PRIu64 ")\n", frame); if (current && current != io) errx(1, "io_frame_leave: io mismatch"); /* io has been cleared */ if (current == NULL) goto done; /* TODO: There is a possible optimization there: * In a typical half-duplex request/response scenario, * the io is waiting to read a request, and when done, it queues * the response in the output buffer and goes to write mode. * There, the write event is set and will be triggered in the next * event frame. In most case, the write call could be done * immediately as part of the last read frame, thus avoiding to go * through the event loop machinery. So, as an optimisation, we * could detect that case here and force an event dispatching. */ /* Reload the io if it has not been reset already. */ io_release(io); current = NULL; done: io_debug("=== /%" PRIu64 "\n", frame); frame += 1; } void _io_init() { static int init = 0; if (init) return; init = 1; _io_debug = getenv("IO_DEBUG") != NULL; } void io_init(struct io *io, int sock, void *arg, void(*cb)(struct io*, int), struct iobuf *iobuf) { _io_init(); memset(io, 0, sizeof *io); io->sock = sock; io->timeout = -1; io->arg = arg; io->iobuf = iobuf; io->cb = cb; if (sock != -1) io_reload(io); } void io_clear(struct io *io) { io_debug("io_clear(%p)\n", io); /* the current io is virtually dead */ if (io == current) current = NULL; #ifdef IO_SSL if (io->ssl) { SSL_free(io->ssl); io->ssl = NULL; } #endif if (event_initialized(&io->ev)) event_del(&io->ev); if (io->sock != -1) { close(io->sock); io->sock = -1; } } void io_hold(struct io *io) { io_debug("io_enter(%p)\n", io); if (io->flags & IO_HELD) errx(1, "io_hold: io is already held"); io->flags &= ~IO_RESET; io->flags |= IO_HELD; } void io_release(struct io *io) { if (!(io->flags & IO_HELD)) errx(1, "io_release: io is not held"); io->flags &= ~IO_HELD; if (!(io->flags & IO_RESET)) io_reload(io); } void io_set_timeout(struct io *io, int msec) { io_debug("io_set_timeout(%p, %d)\n", io, msec); io->timeout = msec; } void io_set_lowat(struct io *io, size_t lowat) { io_debug("io_set_lowat(%p, %zu)\n", io, lowat); io->lowat = lowat; } void io_pause(struct io *io, int dir) { io_debug("io_pause(%p, %x)\n", io, dir); io->flags |= dir & (IO_PAUSE_IN | IO_PAUSE_OUT); io_reload(io); } void io_resume(struct io *io, int dir) { io_debug("io_resume(%p, %x)\n", io, dir); io->flags &= ~(dir & (IO_PAUSE_IN | IO_PAUSE_OUT)); io_reload(io); } void io_set_read(struct io *io) { int mode; io_debug("io_set_read(%p)\n", io); mode = io->flags & IO_RW; if (!(mode == 0 || mode == IO_WRITE)) errx(1, "io_set_read(): full-duplex or reading"); io->flags &= ~IO_RW; io->flags |= IO_READ; io_reload(io); } void io_set_write(struct io *io) { int mode; io_debug("io_set_write(%p)\n", io); mode = io->flags & IO_RW; if (!(mode == 0 || mode == IO_READ)) errx(1, "io_set_write(): full-duplex or writing"); io->flags &= ~IO_RW; io->flags |= IO_WRITE; io_reload(io); } #define IO_READING(io) (((io)->flags & IO_RW) != IO_WRITE) #define IO_WRITING(io) (((io)->flags & IO_RW) != IO_READ) /* * Setup the necessary events as required by the current io state, * honouring duplex mode and i/o pauses. */ void io_reload(struct io *io) { short events; /* io will be reloaded at release time */ if (io->flags & IO_HELD) return; #ifdef IO_SSL if (io->ssl) { io_reload_ssl(io); return; } #endif io_debug("io_reload(%p)\n", io); events = 0; if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) events = EV_READ; if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io)) events |= EV_WRITE; io_reset(io, events, io_dispatch); } /* Set the requested event. */ void io_reset(struct io *io, short events, void (*dispatch)(int, short, void*)) { struct timeval tv, *ptv; io_debug("io_reset(%p, %s, %p) -> %s\n", io, io_evstr(events), dispatch, io_strio(io)); /* * Indicate that the event has already been reset so that reload * is not called on frame_leave. */ io->flags |= IO_RESET; if (event_initialized(&io->ev)) event_del(&io->ev); /* * The io is paused by the user, so we don't want the timeout to be * effective. */ if (events == 0) return; event_set(&io->ev, io->sock, events, dispatch, io); if (io->timeout >= 0) { tv.tv_sec = io->timeout / 1000; tv.tv_usec = (io->timeout % 1000) * 1000; ptv = &tv; } else ptv = NULL; event_add(&io->ev, ptv); } size_t io_pending(struct io *io) { return iobuf_len(io->iobuf); } size_t io_queued(struct io *io) { return iobuf_queued(io->iobuf); } const char* io_strflags(int flags) { static char buf[64]; buf[0] = '\0'; switch (flags & IO_RW) { case 0: (void)strlcat(buf, "rw", sizeof buf); break; case IO_READ: (void)strlcat(buf, "R", sizeof buf); break; case IO_WRITE: (void)strlcat(buf, "W", sizeof buf); break; case IO_RW: (void)strlcat(buf, "RW", sizeof buf); break; } if (flags & IO_PAUSE_IN) (void)strlcat(buf, ",F_PI", sizeof buf); if (flags & IO_PAUSE_OUT) (void)strlcat(buf, ",F_PO", sizeof buf); return buf; } const char* io_evstr(short ev) { static char buf[64]; char buf2[16]; int n; n = 0; buf[0] = '\0'; if (ev == 0) { (void)strlcat(buf, "", sizeof(buf)); return buf; } if (ev & EV_TIMEOUT) { (void)strlcat(buf, "EV_TIMEOUT", sizeof(buf)); ev &= ~EV_TIMEOUT; n++; } if (ev & EV_READ) { if (n) (void)strlcat(buf, "|", sizeof(buf)); (void)strlcat(buf, "EV_READ", sizeof(buf)); ev &= ~EV_READ; n++; } if (ev & EV_WRITE) { if (n) (void)strlcat(buf, "|", sizeof(buf)); (void)strlcat(buf, "EV_WRITE", sizeof(buf)); ev &= ~EV_WRITE; n++; } if (ev & EV_SIGNAL) { if (n) (void)strlcat(buf, "|", sizeof(buf)); (void)strlcat(buf, "EV_SIGNAL", sizeof(buf)); ev &= ~EV_SIGNAL; n++; } if (ev) { if (n) (void)strlcat(buf, "|", sizeof(buf)); (void)strlcat(buf, "EV_?=0x", sizeof(buf)); (void)snprintf(buf2, sizeof(buf2), "%hx", ev); (void)strlcat(buf, buf2, sizeof(buf)); } return buf; } void io_dispatch(int fd, short ev, void *humppa) { struct io *io = humppa; size_t w; ssize_t n; int saved_errno; io_frame_enter("io_dispatch", io, ev); if (ev == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } if (ev & EV_WRITE && (w = io_queued(io))) { if ((n = iobuf_write(io->iobuf, io->sock)) < 0) { if (n == IOBUF_WANT_WRITE) /* kqueue bug? */ goto read; if (n == IOBUF_CLOSED) io_callback(io, IO_DISCONNECTED); else { saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); } goto leave; } if (w > io->lowat && w - n <= io->lowat) io_callback(io, IO_LOWAT); } read: if (ev & EV_READ) { if ((n = iobuf_read(io->iobuf, io->sock)) < 0) { if (n == IOBUF_CLOSED) io_callback(io, IO_DISCONNECTED); else { saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); } goto leave; } if (n) io_callback(io, IO_DATAIN); } leave: io_frame_leave(io); } void io_callback(struct io *io, int evt) { io->cb(io, evt); } int io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa) { int sock, errno_save; if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) == -1) goto fail; io_set_nonblocking(sock); io_set_nolinger(sock); if (bsa && bind(sock, bsa, SA_LEN(bsa)) == -1) goto fail; if (connect(sock, sa, SA_LEN(sa)) == -1) if (errno != EINPROGRESS) goto fail; io->sock = sock; io_reset(io, EV_WRITE, io_dispatch_connect); return (sock); fail: if (sock != -1) { errno_save = errno; close(sock); errno = errno_save; io->error = strerror(errno); } return (-1); } void io_dispatch_connect(int fd, short ev, void *humppa) { struct io *io = humppa; int r, e; socklen_t sl; io_frame_enter("io_dispatch_connect", io, ev); if (ev == EV_TIMEOUT) { close(fd); io->sock = -1; io_callback(io, IO_TIMEOUT); } else { sl = sizeof(e); r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &e, &sl); if (r == -1) { warn("io_dispatch_connect: getsockopt"); e = errno; } if (e) { close(fd); io->sock = -1; io->error = strerror(e); io_callback(io, e == ETIMEDOUT ? IO_TIMEOUT : IO_ERROR); } else { io->state = IO_STATE_UP; io_callback(io, IO_CONNECTED); } } io_frame_leave(io); } #ifdef IO_SSL static const char* io_ssl_error(void) { static char buf[128]; unsigned long e; e = ERR_peek_last_error(); if (e) { ERR_error_string(e, buf); return (buf); } return ("No SSL error"); } int io_start_tls(struct io *io, void *ssl) { int mode; mode = io->flags & IO_RW; if (mode == 0 || mode == IO_RW) errx(1, "io_start_tls(): full-duplex or unset"); if (io->ssl) errx(1, "io_start_tls(): SSL already started"); io->ssl = ssl; if (SSL_set_fd(io->ssl, io->sock) == 0) { ssl_error("io_start_ssl:SSL_set_fd"); return (-1); } if (mode == IO_WRITE) { io->state = IO_STATE_CONNECT_SSL; SSL_set_connect_state(io->ssl); io_reset(io, EV_WRITE, io_dispatch_connect_ssl); } else { io->state = IO_STATE_ACCEPT_SSL; SSL_set_accept_state(io->ssl); io_reset(io, EV_READ, io_dispatch_accept_ssl); } return (0); } void io_dispatch_accept_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int e, ret; io_frame_enter("io_dispatch_accept_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } if ((ret = SSL_accept(io->ssl)) > 0) { io->state = IO_STATE_UP; io_callback(io, IO_TLSREADY); goto leave; } switch ((e = SSL_get_error(io->ssl, ret))) { case SSL_ERROR_WANT_READ: io_reset(io, EV_READ, io_dispatch_accept_ssl); break; case SSL_ERROR_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_accept_ssl); break; default: io->error = io_ssl_error(); ssl_error("io_dispatch_accept_ssl:SSL_accept"); io_callback(io, IO_ERROR); break; } leave: io_frame_leave(io); } void io_dispatch_connect_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int e, ret; io_frame_enter("io_dispatch_connect_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } if ((ret = SSL_connect(io->ssl)) > 0) { io->state = IO_STATE_UP; io_callback(io, IO_TLSREADY); goto leave; } switch ((e = SSL_get_error(io->ssl, ret))) { case SSL_ERROR_WANT_READ: io_reset(io, EV_READ, io_dispatch_connect_ssl); break; case SSL_ERROR_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_connect_ssl); break; default: io->error = io_ssl_error(); ssl_error("io_dispatch_connect_ssl:SSL_connect"); io_callback(io, IO_TLSERROR); break; } leave: io_frame_leave(io); } void io_dispatch_read_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; io_frame_enter("io_dispatch_read_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } again: switch ((n = iobuf_read_ssl(io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_read_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_read_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_read_ssl:SSL_read"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_read_ssl(...) -> r=%d\n", n); io_callback(io, IO_DATAIN); if (current == io && IO_READING(io) && SSL_pending(io->ssl)) goto again; } leave: io_frame_leave(io); } void io_dispatch_write_ssl(int fd, short event, void *humppa) { struct io *io = humppa; int n, saved_errno; size_t w2, w; io_frame_enter("io_dispatch_write_ssl", io, event); if (event == EV_TIMEOUT) { io_callback(io, IO_TIMEOUT); goto leave; } w = io_queued(io); switch ((n = iobuf_write_ssl(io->iobuf, (SSL*)io->ssl))) { case IOBUF_WANT_READ: io_reset(io, EV_READ, io_dispatch_write_ssl); break; case IOBUF_WANT_WRITE: io_reset(io, EV_WRITE, io_dispatch_write_ssl); break; case IOBUF_CLOSED: io_callback(io, IO_DISCONNECTED); break; case IOBUF_ERROR: saved_errno = errno; io->error = strerror(errno); errno = saved_errno; io_callback(io, IO_ERROR); break; case IOBUF_SSLERROR: io->error = io_ssl_error(); ssl_error("io_dispatch_write_ssl:SSL_write"); io_callback(io, IO_ERROR); break; default: io_debug("io_dispatch_write_ssl(...) -> w=%d\n", n); w2 = io_queued(io); if (w > io->lowat && w2 <= io->lowat) io_callback(io, IO_LOWAT); break; } leave: io_frame_leave(io); } void io_reload_ssl(struct io *io) { short ev = 0; void (*dispatch)(int, short, void*) = NULL; switch (io->state) { case IO_STATE_CONNECT_SSL: ev = EV_WRITE; dispatch = io_dispatch_connect_ssl; break; case IO_STATE_ACCEPT_SSL: ev = EV_READ; dispatch = io_dispatch_accept_ssl; break; case IO_STATE_UP: ev = 0; if (IO_READING(io) && !(io->flags & IO_PAUSE_IN)) { ev = EV_READ; dispatch = io_dispatch_read_ssl; } else if (IO_WRITING(io) && !(io->flags & IO_PAUSE_OUT) && io_queued(io)) { ev = EV_WRITE; dispatch = io_dispatch_write_ssl; } if (!ev) return; /* paused */ break; default: errx(1, "io_reload_ssl(): bad state"); } io_reset(io, ev, dispatch); } #endif /* IO_SSL */ OpenSMTPD-extras-6.6.0/api/ioev.h000066400000000000000000000044001355240505200164230ustar00rootroot00000000000000/* $OpenBSD: ioev.h,v 1.6 2016/03/25 15:06:58 krw Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include enum { IO_CONNECTED = 0, /* connection successful */ IO_TLSREADY, /* TLS started successfully */ IO_TLSVERIFIED, /* XXX - needs more work */ IO_TLSERROR, /* XXX - needs more work */ IO_DATAIN, /* new data in input buffer */ IO_LOWAT, /* output queue running low */ IO_DISCONNECTED, /* error? */ IO_TIMEOUT, /* error? */ IO_ERROR, /* details? */ }; #define IO_READ 0x01 #define IO_WRITE 0x02 #define IO_RW (IO_READ | IO_WRITE) #define IO_PAUSE_IN 0x04 #define IO_PAUSE_OUT 0x08 #define IO_RESET 0x10 /* internal */ #define IO_HELD 0x20 /* internal */ struct iobuf; struct io { int sock; void *arg; void (*cb)(struct io*, int); struct iobuf *iobuf; size_t lowat; int timeout; int flags; int state; struct event ev; void *ssl; const char *error; /* only valid immediately on callback */ }; void io_set_nonblocking(int); void io_set_nolinger(int); void io_init(struct io*, int, void*, void(*)(struct io*, int), struct iobuf*); void io_clear(struct io*); void io_set_read(struct io *); void io_set_write(struct io *); void io_set_timeout(struct io *, int); void io_set_lowat(struct io *, size_t); void io_pause(struct io *, int); void io_resume(struct io *, int); void io_reload(struct io *); int io_connect(struct io *, const struct sockaddr *, const struct sockaddr *); int io_start_tls(struct io *, void *); const char* io_strio(struct io *); const char* io_strevent(int); OpenSMTPD-extras-6.6.0/api/log.c000066400000000000000000000074341355240505200162470ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #define TRACE_DEBUG 0x1 static int foreground; static int verbose; void vlog(int, const char *, va_list); void logit(int, const char *, ...) __attribute__((format (printf, 2, 3))); void log_init(int n_foreground) { extern char *__progname; foreground = n_foreground; if (!foreground) openlog(__progname, LOG_PID | LOG_NDELAY, LOG_MAIL); tzset(); } void log_verbose(int v) { verbose = v; } void logit(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlog(pri, fmt, ap); va_end(ap); } void vlog(int pri, const char *fmt, va_list ap) { extern char *__progname; char *nfmt; if (foreground) { /* best effort in out of mem situations */ if (asprintf(&nfmt, "%s[%u]: %s\n", __progname, getpid(), fmt) == -1) { fprintf(stderr, "%s[%u]: ", __progname, getpid()); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } else { vfprintf(stderr, nfmt, ap); free(nfmt); } fflush(stderr); } else vsyslog(pri, fmt, ap); } void log_warn(const char *emsg, ...) { char *nfmt; va_list ap; /* best effort to even work in out of memory situations */ if (emsg == NULL) logit(LOG_CRIT, "%s", strerror(errno)); else { va_start(ap, emsg); if (asprintf(&nfmt, "%s: %s", emsg, strerror(errno)) == -1) { /* we tried it... */ vlog(LOG_CRIT, emsg, ap); logit(LOG_CRIT, "%s", strerror(errno)); } else { vlog(LOG_CRIT, nfmt, ap); free(nfmt); } va_end(ap); } } void log_warnx(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vlog(LOG_CRIT, emsg, ap); va_end(ap); } void log_info(const char *emsg, ...) { va_list ap; va_start(ap, emsg); vlog(LOG_INFO, emsg, ap); va_end(ap); } void log_debug(const char *emsg, ...) { va_list ap; if (verbose & TRACE_DEBUG) { va_start(ap, emsg); vlog(LOG_DEBUG, emsg, ap); va_end(ap); } } void log_trace(int mask, const char *emsg, ...) { va_list ap; if (verbose & mask) { va_start(ap, emsg); vlog(LOG_DEBUG, emsg, ap); va_end(ap); } } static void fatal_arg(const char *emsg, va_list ap) { #define FATALBUFSIZE 1024 static char ebuffer[FATALBUFSIZE]; if (emsg == NULL) (void)strlcpy(ebuffer, strerror(errno), sizeof ebuffer); else { if (errno) { (void)vsnprintf(ebuffer, sizeof ebuffer, emsg, ap); (void)strlcat(ebuffer, ": ", sizeof ebuffer); (void)strlcat(ebuffer, strerror(errno), sizeof ebuffer); } else (void)vsnprintf(ebuffer, sizeof ebuffer, emsg, ap); } logit(LOG_CRIT, "fatal: %s", ebuffer); } void fatal(const char *emsg, ...) { va_list ap; va_start(ap, emsg); fatal_arg(emsg, ap); va_end(ap); exit(1); } void fatalx(const char *emsg, ...) { va_list ap; errno = 0; va_start(ap, emsg); fatal_arg(emsg, ap); va_end(ap); exit(1); } OpenSMTPD-extras-6.6.0/api/log.h000066400000000000000000000025721355240505200162520ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2010 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ void log_init(int); void log_verbose(int); void log_warn(const char *, ...) __attribute__((format (printf, 1, 2))); void log_warnx(const char *, ...) __attribute__((format (printf, 1, 2))); void log_info(const char *, ...) __attribute__((format (printf, 1, 2))); void log_debug(const char *, ...) __attribute__((format (printf, 1, 2))); void log_trace(int, const char *, ...) __attribute__((format (printf, 2, 3))); void fatal(const char *, ...) __attribute__((format (printf, 1, 2))); void fatalx(const char *, ...) __attribute__((format (printf, 1, 2))); OpenSMTPD-extras-6.6.0/api/mproc.c000066400000000000000000000307211355240505200166010ustar00rootroot00000000000000/* $OpenBSD: mproc.c,v 1.8 2014/04/19 17:45:05 gilles Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* from filter_api.c */ const char *proc_name(enum smtp_proc_type); const char *imsg_to_str(int); enum smtp_proc_type smtpd_process = PROC_PONY; static void mproc_dispatch(int, short, void *); static ssize_t msgbuf_write2(struct msgbuf *); void session_socket_blockmode(int fd, enum blockmodes bm) { int flags; if ((flags = fcntl(fd, F_GETFL, 0)) == -1) fatal("fcntl F_GETFL"); if (bm == BM_NONBLOCK) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if ((flags = fcntl(fd, F_SETFL, flags)) == -1) fatal("fcntl F_SETFL"); } int mproc_fork(struct mproc *p, const char *path, char *argv[]) { int sp[2]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) < 0) return (-1); session_socket_blockmode(sp[0], BM_NONBLOCK); session_socket_blockmode(sp[1], BM_NONBLOCK); if ((p->pid = fork()) == -1) goto err; if (p->pid == 0) { /* child process */ dup2(sp[0], STDIN_FILENO); closefrom(STDERR_FILENO + 1); execv(path, argv); err(1, "execv: %s", path); } /* parent process */ close(sp[0]); mproc_init(p, sp[1]); return (0); err: log_warn("warn: Failed to start process %s, instance of %s", argv[0], path); close(sp[0]); close(sp[1]); return (-1); } void mproc_init(struct mproc *p, int fd) { imsg_init(&p->imsgbuf, fd); } void mproc_clear(struct mproc *p) { event_del(&p->ev); close(p->imsgbuf.fd); imsg_clear(&p->imsgbuf); } void mproc_enable(struct mproc *p) { if (p->enable == 0) { log_trace(TRACE_MPROC, "mproc: %s -> %s: enabled", proc_name(smtpd_process), proc_name(p->proc)); p->enable = 1; } mproc_event_add(p); } void mproc_disable(struct mproc *p) { if (p->enable == 1) { log_trace(TRACE_MPROC, "mproc: %s -> %s: disabled", proc_name(smtpd_process), proc_name(p->proc)); p->enable = 0; } mproc_event_add(p); } void mproc_event_add(struct mproc *p) { short events; if (p->enable) events = EV_READ; else events = 0; if (p->imsgbuf.w.queued) events |= EV_WRITE; if (p->events) event_del(&p->ev); p->events = events; if (events) { event_set(&p->ev, p->imsgbuf.fd, events, mproc_dispatch, p); event_add(&p->ev, NULL); } } static void mproc_dispatch(int fd, short event, void *arg) { struct mproc *p = arg; struct imsg imsg; ssize_t n; p->events = 0; if (event & EV_READ) { if ((n = imsg_read(&p->imsgbuf)) == -1) { log_warn("warn: %s -> %s: imsg_read", proc_name(smtpd_process), p->name); if (errno != EAGAIN) fatal("exiting"); } else if (n == 0) { /* this pipe is dead, so remove the event handler */ if (smtpd_process != PROC_CONTROL || p->proc != PROC_CLIENT) log_warnx("warn: %s -> %s: pipe closed", proc_name(smtpd_process), p->name); p->handler(p, NULL); return; } else p->bytes_in += n; } if (event & EV_WRITE) { n = msgbuf_write2(&p->imsgbuf.w); if (n == 0 || (n == -1 && errno != EAGAIN)) { /* this pipe is dead, so remove the event handler */ if (smtpd_process != PROC_CONTROL || p->proc != PROC_CLIENT) log_warnx("warn: %s -> %s: pipe closed", proc_name(smtpd_process), p->name); p->handler(p, NULL); return; } else if (n != -1) { p->bytes_out += n; p->bytes_queued -= n; } } for (;;) { if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) { log_warn("fatal: %s: error in imsg_get for %s", proc_name(smtpd_process), p->name); fatalx(NULL); } if (n == 0) break; p->msg_in += 1; p->handler(p, &imsg); imsg_free(&imsg); } #if 0 if (smtpd_process == PROC_QUEUE) queue_flow_control(); #endif mproc_event_add(p); } /* XXX msgbuf_write() should return n ... */ static ssize_t msgbuf_write2(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; if (buf->fd != -1) break; } msg.msg_iov = iov; msg.msg_iovlen = i; if (buf != NULL && buf->fd != -1) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = buf->fd; } again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf != NULL && buf->fd != -1) { close(buf->fd); buf->fd = -1; } msgbuf_drain(msgbuf, n); return (n); } void m_forward(struct mproc *p, struct imsg *imsg) { imsg_compose(&p->imsgbuf, imsg->hdr.type, imsg->hdr.peerid, imsg->hdr.pid, imsg->fd, imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (forward)", proc_name(smtpd_process), proc_name(p->proc), imsg->hdr.len - sizeof(imsg->hdr), imsg_to_str(imsg->hdr.type)); p->msg_out += 1; p->bytes_queued += imsg->hdr.len; if (p->bytes_queued > p->bytes_queued_max) p->bytes_queued_max = p->bytes_queued; mproc_event_add(p); } void m_compose(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, void *data, size_t len) { imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len); log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", proc_name(smtpd_process), proc_name(p->proc), len, imsg_to_str(type)); p->msg_out += 1; p->bytes_queued += len + IMSG_HEADER_SIZE; if (p->bytes_queued > p->bytes_queued_max) p->bytes_queued_max = p->bytes_queued; mproc_event_add(p); } void m_composev(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd, const struct iovec *iov, int n) { size_t len; int i; imsg_composev(&p->imsgbuf, type, peerid, pid, fd, iov, n); len = 0; for (i = 0; i < n; i++) len += iov[i].iov_len; p->msg_out += 1; p->bytes_queued += IMSG_HEADER_SIZE + len; if (p->bytes_queued > p->bytes_queued_max) p->bytes_queued_max = p->bytes_queued; log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", proc_name(smtpd_process), proc_name(p->proc), len, imsg_to_str(type)); mproc_event_add(p); } void m_create(struct mproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd) { if (p->m_buf == NULL) { p->m_alloc = 128; log_trace(TRACE_MPROC, "mproc: %s -> %s: allocating %zu", proc_name(smtpd_process), proc_name(p->proc), p->m_alloc); p->m_buf = malloc(p->m_alloc); if (p->m_buf == NULL) fatal("warn: m_create: malloc"); } p->m_pos = 0; p->m_type = type; p->m_peerid = peerid; p->m_pid = pid; p->m_fd = fd; } void m_add(struct mproc *p, const void *data, size_t len) { size_t alloc; void *tmp; if (p->m_pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE) { log_warnx("warn: message to large"); fatal(NULL); } alloc = p->m_alloc; while (p->m_pos + len > alloc) alloc *= 2; if (alloc != p->m_alloc) { log_trace(TRACE_MPROC, "mproc: %s -> %s: realloc %zu -> %zu", proc_name(smtpd_process), proc_name(p->proc), p->m_alloc, alloc); tmp = realloc(p->m_buf, alloc); if (tmp == NULL) fatal("realloc"); p->m_alloc = alloc; p->m_buf = tmp; } memmove(p->m_buf + p->m_pos, data, len); p->m_pos += len; } void m_close(struct mproc *p) { if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, p->m_buf, p->m_pos) == -1) fatal("imsg_compose"); log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s", proc_name(smtpd_process), proc_name(p->proc), p->m_pos, imsg_to_str(p->m_type)); p->msg_out += 1; p->bytes_queued += p->m_pos + IMSG_HEADER_SIZE; if (p->bytes_queued > p->bytes_queued_max) p->bytes_queued_max = p->bytes_queued; mproc_event_add(p); } void m_flush(struct mproc *p) { if (imsg_compose(&p->imsgbuf, p->m_type, p->m_peerid, p->m_pid, p->m_fd, p->m_buf, p->m_pos) == -1) fatal("imsg_compose"); log_trace(TRACE_MPROC, "mproc: %s -> %s : %zu %s (flush)", proc_name(smtpd_process), proc_name(p->proc), p->m_pos, imsg_to_str(p->m_type)); p->msg_out += 1; p->m_pos = 0; imsg_flush(&p->imsgbuf); } static struct imsg * current; static void m_error(const char *error) { char buf[512]; (void)snprintf(buf, sizeof buf, "%s: %s: %s", proc_name(smtpd_process), imsg_to_str(current->hdr.type), error); fatalx("%s", buf); } void m_msg(struct msg *m, struct imsg *imsg) { current = imsg; m->pos = imsg->data; m->end = m->pos + (imsg->hdr.len - sizeof(imsg->hdr)); } void m_end(struct msg *m) { if (m->pos != m->end) m_error("not at msg end"); } int m_is_eom(struct msg *m) { return (m->pos == m->end); } static inline void m_get(struct msg *m, void *dst, size_t sz) { if (sz > MAX_IMSGSIZE || m->end - m->pos < (ssize_t)sz) m_error("msg too short"); memmove(dst, m->pos, sz); m->pos += sz; } void m_add_int(struct mproc *m, int v) { m_add(m, &v, sizeof(v)); }; void m_add_u32(struct mproc *m, uint32_t u32) { m_add(m, &u32, sizeof(u32)); }; void m_add_size(struct mproc *m, size_t sz) { m_add(m, &sz, sizeof(sz)); }; void m_add_time(struct mproc *m, time_t v) { m_add(m, &v, sizeof(v)); }; void m_add_string(struct mproc *m, const char *v) { m_add(m, v, strlen(v) + 1); }; void m_add_data(struct mproc *m, const void *v, size_t len) { m_add_size(m, len); m_add(m, v, len); }; void m_add_id(struct mproc *m, uint64_t v) { m_add(m, &v, sizeof(v)); } void m_add_evpid(struct mproc *m, uint64_t v) { m_add(m, &v, sizeof(v)); } void m_add_msgid(struct mproc *m, uint32_t v) { m_add(m, &v, sizeof(v)); } void m_add_sockaddr(struct mproc *m, const struct sockaddr *sa) { m_add_size(m, SA_LEN(sa)); m_add(m, sa, SA_LEN(sa)); } void m_add_mailaddr(struct mproc *m, const struct mailaddr *maddr) { m_add(m, maddr, sizeof(*maddr)); } void m_get_int(struct msg *m, int *i) { m_get(m, i, sizeof(*i)); } void m_get_u32(struct msg *m, uint32_t *u32) { m_get(m, u32, sizeof(*u32)); } void m_get_size(struct msg *m, size_t *sz) { m_get(m, sz, sizeof(*sz)); } void m_get_time(struct msg *m, time_t *t) { m_get(m, t, sizeof(*t)); } void m_get_string(struct msg *m, const char **s) { uint8_t *end; if (m->pos >= m->end) m_error("msg too short"); end = memchr(m->pos, 0, m->end - m->pos); if (end == NULL) m_error("unterminated string"); *s = m->pos; m->pos = end + 1; } void m_get_data(struct msg *m, const void **data, size_t *sz) { m_get_size(m, sz); if (m->pos + *sz > m->end) m_error("msg too short"); *data = m->pos; m->pos += *sz; } void m_get_evpid(struct msg *m, uint64_t *evpid) { m_get(m, evpid, sizeof(*evpid)); } void m_get_msgid(struct msg *m, uint32_t *msgid) { m_get(m, msgid, sizeof(*msgid)); } void m_get_id(struct msg *m, uint64_t *id) { m_get(m, id, sizeof(*id)); } void m_get_sockaddr(struct msg *m, struct sockaddr *sa) { size_t len; m_get_size(m, &len); m_get(m, sa, len); } void m_get_mailaddr(struct msg *m, struct mailaddr *maddr) { m_get(m, maddr, sizeof(*maddr)); } OpenSMTPD-extras-6.6.0/api/queue_api.c000066400000000000000000000230031355240505200174310ustar00rootroot00000000000000/* $OpenBSD: queue_api.c,v 1.4 2014/04/19 17:47:40 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include /* needed for setgroups */ #include #include #include #include #include static int (*handler_close)(void); static int (*handler_message_create)(uint32_t *); static int (*handler_message_commit)(uint32_t, const char *); static int (*handler_message_delete)(uint32_t); static int (*handler_message_fd_r)(uint32_t); static int (*handler_message_corrupt)(uint32_t); static int (*handler_message_uncorrupt)(uint32_t); static int (*handler_envelope_create)(uint32_t, const char *, size_t, uint64_t *); static int (*handler_envelope_delete)(uint64_t); static int (*handler_envelope_update)(uint64_t, const char *, size_t); static int (*handler_envelope_load)(uint64_t, char *, size_t); static int (*handler_envelope_walk)(uint64_t *, char *, size_t); static int (*handler_message_walk)(uint64_t *, char *, size_t, uint32_t, int *, void **); static struct imsgbuf ibuf; static struct imsg imsg; static size_t rlen; static char *rdata; static struct ibuf *buf; static const char *rootpath = PATH_SPOOL; static const char *user = SMTPD_QUEUE_USER; static void queue_msg_get(void *dst, size_t len) { if (len > rlen) { log_warnx("warn: queue-proc: bad msg len"); fatalx("queue-proc: exiting"); } if (len == 0) return; if (dst) memmove(dst, rdata, len); rlen -= len; rdata += len; } static void queue_msg_end(void) { if (rlen) { log_warnx("warn: queue-proc: bogus data"); fatalx("queue-proc: exiting"); } imsg_free(&imsg); } static void queue_msg_add(const void *data, size_t len) { if (buf == NULL) buf = imsg_create(&ibuf, PROC_QUEUE_OK, 0, 0, 1024); if (buf == NULL) { log_warnx("warn: queue-api: imsg_create failed"); fatalx("queue-api: exiting"); } if (imsg_add(buf, data, len) == -1) { log_warnx("warn: queue-api: imsg_add failed"); fatalx("queue-api: exiting"); } } static void queue_msg_close(void) { imsg_close(&ibuf, buf); buf = NULL; } static void queue_msg_dispatch(void) { uint64_t evpid; uint32_t msgid, version; size_t n, m; char buffer[8192], path[PATH_MAX]; int r, fd; FILE *ifile, *ofile; switch (imsg.hdr.type) { case PROC_QUEUE_INIT: queue_msg_get(&version, sizeof(version)); queue_msg_end(); if (version != PROC_QUEUE_API_VERSION) { log_warnx("warn: queue-api: bad API version"); fatalx("queue-api: exiting"); } imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, NULL, 0); break; case PROC_QUEUE_CLOSE: queue_msg_end(); if (handler_close) r = handler_close(); else r = 1; imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_MESSAGE_CREATE: queue_msg_end(); r = handler_message_create(&msgid); queue_msg_add(&r, sizeof(r)); if (r == 1) queue_msg_add(&msgid, sizeof(msgid)); queue_msg_close(); break; case PROC_QUEUE_MESSAGE_DELETE: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); r = handler_message_delete(msgid); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_MESSAGE_COMMIT: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); /* XXX needs more love */ r = -1; (void)snprintf(path, sizeof path, "/tmp/message.XXXXXXXXXX"); fd = mkstemp(path); if (fd == -1) { log_warn("warn: queue-api: mkstemp"); } else { ifile = fdopen(imsg.fd, "r"); ofile = fdopen(fd, "w"); m = n = 0; if (ifile && ofile) { while (!feof(ifile)) { n = fread(buffer, 1, sizeof(buffer), ifile); m = fwrite(buffer, 1, n, ofile); if (m != n) break; fflush(ofile); } if (m != n) r = 0; else r = handler_message_commit(msgid, path); } if (ifile) fclose(ifile); if (ofile) fclose(ofile); } imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_MESSAGE_FD_R: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); fd = handler_message_fd_r(msgid); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, fd, NULL, 0); break; case PROC_QUEUE_MESSAGE_CORRUPT: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); r = handler_message_corrupt(msgid); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_MESSAGE_UNCORRUPT: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); r = handler_message_uncorrupt(msgid); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_ENVELOPE_CREATE: queue_msg_get(&msgid, sizeof(msgid)); r = handler_envelope_create(msgid, rdata, rlen, &evpid); queue_msg_get(NULL, rlen); queue_msg_end(); queue_msg_add(&r, sizeof(r)); if (r == 1) queue_msg_add(&evpid, sizeof(evpid)); queue_msg_close(); break; case PROC_QUEUE_ENVELOPE_DELETE: queue_msg_get(&evpid, sizeof(evpid)); queue_msg_end(); r = handler_envelope_delete(evpid); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_ENVELOPE_LOAD: queue_msg_get(&evpid, sizeof(evpid)); queue_msg_end(); r = handler_envelope_load(evpid, buffer, sizeof(buffer)); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, buffer, r); break; case PROC_QUEUE_ENVELOPE_UPDATE: queue_msg_get(&evpid, sizeof(evpid)); r = handler_envelope_update(evpid, rdata, rlen); queue_msg_get(NULL, rlen); queue_msg_end(); imsg_compose(&ibuf, PROC_QUEUE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_QUEUE_ENVELOPE_WALK: queue_msg_end(); r = handler_envelope_walk(&evpid, buffer, sizeof(buffer)); queue_msg_add(&r, sizeof(r)); if (r > 0) { queue_msg_add(&evpid, sizeof(evpid)); queue_msg_add(buffer, r); } queue_msg_close(); break; case PROC_QUEUE_MESSAGE_WALK: queue_msg_get(&msgid, sizeof(msgid)); queue_msg_end(); r = handler_message_walk(&evpid, buffer, sizeof(buffer), msgid, NULL, NULL); queue_msg_add(&r, sizeof(r)); if (r > 0) { queue_msg_add(&evpid, sizeof(evpid)); queue_msg_add(buffer, r); } queue_msg_close(); break; default: log_warnx("warn: queue-api: bad message %d", imsg.hdr.type); fatalx("queue-api: exiting"); } } void queue_api_on_close(int(*cb)(void)) { handler_close = cb; } void queue_api_on_message_create(int(*cb)(uint32_t *)) { handler_message_create = cb; } void queue_api_on_message_commit(int(*cb)(uint32_t, const char *)) { handler_message_commit = cb; } void queue_api_on_message_delete(int(*cb)(uint32_t)) { handler_message_delete = cb; } void queue_api_on_message_fd_r(int(*cb)(uint32_t)) { handler_message_fd_r = cb; } void queue_api_on_message_corrupt(int(*cb)(uint32_t)) { handler_message_corrupt = cb; } void queue_api_on_message_uncorrupt(int(*cb)(uint32_t)) { handler_message_uncorrupt = cb; } void queue_api_on_envelope_create(int(*cb)(uint32_t, const char *, size_t, uint64_t *)) { handler_envelope_create = cb; } void queue_api_on_envelope_delete(int(*cb)(uint64_t)) { handler_envelope_delete = cb; } void queue_api_on_envelope_update(int(*cb)(uint64_t, const char *, size_t)) { handler_envelope_update = cb; } void queue_api_on_envelope_load(int(*cb)(uint64_t, char *, size_t)) { handler_envelope_load = cb; } void queue_api_on_envelope_walk(int(*cb)(uint64_t *, char *, size_t)) { handler_envelope_walk = cb; } void queue_api_on_message_walk(int(*cb)(uint64_t *, char *, size_t, uint32_t, int *, void **)) { handler_message_walk = cb; } void queue_api_no_chroot(void) { rootpath = NULL; } void queue_api_set_chroot(const char *path) { rootpath = path; } void queue_api_set_user(const char *username) { user = username; } int queue_api_dispatch(void) { struct passwd *pw = NULL; ssize_t n; if (user) { pw = getpwnam(user); if (pw == NULL) { log_warn("queue-api: getpwnam"); fatalx("queue-api: exiting"); } } if (rootpath) { if (chroot(rootpath) == -1) { log_warn("queue-api: chroot"); fatalx("queue-api: exiting"); } if (chdir("/") == -1) { log_warn("queue-api: chdir"); fatalx("queue-api: exiting"); } } if (pw && (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))) { log_warn("queue-api: cannot drop privileges"); fatalx("queue-api: exiting"); } imsg_init(&ibuf, 0); while (1) { n = imsg_get(&ibuf, &imsg); if (n == -1) { log_warn("warn: queue-api: imsg_get"); break; } if (n) { rdata = imsg.data; rlen = imsg.hdr.len - IMSG_HEADER_SIZE; queue_msg_dispatch(); imsg_flush(&ibuf); continue; } n = imsg_read(&ibuf); if (n == -1) { log_warn("warn: queue-api: imsg_read"); break; } if (n == 0) { log_warnx("warn: queue-api: pipe closed"); break; } } return (1); } OpenSMTPD-extras-6.6.0/api/queue_utils.c000066400000000000000000000034061355240505200200250ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include uint32_t queue_generate_msgid(void) { uint32_t msgid; while ((msgid = arc4random_uniform(0xffffffff)) == 0) ; return msgid; } uint64_t queue_generate_evpid(uint32_t msgid) { uint32_t rnd; uint64_t evpid; while ((rnd = arc4random_uniform(0xffffffff)) == 0) ; evpid = msgid; evpid <<= 32; evpid |= rnd; return evpid; } int mktmpfile(void) { static char *tempdir = "/temporary"; char path[PATH_MAX]; int fd; mode_t omode; if (snprintf(path, sizeof(path), "%s/smtpd.XXXXXXXXXX", tempdir) >= (int)sizeof(path)) { log_warnx("warn: queue-api: tempdir too large \"%s\"", tempdir); return (-1); } omode = umask(7077); if ((fd = mkstemp(path)) == -1) { log_warnx("warn: queue-api: cannot create temporary file \"%s\"", path); } umask(omode); unlink(path); return (fd); } OpenSMTPD-extras-6.6.0/api/rfc2822.c000066400000000000000000000160071355240505200165520ustar00rootroot00000000000000/* $OpenBSD: rfc2822.c,v 1.7 2016/02/04 22:35:17 eric Exp $ */ /* * Copyright (c) 2014 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "rfc2822.h" /* default no-op callbacks */ static void hdr_dflt_cb(const struct rfc2822_header *hdr, void *arg) {} static void body_dflt_cb(const char *line, void *arg) {} static void header_reset(struct rfc2822_header *hdr) { struct rfc2822_line *line; while ((line = TAILQ_FIRST(&hdr->lines))) { TAILQ_REMOVE(&hdr->lines, line, next); free(line); } } static void header_callback(struct rfc2822_parser *rp) { struct rfc2822_hdr_cb *hdr_cb; struct rfc2822_hdr_miss_cb *hdr_miss_cb; TAILQ_FOREACH(hdr_cb, &rp->hdr_cb, next) if (strcasecmp(hdr_cb->name, rp->header.name) == 0) { hdr_cb->func(&rp->header, hdr_cb->arg); goto end; } rp->hdr_dflt_cb.func(&rp->header, rp->hdr_dflt_cb.arg); end: TAILQ_FOREACH(hdr_miss_cb, &rp->hdr_miss_cb, next) if (strcasecmp(hdr_miss_cb->name, rp->header.name) == 0) break; if (hdr_miss_cb) TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next); free(hdr_miss_cb); header_reset(&rp->header); rp->in_hdr = 0; return; } static void missing_headers_callback(struct rfc2822_parser *rp) { struct rfc2822_hdr_miss_cb *hdr_miss_cb; while ((hdr_miss_cb = TAILQ_FIRST(&rp->hdr_miss_cb))) { hdr_miss_cb->func(hdr_miss_cb->name, hdr_miss_cb->arg); TAILQ_REMOVE(&rp->hdr_miss_cb, hdr_miss_cb, next); free(hdr_miss_cb); } } static void eoh_callback(struct rfc2822_parser *rp) { if (rp->eoh_cb.func) rp->eoh_cb.func(rp->eoh_cb.arg); } static void body_callback(struct rfc2822_parser *rp, const char *line) { rp->body_line_cb.func(line, rp->body_line_cb.arg); } static int parser_feed_header(struct rfc2822_parser *rp, char *line) { struct rfc2822_line *hdrline; char *pos; /* new header */ if (!isspace(*line) && *line != '\0') { rp->in_hdr = 1; if ((pos = strchr(line, ':')) == NULL) return 0; memset(rp->header.name, 0, sizeof rp->header.name); (void)memcpy(rp->header.name, line, pos - line); if (isspace(*(pos + 1))) return parser_feed_header(rp, pos + 1); else { *pos = ' '; return parser_feed_header(rp, pos); } } /* continuation */ if (!rp->in_hdr) return 0; /* append line to header */ if ((hdrline = calloc(1, sizeof *hdrline)) == NULL) return -1; (void)strlcpy(hdrline->buffer, line, sizeof hdrline->buffer); TAILQ_INSERT_TAIL(&rp->header.lines, hdrline, next); return 1; } static int parser_feed_body(struct rfc2822_parser *rp, const char *line) { /* for now, we only support per-line callbacks */ body_callback(rp, line); return 1; } void rfc2822_parser_init(struct rfc2822_parser *rp) { memset(rp, 0, sizeof *rp); TAILQ_INIT(&rp->hdr_cb); TAILQ_INIT(&rp->hdr_miss_cb); TAILQ_INIT(&rp->header.lines); rfc2822_header_default_callback(rp, hdr_dflt_cb, NULL); rfc2822_body_callback(rp, body_dflt_cb, NULL); } void rfc2822_parser_flush(struct rfc2822_parser *rp) { if (!rp->in_hdrs) return; header_callback(rp); } void rfc2822_parser_reset(struct rfc2822_parser *rp) { header_reset(&rp->header); rp->in_hdrs = 1; } void rfc2822_parser_release(struct rfc2822_parser *rp) { struct rfc2822_hdr_cb *cb; struct rfc2822_hdr_miss_cb *mcb; rfc2822_parser_reset(rp); while ((cb = TAILQ_FIRST(&rp->hdr_cb))) { TAILQ_REMOVE(&rp->hdr_cb, cb, next); free(cb); } while ((mcb = TAILQ_FIRST(&rp->hdr_miss_cb))) { TAILQ_REMOVE(&rp->hdr_miss_cb, mcb, next); free(mcb); } } int rfc2822_parser_feed(struct rfc2822_parser *rp, const char *line) { char buffer[RFC2822_MAX_LINE_SIZE+1]; /* in header and line is not a continuation, execute callback */ if (rp->in_hdr && (*line == '\0' || !isspace(*line))) header_callback(rp); /* no longer in headers */ if (*line == '\0') { if (rp->in_hdrs) { missing_headers_callback(rp); eoh_callback(rp); } rp->in_hdrs = 0; } if (rp->in_hdrs) { /* line exceeds RFC maximum size requirement */ if (strlcpy(buffer, line, sizeof buffer) >= sizeof buffer) return 0; return parser_feed_header(rp, buffer); } /* don't enforce line max length on content, too many MUA break */ return parser_feed_body(rp, line); } int rfc2822_header_callback(struct rfc2822_parser *rp, const char *header, void (*func)(const struct rfc2822_header *, void *), void *arg) { struct rfc2822_hdr_cb *cb; struct rfc2822_hdr_cb *cb_tmp; char buffer[RFC2822_MAX_LINE_SIZE+1]; /* line exceeds RFC maximum size requirement */ if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer) return 0; TAILQ_FOREACH_SAFE(cb, &rp->hdr_cb, next, cb_tmp) { if (strcasecmp(cb->name, buffer) == 0) { TAILQ_REMOVE(&rp->hdr_cb, cb, next); free(cb); } } if ((cb = calloc(1, sizeof *cb)) == NULL) return -1; (void)strlcpy(cb->name, buffer, sizeof cb->name); cb->func = func; cb->arg = arg; TAILQ_INSERT_TAIL(&rp->hdr_cb, cb, next); return 1; } int rfc2822_missing_header_callback(struct rfc2822_parser *rp, const char *header, void (*func)(const char *, void *), void *arg) { struct rfc2822_hdr_miss_cb *cb; struct rfc2822_hdr_miss_cb *cb_tmp; char buffer[RFC2822_MAX_LINE_SIZE+1]; /* line exceeds RFC maximum size requirement */ if (strlcpy(buffer, header, sizeof buffer) >= sizeof buffer) return 0; TAILQ_FOREACH_SAFE(cb, &rp->hdr_miss_cb, next, cb_tmp) { if (strcasecmp(cb->name, buffer) == 0) { TAILQ_REMOVE(&rp->hdr_miss_cb, cb, next); free(cb); } } if ((cb = calloc(1, sizeof *cb)) == NULL) return -1; (void)strlcpy(cb->name, buffer, sizeof cb->name); cb->func = func; cb->arg = arg; TAILQ_INSERT_TAIL(&rp->hdr_miss_cb, cb, next); return 1; } void rfc2822_header_default_callback(struct rfc2822_parser *rp, void (*func)(const struct rfc2822_header *, void *), void *arg) { struct rfc2822_hdr_cb *cb; cb = &rp->hdr_dflt_cb; cb->func = func; cb->arg = arg; } void rfc2822_body_callback(struct rfc2822_parser *rp, void (*func)(const char *, void *), void *arg) { struct rfc2822_line_cb *cb; cb = &rp->body_line_cb; cb->func = func; cb->arg = arg; } void rfc2822_eoh_callback(struct rfc2822_parser *rp, void (*func)(void *), void *arg) { struct rfc2822_eoh_cb *cb; cb = &rp->eoh_cb; cb->func = func; cb->arg = arg; } OpenSMTPD-extras-6.6.0/api/rfc2822.h000066400000000000000000000056131355240505200165600ustar00rootroot00000000000000/* $OpenBSD: rfc2822.h,v 1.4 2015/11/05 08:55:09 gilles Exp $ */ /* * Copyright (c) 2014 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _RFC2822_H_ #define _RFC2822_H_ #ifdef HAVE_STDINT_H #include #endif #define RFC2822_MAX_LINE_SIZE 4096 struct rfc2822_line { TAILQ_ENTRY(rfc2822_line) next; char buffer[RFC2822_MAX_LINE_SIZE+1]; }; struct rfc2822_header { char name[RFC2822_MAX_LINE_SIZE+1]; TAILQ_HEAD(, rfc2822_line) lines; }; struct rfc2822_hdr_cb { TAILQ_ENTRY(rfc2822_hdr_cb) next; char name[RFC2822_MAX_LINE_SIZE+1]; void (*func)(const struct rfc2822_header *, void *); void *arg; }; struct rfc2822_hdr_miss_cb { TAILQ_ENTRY(rfc2822_hdr_miss_cb) next; char name[RFC2822_MAX_LINE_SIZE+1]; void (*func)(const char *, void *); void *arg; }; struct rfc2822_line_cb { void (*func)(const char *, void *); void *arg; }; struct rfc2822_eoh_cb { void (*func)(void *); void *arg; }; struct rfc2822_parser { uint8_t in_hdrs; /* in headers */ TAILQ_HEAD(hdr_cb, rfc2822_hdr_cb) hdr_cb; TAILQ_HEAD(hdr_miss_cb, rfc2822_hdr_miss_cb) hdr_miss_cb; uint8_t in_hdr; /* in specific header */ struct rfc2822_header header; struct rfc2822_hdr_cb hdr_dflt_cb; struct rfc2822_eoh_cb eoh_cb; struct rfc2822_line_cb body_line_cb; }; void rfc2822_parser_init(struct rfc2822_parser *); int rfc2822_parser_feed(struct rfc2822_parser *, const char *); void rfc2822_parser_flush(struct rfc2822_parser *); void rfc2822_parser_reset(struct rfc2822_parser *); void rfc2822_parser_release(struct rfc2822_parser *); int rfc2822_header_callback(struct rfc2822_parser *, const char *, void (*)(const struct rfc2822_header *, void *), void *); int rfc2822_missing_header_callback(struct rfc2822_parser *, const char *, void (*)(const char *, void *), void *); void rfc2822_header_default_callback(struct rfc2822_parser *, void (*)(const struct rfc2822_header *, void *), void *); void rfc2822_body_callback(struct rfc2822_parser *, void (*)(const char *, void *), void *); void rfc2822_eoh_callback(struct rfc2822_parser *, void (*)(void *), void *); #endif OpenSMTPD-extras-6.6.0/api/scheduler_api.c000066400000000000000000000250641355240505200202740ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include /* needed for setgroups */ #include #include #include static int (*handler_init)(void); static int (*handler_insert)(struct scheduler_info *); static size_t (*handler_commit)(uint32_t); static size_t (*handler_rollback)(uint32_t); static int (*handler_update)(struct scheduler_info *); static int (*handler_delete)(uint64_t); static int (*handler_hold)(uint64_t, uint64_t); static int (*handler_release)(int, uint64_t, int); static int (*handler_batch)(int, int *, size_t *, uint64_t *, int *); static size_t (*handler_messages)(uint32_t, uint32_t *, size_t); static size_t (*handler_envelopes)(uint64_t, struct evpstate *, size_t); static int (*handler_schedule)(uint64_t); static int (*handler_remove)(uint64_t); static int (*handler_suspend)(uint64_t); static int (*handler_resume)(uint64_t); #define MAX_BATCH_SIZE 1024 static struct imsgbuf ibuf; static struct imsg imsg; static size_t rlen; static char *rdata; static struct ibuf *buf; static const char *rootpath = PATH_CHROOT; static const char *user = SMTPD_USER; static void scheduler_msg_get(void *dst, size_t len) { if (len > rlen) { log_warnx("warn: scheduler-proc: bad msg len"); fatalx("scheduler-proc: exiting"); } if (len == 0) return; if (dst) memmove(dst, rdata, len); rlen -= len; rdata += len; } static void scheduler_msg_end(void) { if (rlen) { log_warnx("warn: scheduler-proc: bogus data"); fatalx("scheduler-proc: exiting"); } imsg_free(&imsg); } static void scheduler_msg_add(const void *data, size_t len) { if (buf == NULL) buf = imsg_create(&ibuf, PROC_SCHEDULER_OK, 0, 0, 1024); if (buf == NULL) { log_warnx("warn: table-api: imsg_create failed"); fatalx("table-api: exiting"); } if (imsg_add(buf, data, len) == -1) { log_warnx("warn: table-api: imsg_add failed"); fatalx("table-api: exiting"); } } static void scheduler_msg_close(void) { imsg_close(&ibuf, buf); buf = NULL; } static void scheduler_msg_dispatch(void) { size_t n, sz, count; struct evpstate evpstates[MAX_BATCH_SIZE]; uint64_t evpid, evpids[MAX_BATCH_SIZE], u64; uint32_t msgids[MAX_BATCH_SIZE], version, msgid; struct scheduler_info info; int typemask, r, type, types[MAX_BATCH_SIZE]; int delay; switch (imsg.hdr.type) { case PROC_SCHEDULER_INIT: log_debug("scheduler-api: PROC_SCHEDULER_INIT"); scheduler_msg_get(&version, sizeof(version)); scheduler_msg_end(); if (version != PROC_SCHEDULER_API_VERSION) { log_warnx("warn: scheduler-api: bad API version"); fatalx("scheduler-api: exiting"); } r = handler_init(); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_INSERT: log_debug("scheduler-api: PROC_SCHEDULER_INSERT"); scheduler_msg_get(&info, sizeof(info)); scheduler_msg_end(); r = handler_insert(&info); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_COMMIT: log_debug("scheduler-api: PROC_SCHEDULER_COMMIT"); scheduler_msg_get(&msgid, sizeof(msgid)); scheduler_msg_end(); n = handler_commit(msgid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &n, sizeof(n)); break; case PROC_SCHEDULER_ROLLBACK: log_debug("scheduler-api: PROC_SCHEDULER_ROLLBACK"); scheduler_msg_get(&msgid, sizeof(msgid)); scheduler_msg_end(); n = handler_rollback(msgid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &n, sizeof(n)); break; case PROC_SCHEDULER_UPDATE: log_debug("scheduler-api: PROC_SCHEDULER_UPDATE"); scheduler_msg_get(&info, sizeof(info)); scheduler_msg_end(); r = handler_update(&info); scheduler_msg_add(&r, sizeof(r)); if (r == 1) scheduler_msg_add(&info, sizeof(info)); scheduler_msg_close(); break; case PROC_SCHEDULER_DELETE: log_debug("scheduler-api: PROC_SCHEDULER_DELETE"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_end(); r = handler_delete(evpid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_HOLD: log_debug("scheduler-api: PROC_SCHEDULER_HOLD"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_get(&u64, sizeof(u64)); scheduler_msg_end(); r = handler_hold(evpid, u64); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_RELEASE: log_debug("scheduler-api: PROC_SCHEDULER_RELEASE"); scheduler_msg_get(&type, sizeof(type)); scheduler_msg_get(&u64, sizeof(u64)); scheduler_msg_get(&r, sizeof(r)); scheduler_msg_end(); r = handler_release(type, u64, r); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_BATCH: log_debug("scheduler-api: PROC_SCHEDULER_BATCH"); scheduler_msg_get(&typemask, sizeof(typemask)); scheduler_msg_get(&count, sizeof(count)); scheduler_msg_end(); if (count > MAX_BATCH_SIZE) count = MAX_BATCH_SIZE; r = handler_batch(typemask, &delay, &count, evpids, types); scheduler_msg_add(&r, sizeof(r)); scheduler_msg_add(&delay, sizeof(delay)); scheduler_msg_add(&count, sizeof(count)); if (r > 0) { scheduler_msg_add(evpids, sizeof(*evpids) * count); scheduler_msg_add(types, sizeof(*types) * count); } scheduler_msg_close(); break; case PROC_SCHEDULER_MESSAGES: log_debug("scheduler-api: PROC_SCHEDULER_MESSAGES"); scheduler_msg_get(&msgid, sizeof(msgid)); scheduler_msg_get(&sz, sizeof(sz)); scheduler_msg_end(); if (sz > MAX_BATCH_SIZE) sz = MAX_BATCH_SIZE; n = handler_messages(msgid, msgids, sz); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, msgids, n * sizeof(*msgids)); break; case PROC_SCHEDULER_ENVELOPES: log_debug("scheduler-api: PROC_SCHEDULER_ENVELOPES"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_get(&sz, sizeof(sz)); scheduler_msg_end(); if (sz > MAX_BATCH_SIZE) sz = MAX_BATCH_SIZE; n = handler_envelopes(evpid, evpstates, sz); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, evpstates, n * sizeof(*evpstates)); break; case PROC_SCHEDULER_SCHEDULE: log_debug("scheduler-api: PROC_SCHEDULER_SCHEDULE"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_end(); r = handler_schedule(evpid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_REMOVE: log_debug("scheduler-api: PROC_SCHEDULER_REMOVE"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_end(); r = handler_remove(evpid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_SUSPEND: log_debug("scheduler-api: PROC_SCHEDULER_SUSPEND"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_end(); r = handler_suspend(evpid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_SCHEDULER_RESUME: log_debug("scheduler-api: PROC_SCHEDULER_RESUME"); scheduler_msg_get(&evpid, sizeof(evpid)); scheduler_msg_end(); r = handler_resume(evpid); imsg_compose(&ibuf, PROC_SCHEDULER_OK, 0, 0, -1, &r, sizeof(r)); break; default: log_warnx("warn: scheduler-api: bad message %d", imsg.hdr.type); fatalx("scheduler-api: exiting"); } } void scheduler_api_on_init(int(*cb)(void)) { handler_init = cb; } void scheduler_api_on_insert(int(*cb)(struct scheduler_info *)) { handler_insert = cb; } void scheduler_api_on_commit(size_t(*cb)(uint32_t)) { handler_commit = cb; } void scheduler_api_on_rollback(size_t(*cb)(uint32_t)) { handler_rollback = cb; } void scheduler_api_on_update(int(*cb)(struct scheduler_info *)) { handler_update = cb; } void scheduler_api_on_delete(int(*cb)(uint64_t)) { handler_delete = cb; } void scheduler_api_on_batch(int(*cb)(int, int *, size_t *, uint64_t *, int *)) { handler_batch = cb; } void scheduler_api_on_messages(size_t(*cb)(uint32_t, uint32_t *, size_t)) { handler_messages = cb; } void scheduler_api_on_envelopes(size_t(*cb)(uint64_t, struct evpstate *, size_t)) { handler_envelopes = cb; } void scheduler_api_on_schedule(int(*cb)(uint64_t)) { handler_schedule = cb; } void scheduler_api_on_remove(int(*cb)(uint64_t)) { handler_remove = cb; } void scheduler_api_on_suspend(int(*cb)(uint64_t)) { handler_suspend = cb; } void scheduler_api_on_resume(int(*cb)(uint64_t)) { handler_resume = cb; } void scheduler_api_on_hold(int(*cb)(uint64_t, uint64_t)) { handler_hold = cb; } void scheduler_api_on_release(int(*cb)(int, uint64_t, int)) { handler_release = cb; } void scheduler_api_no_chroot(void) { rootpath = NULL; } void scheduler_api_set_chroot(const char *path) { rootpath = path; } void scheduler_api_set_user(const char *username) { user = username; } int scheduler_api_dispatch(void) { struct passwd *pw = NULL; ssize_t n; if (user) { pw = getpwnam(user); if (pw == NULL) { log_warn("queue-api: getpwnam"); fatalx("queue-api: exiting"); } } if (rootpath) { if (chroot(rootpath) == -1) { log_warn("queue-api: chroot"); fatalx("queue-api: exiting"); } if (chdir("/") == -1) { log_warn("queue-api: chdir"); fatalx("queue-api: exiting"); } } if (pw && (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))) { log_warn("queue-api: cannot drop privileges"); fatalx("queue-api: exiting"); } imsg_init(&ibuf, 0); while (1) { n = imsg_get(&ibuf, &imsg); if (n == -1) { log_warn("warn: scheduler-api: imsg_get"); break; } if (n) { rdata = imsg.data; rlen = imsg.hdr.len - IMSG_HEADER_SIZE; scheduler_msg_dispatch(); imsg_flush(&ibuf); continue; } n = imsg_read(&ibuf); if (n == -1) { log_warn("warn: scheduler-api: imsg_read"); break; } if (n == 0) { log_warnx("warn: scheduler-api: pipe closed"); break; } } return (1); } OpenSMTPD-extras-6.6.0/api/smtpd-api.h000066400000000000000000000400261355240505200173630ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Eric Faurot * Copyright (c) 2011 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _SMTPD_API_H_ #define _SMTPD_API_H_ #include #include #include #include #include #include #include #include #include #include "smtpd-defines.h" #include "ioev.h" #include "iobuf.h" #include "log.h" #include "rfc2822.h" #define FILTER_API_VERSION 52 enum blockmodes { BM_NORMAL, BM_NONBLOCK }; struct mproc { pid_t pid; char *name; int proc; void (*handler)(struct mproc *, struct imsg *); struct imsgbuf imsgbuf; char *m_buf; size_t m_alloc; size_t m_pos; uint32_t m_type; uint32_t m_peerid; pid_t m_pid; int m_fd; int enable; short events; struct event ev; void *data; off_t msg_in; off_t msg_out; off_t bytes_in; off_t bytes_out; size_t bytes_queued; size_t bytes_queued_max; }; struct msg { const uint8_t *pos; const uint8_t *end; }; struct mailaddr { char user[SMTPD_MAXLOCALPARTSIZE]; char domain[SMTPD_MAXDOMAINPARTSIZE]; }; SPLAY_HEAD(_dict, dictentry); SPLAY_HEAD(_tree, treeentry); struct tree { struct _tree tree; size_t count; }; struct dict { struct _dict dict; size_t count; }; enum filter_status { FILTER_OK, FILTER_FAIL, FILTER_CLOSE, }; enum filter_imsg { IMSG_FILTER_REGISTER, IMSG_FILTER_EVENT, IMSG_FILTER_QUERY, IMSG_FILTER_PIPE, IMSG_FILTER_RESPONSE }; /* XXX - server side requires mfa_session.c update on filter_event */ enum filter_event_type { EVENT_CONNECT, EVENT_RESET, EVENT_DISCONNECT, EVENT_TX_BEGIN, EVENT_TX_COMMIT, EVENT_TX_ROLLBACK, }; /* XXX - server side requires mfa_session.c update on filter_hook changes */ enum filter_query_type { QUERY_CONNECT, QUERY_HELO, QUERY_MAIL, QUERY_RCPT, QUERY_DATA, QUERY_EOM, QUERY_DATALINE, }; struct filter_connect { struct sockaddr_storage local; struct sockaddr_storage remote; const char *hostname; }; #define PROC_QUEUE_API_VERSION 2 enum { PROC_QUEUE_OK, PROC_QUEUE_FAIL, PROC_QUEUE_INIT, PROC_QUEUE_CLOSE, PROC_QUEUE_MESSAGE_CREATE, PROC_QUEUE_MESSAGE_DELETE, PROC_QUEUE_MESSAGE_COMMIT, PROC_QUEUE_MESSAGE_FD_R, PROC_QUEUE_MESSAGE_CORRUPT, PROC_QUEUE_MESSAGE_UNCORRUPT, PROC_QUEUE_ENVELOPE_CREATE, PROC_QUEUE_ENVELOPE_DELETE, PROC_QUEUE_ENVELOPE_LOAD, PROC_QUEUE_ENVELOPE_UPDATE, PROC_QUEUE_ENVELOPE_WALK, PROC_QUEUE_MESSAGE_WALK, }; #define PROC_SCHEDULER_API_VERSION 2 struct scheduler_info; enum { PROC_SCHEDULER_OK, PROC_SCHEDULER_FAIL, PROC_SCHEDULER_INIT, PROC_SCHEDULER_INSERT, PROC_SCHEDULER_COMMIT, PROC_SCHEDULER_ROLLBACK, PROC_SCHEDULER_UPDATE, PROC_SCHEDULER_DELETE, PROC_SCHEDULER_HOLD, PROC_SCHEDULER_RELEASE, PROC_SCHEDULER_BATCH, PROC_SCHEDULER_MESSAGES, PROC_SCHEDULER_ENVELOPES, PROC_SCHEDULER_SCHEDULE, PROC_SCHEDULER_REMOVE, PROC_SCHEDULER_SUSPEND, PROC_SCHEDULER_RESUME, }; enum envelope_flags { EF_AUTHENTICATED = 0x01, EF_BOUNCE = 0x02, EF_INTERNAL = 0x04, /* Internal expansion forward */ /* runstate, not saved on disk */ EF_PENDING = 0x10, EF_INFLIGHT = 0x20, EF_SUSPEND = 0x40, EF_HOLD = 0x80, }; struct evpstate { uint64_t evpid; uint16_t flags; uint16_t retry; time_t time; }; enum delivery_type { D_MDA, D_MTA, D_BOUNCE, }; struct scheduler_info { uint64_t evpid; enum delivery_type type; uint16_t retry; time_t creation; time_t expire; time_t lasttry; time_t lastbounce; time_t nexttry; }; #define SCHED_REMOVE 0x01 #define SCHED_EXPIRE 0x02 #define SCHED_UPDATE 0x04 #define SCHED_BOUNCE 0x08 #define SCHED_MDA 0x10 #define SCHED_MTA 0x20 #define PROC_TABLE_API_VERSION 2 struct table_open_params { uint32_t version; char name[LINE_MAX]; }; enum table_service { K_NONE = 0x000, K_ALIAS = 0x001, /* returns struct expand */ K_DOMAIN = 0x002, /* returns struct destination */ K_CREDENTIALS = 0x004, /* returns struct credentials */ K_NETADDR = 0x008, /* returns struct netaddr */ K_USERINFO = 0x010, /* returns struct userinfo */ K_SOURCE = 0x020, /* returns struct source */ K_MAILADDR = 0x040, /* returns struct mailaddr */ K_ADDRNAME = 0x080, /* returns struct addrname */ K_MAILADDRMAP = 0x100, /* returns struct mailaddr */ }; #define K_ANY 0xfff enum { PROC_TABLE_OK, PROC_TABLE_FAIL, PROC_TABLE_OPEN, PROC_TABLE_CLOSE, PROC_TABLE_UPDATE, PROC_TABLE_CHECK, PROC_TABLE_LOOKUP, PROC_TABLE_FETCH, }; enum enhanced_status_code { /* 0.0 */ ESC_OTHER_STATUS = 00, /* 1.x */ ESC_OTHER_ADDRESS_STATUS = 10, ESC_BAD_DESTINATION_MAILBOX_ADDRESS = 11, ESC_BAD_DESTINATION_SYSTEM_ADDRESS = 12, ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX = 13, ESC_DESTINATION_MAILBOX_ADDRESS_AMBIGUOUS = 14, ESC_DESTINATION_ADDRESS_VALID = 15, ESC_DESTINATION_MAILBOX_HAS_MOVED = 16, ESC_BAD_SENDER_MAILBOX_ADDRESS_SYNTAX = 17, ESC_BAD_SENDER_SYSTEM_ADDRESS = 18, /* 2.x */ ESC_OTHER_MAILBOX_STATUS = 20, ESC_MAILBOX_DISABLED = 21, ESC_MAILBOX_FULL = 22, ESC_MESSAGE_LENGTH_TOO_LARGE = 23, ESC_MAILING_LIST_EXPANSION_PROBLEM = 24, /* 3.x */ ESC_OTHER_MAIL_SYSTEM_STATUS = 30, ESC_MAIL_SYSTEM_FULL = 31, ESC_SYSTEM_NOT_ACCEPTING_MESSAGES = 32, ESC_SYSTEM_NOT_CAPABLE_OF_SELECTED_FEATURES = 33, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM = 34, ESC_SYSTEM_INCORRECTLY_CONFIGURED = 35, /* 4.x */ ESC_OTHER_NETWORK_ROUTING_STATUS = 40, ESC_NO_ANSWER_FROM_HOST = 41, ESC_BAD_CONNECTION = 42, ESC_DIRECTORY_SERVER_FAILURE = 43, ESC_UNABLE_TO_ROUTE = 44, ESC_MAIL_SYSTEM_CONGESTION = 45, ESC_ROUTING_LOOP_DETECTED = 46, ESC_DELIVERY_TIME_EXPIRED = 47, /* 5.x */ ESC_INVALID_RECIPIENT = 50, ESC_INVALID_COMMAND = 51, ESC_SYNTAX_ERROR = 52, ESC_TOO_MANY_RECIPIENTS = 53, ESC_INVALID_COMMAND_ARGUMENTS = 54, ESC_WRONG_PROTOCOL_VERSION = 55, /* 6.x */ ESC_OTHER_MEDIA_ERROR = 60, ESC_MEDIA_NOT_SUPPORTED = 61, ESC_CONVERSION_REQUIRED_AND_PROHIBITED = 62, ESC_CONVERSION_REQUIRED_BUT_NOT_SUPPORTED = 63, ESC_CONVERSION_WITH_LOSS_PERFORMED = 64, ESC_CONVERSION_FAILED = 65, /* 7.x */ ESC_OTHER_SECURITY_STATUS = 70, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED = 71, ESC_MAILING_LIST_EXPANSION_PROHIBITED = 72, ESC_SECURITY_CONVERSION_REQUIRED_NOT_POSSIBLE = 73, ESC_SECURITY_FEATURES_NOT_SUPPORTED = 74, ESC_CRYPTOGRAPHIC_FAILURE = 75, ESC_CRYPTOGRAPHIC_ALGORITHM_NOT_SUPPORTED = 76, ESC_MESSAGE_INTEGRITY_FAILURE = 77, }; enum enhanced_status_class { ESC_STATUS_OK = 2, ESC_STATUS_TEMPFAIL = 4, ESC_STATUS_PERMFAIL = 5, }; static inline uint32_t evpid_to_msgid(uint64_t evpid) { return (evpid >> 32); } static inline uint64_t msgid_to_evpid(uint32_t msgid) { return ((uint64_t)msgid << 32); } /* dict.c */ #define dict_init(d) do { SPLAY_INIT(&((d)->dict)); (d)->count = 0; } while(0) #define dict_empty(d) SPLAY_EMPTY(&((d)->dict)) #define dict_count(d) ((d)->count) int dict_check(struct dict *, const char *); void *dict_set(struct dict *, const char *, void *); void dict_xset(struct dict *, const char *, void *); void *dict_get(struct dict *, const char *); void *dict_xget(struct dict *, const char *); void *dict_pop(struct dict *, const char *); void *dict_xpop(struct dict *, const char *); int dict_poproot(struct dict *, void **); int dict_root(struct dict *, const char **, void **); int dict_iter(struct dict *, void **, const char **, void **); int dict_iterfrom(struct dict *, void **, const char *, const char **, void **); void dict_merge(struct dict *, struct dict *); /* esc.c */ const char *esc_code(enum enhanced_status_class, enum enhanced_status_code); const char *esc_description(enum enhanced_status_code); /* filter_api.c */ void filter_api_session_allocator(void *(*)(uint64_t)); void filter_api_session_destructor(void (*)(void *)); void *filter_api_session(uint64_t); void filter_api_transaction_allocator(void *(*)(uint64_t)); void filter_api_transaction_destructor(void (*)(void *)); void *filter_api_transaction(uint64_t); void filter_api_setugid(uid_t, gid_t); void filter_api_set_chroot(const char *); void filter_api_no_chroot(void); void filter_api_data_buffered(void); void filter_api_data_buffered_stream(uint64_t); void filter_api_loop(void); int filter_api_accept(uint64_t); int filter_api_reject(uint64_t, enum filter_status); int filter_api_reject_code(uint64_t, enum filter_status, uint32_t, const char *); void filter_api_writeln(uint64_t, const char *); void filter_api_printf(uint64_t id, const char *, ...); void filter_api_timer(uint64_t, uint32_t, void (*)(uint64_t, void *), void *); const char *filter_api_sockaddr_to_text(const struct sockaddr *); const char *filter_api_mailaddr_to_text(const struct mailaddr *); FILE *filter_api_datahold_open(uint64_t, void (*callback)(uint64_t, FILE *, void *), void *); void filter_api_datahold_close(uint64_t); void filter_api_on_connect(int(*)(uint64_t, struct filter_connect *)); void filter_api_on_helo(int(*)(uint64_t, const char *)); void filter_api_on_mail(int(*)(uint64_t, struct mailaddr *)); void filter_api_on_rcpt(int(*)(uint64_t, struct mailaddr *)); void filter_api_on_data(int(*)(uint64_t)); void filter_api_on_msg_line(void(*)(uint64_t, const char *)); void filter_api_on_msg_start(void(*)(uint64_t)); void filter_api_on_msg_end(int(*)(uint64_t, size_t)); void filter_api_on_reset(void(*)(uint64_t)); void filter_api_on_disconnect(void(*)(uint64_t)); void filter_api_on_tx_begin(void(*)(uint64_t)); void filter_api_on_tx_commit(void(*)(uint64_t)); void filter_api_on_tx_rollback(void(*)(uint64_t)); const char *proc_name(enum smtp_proc_type); const char *imsg_to_str(int); /* mproc.c */ int mproc_fork(struct mproc *, const char*, char **); void mproc_init(struct mproc *, int); void mproc_clear(struct mproc *); void mproc_enable(struct mproc *); void mproc_disable(struct mproc *); void mproc_event_add(struct mproc *); void m_compose(struct mproc *, uint32_t, uint32_t, pid_t, int, void *, size_t); void m_composev(struct mproc *, uint32_t, uint32_t, pid_t, int, const struct iovec *, int); void m_forward(struct mproc *, struct imsg *); void m_create(struct mproc *, uint32_t, uint32_t, pid_t, int); void m_add(struct mproc *, const void *, size_t); void m_add_int(struct mproc *, int); void m_add_u32(struct mproc *, uint32_t); void m_add_size(struct mproc *, size_t); void m_add_time(struct mproc *, time_t); void m_add_string(struct mproc *, const char *); void m_add_data(struct mproc *, const void *, size_t); void m_add_evpid(struct mproc *, uint64_t); void m_add_msgid(struct mproc *, uint32_t); void m_add_id(struct mproc *, uint64_t); void m_add_sockaddr(struct mproc *, const struct sockaddr *); void m_add_mailaddr(struct mproc *, const struct mailaddr *); void m_close(struct mproc *); void m_flush(struct mproc *); void m_msg(struct msg *, struct imsg *); int m_is_eom(struct msg *); void m_end(struct msg *); void m_get_int(struct msg *, int *); void m_get_size(struct msg *, size_t *); void m_get_u32(struct msg *, uint32_t *); void m_get_time(struct msg *, time_t *); void m_get_string(struct msg *, const char **); void m_get_data(struct msg *, const void **, size_t *); void m_get_evpid(struct msg *, uint64_t *); void m_get_msgid(struct msg *, uint32_t *); void m_get_id(struct msg *, uint64_t *); void m_get_sockaddr(struct msg *, struct sockaddr *); void m_get_mailaddr(struct msg *, struct mailaddr *); #ifdef BUILD_DAEMON void m_add_envelope(struct mproc *, const struct envelope *); void m_get_envelope(struct msg *, struct envelope *); #endif /* queue */ void queue_api_on_close(int(*)(void)); void queue_api_on_message_create(int(*)(uint32_t *)); void queue_api_on_message_commit(int(*)(uint32_t, const char*)); void queue_api_on_message_delete(int(*)(uint32_t)); void queue_api_on_message_fd_r(int(*)(uint32_t)); void queue_api_on_message_corrupt(int(*)(uint32_t)); void queue_api_on_message_uncorrupt(int(*)(uint32_t)); void queue_api_on_envelope_create(int(*)(uint32_t, const char *, size_t, uint64_t *)); void queue_api_on_envelope_delete(int(*)(uint64_t)); void queue_api_on_envelope_update(int(*)(uint64_t, const char *, size_t)); void queue_api_on_envelope_load(int(*)(uint64_t, char *, size_t)); void queue_api_on_envelope_walk(int(*)(uint64_t *, char *, size_t)); void queue_api_on_message_walk(int(*)(uint64_t *, char *, size_t, uint32_t, int *, void **)); void queue_api_no_chroot(void); void queue_api_set_chroot(const char *); void queue_api_set_user(const char *); int queue_api_dispatch(void); /* queue utils */ uint32_t queue_generate_msgid(void); uint64_t queue_generate_evpid(uint32_t); int mktmpfile(void); /* scheduler */ void scheduler_api_on_init(int(*)(void)); void scheduler_api_on_insert(int(*)(struct scheduler_info *)); void scheduler_api_on_commit(size_t(*)(uint32_t)); void scheduler_api_on_rollback(size_t(*)(uint32_t)); void scheduler_api_on_update(int(*)(struct scheduler_info *)); void scheduler_api_on_delete(int(*)(uint64_t)); void scheduler_api_on_hold(int(*)(uint64_t, uint64_t)); void scheduler_api_on_release(int(*)(int, uint64_t, int)); void scheduler_api_on_batch(int(*)(int, int *, size_t *, uint64_t *, int *)); void scheduler_api_on_messages(size_t(*)(uint32_t, uint32_t *, size_t)); void scheduler_api_on_envelopes(size_t(*)(uint64_t, struct evpstate *, size_t)); void scheduler_api_on_schedule(int(*)(uint64_t)); void scheduler_api_on_remove(int(*)(uint64_t)); void scheduler_api_on_suspend(int(*)(uint64_t)); void scheduler_api_on_resume(int(*)(uint64_t)); void scheduler_api_no_chroot(void); void scheduler_api_set_chroot(const char *); void scheduler_api_set_user(const char *); int scheduler_api_dispatch(void); /* table */ void table_api_on_update(int(*)(void)); void table_api_on_check(int(*)(int, struct dict *, const char *)); void table_api_on_lookup(int(*)(int, struct dict *, const char *, char *, size_t)); void table_api_on_fetch(int(*)(int, struct dict *, char *, size_t)); int table_api_dispatch(void); const char *table_api_get_name(void); /* tree.c */ #define tree_init(t) do { SPLAY_INIT(&((t)->tree)); (t)->count = 0; } while(0) #define tree_empty(t) SPLAY_EMPTY(&((t)->tree)) #define tree_count(t) ((t)->count) int tree_check(struct tree *, uint64_t); void *tree_set(struct tree *, uint64_t, void *); void tree_xset(struct tree *, uint64_t, void *); void *tree_get(struct tree *, uint64_t); void *tree_xget(struct tree *, uint64_t); void *tree_pop(struct tree *, uint64_t); void *tree_xpop(struct tree *, uint64_t); int tree_poproot(struct tree *, uint64_t *, void **); int tree_root(struct tree *, uint64_t *, void **); int tree_iter(struct tree *, void **, uint64_t *, void **); int tree_iterfrom(struct tree *, void **, uint64_t, uint64_t *, void **); void tree_merge(struct tree *, struct tree *); /* util.c */ struct iobuf; /* forward declaration */ void *xmalloc(size_t, const char *); void *xcalloc(size_t, size_t, const char *); char *xstrdup(const char *, const char *); void *xmemdup(const void *, size_t, const char *); void iobuf_xinit(struct iobuf *, size_t, size_t, const char *); void iobuf_xfqueue(struct iobuf *, const char *, const char *, ...); char *strip(char *); int base64_encode(unsigned char const *, size_t, char *, size_t); int base64_decode(char const *, unsigned char *, size_t); int lowercase(char *, const char *, size_t); #endif OpenSMTPD-extras-6.6.0/api/smtpd-defines.h000066400000000000000000000035361355240505200202340ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _SMTPD_DEFINES_H #define _SMTPD_DEFINES_H #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif enum smtp_proc_type { PROC_PARENT = 0, PROC_LKA, PROC_QUEUE, PROC_CONTROL, PROC_SCHEDULER, PROC_PONY, PROC_CA, PROC_FILTER, PROC_CLIENT, }; #define TRACE_DEBUG 0x0001 #define TRACE_IMSG 0x0002 #define TRACE_IO 0x0004 #define TRACE_SMTP 0x0008 #define TRACE_FILTERS 0x0010 #define TRACE_MTA 0x0020 #define TRACE_BOUNCE 0x0040 #define TRACE_SCHEDULER 0x0080 #define TRACE_LOOKUP 0x0100 #define TRACE_STAT 0x0200 #define TRACE_RULES 0x0400 #define TRACE_MPROC 0x0800 #define TRACE_EXPAND 0x1000 #define TRACE_TABLES 0x2000 #define TRACE_QUEUE 0x4000 #define PROFILE_TOSTAT 0x0001 #define PROFILE_IMSG 0x0002 #define PROFILE_QUEUE 0x0004 #define PROFILE_BUFFERS 0x0008 #define SMTPD_MAXLOCALPARTSIZE (255 + 1) #define SMTPD_MAXDOMAINPARTSIZE (255 + 1) #define SMTPD_USER "_smtpd" #define PATH_CHROOT "/var/empty" #define SMTPD_QUEUE_USER "_smtpq" #define PATH_SPOOL "/var/spool/smtpd" #define TAG_CHAR '+' /* gilles+tag@ */ #endif OpenSMTPD-extras-6.6.0/api/table_api.c000066400000000000000000000152441355240505200174040ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include static int (*handler_update)(void); static int (*handler_check)(int, struct dict *, const char *); static int (*handler_lookup)(int, struct dict *, const char *, char *, size_t); static int (*handler_fetch)(int, struct dict *, char *, size_t); static int quit; static struct imsgbuf ibuf; static struct imsg imsg; static size_t rlen; static char *rdata; static struct ibuf *buf; static char *name; #if 0 static char *rootpath; static char *user = SMTPD_USER; #endif static void table_msg_get(void *dst, size_t len) { if (len > rlen) { log_warnx("warn: table-proc: bad msg len"); fatalx("table-proc: exiting"); } if (len == 0) return; if (dst) memmove(dst, rdata, len); rlen -= len; rdata += len; } static void table_msg_end(void) { if (rlen) { log_warnx("warn: table-proc: bogus data"); fatalx("table-proc: exiting"); } imsg_free(&imsg); } static void table_msg_add(const void *data, size_t len) { if (buf == NULL) buf = imsg_create(&ibuf, PROC_TABLE_OK, 0, 0, 1024); if (buf == NULL) { log_warnx("warn: table-api: imsg_create failed"); fatalx("table-api: exiting"); } if (imsg_add(buf, data, len) == -1) { log_warnx("warn: table-api: imsg_add failed"); fatalx("table-api: exiting"); } } static void table_msg_close(void) { imsg_close(&ibuf, buf); buf = NULL; } static int table_read_params(struct dict *params) { size_t count; char *key; char *value; dict_init(params); table_msg_get(&count, sizeof(count)); for (;count; count--) { key = rdata; table_msg_get(NULL, strlen(key) + 1); value = rdata; table_msg_get(NULL, strlen(value) + 1); dict_set(params, key, value); } return (0); } static void table_clear_params(struct dict *params) { while (dict_poproot(params, NULL)) ; } static void table_msg_dispatch(void) { struct table_open_params op; struct dict params; char res[4096]; int type, r; switch (imsg.hdr.type) { case PROC_TABLE_OPEN: table_msg_get(&op, sizeof op); table_msg_end(); if (op.version != PROC_TABLE_API_VERSION) { log_warnx("warn: table-api: bad API version"); fatalx("table-api: terminating"); } if ((name = strdup(op.name)) == NULL) { log_warn("warn: table-api"); fatalx("table-api: terminating"); } imsg_compose(&ibuf, PROC_TABLE_OK, 0, 0, -1, NULL, 0); break; case PROC_TABLE_UPDATE: table_msg_end(); if (handler_update) r = handler_update(); else r = 1; imsg_compose(&ibuf, PROC_TABLE_OK, 0, 0, -1, &r, sizeof(r)); break; case PROC_TABLE_CLOSE: quit = 1; break; case PROC_TABLE_CHECK: table_msg_get(&type, sizeof(type)); table_read_params(¶ms); if (rlen == 0) { log_warnx("warn: table-api: no key"); fatalx("table-api: exiting"); } if (rdata[rlen - 1] != '\0') { log_warnx("warn: table-api: key not NUL-terminated"); fatalx("table-api: exiting"); } if (handler_check) r = handler_check(type, ¶ms, rdata); else r = -1; table_clear_params(¶ms); table_msg_get(NULL, rlen); table_msg_end(); table_msg_add(&r, sizeof(r)); table_msg_close(); break; case PROC_TABLE_LOOKUP: table_msg_get(&type, sizeof(type)); table_read_params(¶ms); if (rlen == 0) { log_warnx("warn: table-api: no key"); fatalx("table-api: exiting"); } if (rdata[rlen - 1] != '\0') { log_warnx("warn: table-api: key not NUL-terminated"); fatalx("table-api: exiting"); } if (handler_lookup) r = handler_lookup(type, ¶ms, rdata, res, sizeof(res)); else r = -1; table_clear_params(¶ms); table_msg_get(NULL, rlen); table_msg_end(); table_msg_add(&r, sizeof(r)); if (r == 1) table_msg_add(res, strlen(res) + 1); table_msg_close(); break; case PROC_TABLE_FETCH: table_msg_get(&type, sizeof(type)); table_read_params(¶ms); if (handler_fetch) r = handler_fetch(type, ¶ms, res, sizeof(res)); else r = -1; table_clear_params(¶ms); table_msg_end(); table_msg_add(&r, sizeof(r)); if (r == 1) table_msg_add(res, strlen(res) + 1); table_msg_close(); break; default: log_warnx("warn: table-api: bad message %d", imsg.hdr.type); fatalx("table-api: exiting"); } } void table_api_on_update(int(*cb)(void)) { handler_update = cb; } void table_api_on_check(int(*cb)(int, struct dict *, const char *)) { handler_check = cb; } void table_api_on_lookup(int(*cb)(int, struct dict *, const char *, char *, size_t)) { handler_lookup = cb; } void table_api_on_fetch(int(*cb)(int, struct dict *, char *, size_t)) { handler_fetch = cb; } const char * table_api_get_name(void) { return name; } int table_api_dispatch(void) { #if 0 struct passwd *pw; #endif ssize_t n; #if 0 pw = getpwnam(user); if (pw == NULL) { log_warn("table-api: getpwnam"); fatalx("table-api: exiting"); } if (rootpath) { if (chroot(rootpath) == -1) { log_warn("table-api: chroot"); fatalx("table-api: exiting"); } if (chdir("/") == -1) { log_warn("table-api: chdir"); fatalx("table-api: exiting"); } } if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { log_warn("table-api: cannot drop privileges"); fatalx("table-api: exiting"); } #endif imsg_init(&ibuf, 0); while (1) { n = imsg_get(&ibuf, &imsg); if (n == -1) { log_warn("warn: table-api: imsg_get"); break; } if (n) { rdata = imsg.data; rlen = imsg.hdr.len - IMSG_HEADER_SIZE; table_msg_dispatch(); if (quit) break; imsg_flush(&ibuf); continue; } n = imsg_read(&ibuf); if (n == -1) { log_warn("warn: table-api: imsg_read"); break; } if (n == 0) { log_warnx("warn: table-api: pipe closed"); break; } } return (1); } OpenSMTPD-extras-6.6.0/api/to.c000066400000000000000000000151421355240505200161030ustar00rootroot00000000000000/* $OpenBSD: to.c,v 1.17 2014/04/19 14:27:29 gilles Exp $ */ /* * Copyright (c) 2009 Jacek Masiulaniec * Copyright (c) 2012 Eric Faurot * Copyright (c) 2012 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "smtpd-defines.h" #include #include "log.h" static const char *in6addr_to_text(const struct in6_addr *); const char * sockaddr_to_text(struct sockaddr *sa) { static char buf[NI_MAXHOST]; if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, NI_NUMERICHOST)) return ("(unknown)"); else return (buf); } static const char * in6addr_to_text(const struct in6_addr *addr) { struct sockaddr_in6 sa_in6; uint16_t tmp16; memset(&sa_in6, 0, sizeof(sa_in6)); sa_in6.sin6_len = sizeof(sa_in6); sa_in6.sin6_family = AF_INET6; memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); sa_in6.sin6_scope_id = ntohs(tmp16); sa_in6.sin6_addr.s6_addr[2] = 0; sa_in6.sin6_addr.s6_addr[3] = 0; } return (sockaddr_to_text((struct sockaddr *)&sa_in6)); } int text_to_mailaddr(struct mailaddr *maddr, const char *email) { char *username; char *hostname; char buffer[LINE_MAX]; if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer) return 0; memset(maddr, 0, sizeof *maddr); username = buffer; hostname = strrchr(username, '@'); if (hostname == NULL) { if (strlcpy(maddr->user, username, sizeof maddr->user) >= sizeof maddr->user) return 0; } else if (username == hostname) { *hostname++ = '\0'; if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) >= sizeof maddr->domain) return 0; } else { *hostname++ = '\0'; if (strlcpy(maddr->user, username, sizeof maddr->user) >= sizeof maddr->user) return 0; if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) >= sizeof maddr->domain) return 0; } return 1; } const char * mailaddr_to_text(const struct mailaddr *maddr) { static char buffer[LINE_MAX]; (void)strlcpy(buffer, maddr->user, sizeof buffer); (void)strlcat(buffer, "@", sizeof buffer); if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) return NULL; return buffer; } const char * sa_to_text(const struct sockaddr *sa) { static char buf[NI_MAXHOST + 5]; char *p; buf[0] = '\0'; p = buf; if (sa->sa_family == AF_LOCAL) (void)strlcpy(buf, "local", sizeof buf); else if (sa->sa_family == AF_INET) { in_addr_t addr; addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr; addr = ntohl(addr); (void)snprintf(p, NI_MAXHOST, "%d.%d.%d.%d", (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); } else if (sa->sa_family == AF_INET6) { const struct sockaddr_in6 *in6; const struct in6_addr *in6_addr; in6 = (const struct sockaddr_in6 *)sa; (void)strlcpy(buf, "IPv6:", sizeof(buf)); p = buf + 5; in6_addr = &in6->sin6_addr; (void)snprintf(p, NI_MAXHOST, "%s", in6addr_to_text(in6_addr)); } return (buf); } const char * ss_to_text(const struct sockaddr_storage *ss) { return (sa_to_text((const struct sockaddr*)ss)); } const char * time_to_text(time_t when) { struct tm *lt; static char buf[40]; char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; int ret; lt = localtime(&when); if (lt == NULL || when == 0) fatalx("time_to_text: localtime"); /* We do not use strftime because it is subject to locale substitution*/ ret = snprintf(buf, sizeof(buf), "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], lt->tm_year + 1900, lt->tm_hour, lt->tm_min, lt->tm_sec, lt->tm_gmtoff >= 0 ? '+' : '-', abs((int)lt->tm_gmtoff / 3600), abs((int)lt->tm_gmtoff % 3600) / 60, lt->tm_zone); if (ret == -1 || ret >= sizeof (buf)) fatalx("time_to_text: snprintf"); return buf; } const char * duration_to_text(time_t t) { static char dst[64]; char buf[64]; int h, m, s; long long d; if (t == 0) { (void)strlcpy(dst, "0s", sizeof dst); return (dst); } dst[0] = '\0'; if (t < 0) { (void)strlcpy(dst, "-", sizeof dst); t = -t; } s = t % 60; t /= 60; m = t % 60; t /= 60; h = t % 24; d = t / 24; if (d) { (void)snprintf(buf, sizeof buf, "%lldd", d); (void)strlcat(dst, buf, sizeof dst); } if (h) { (void)snprintf(buf, sizeof buf, "%dh", h); (void)strlcat(dst, buf, sizeof dst); } if (m) { (void)snprintf(buf, sizeof buf, "%dm", m); (void)strlcat(dst, buf, sizeof dst); } if (s) { (void)snprintf(buf, sizeof buf, "%ds", s); (void)strlcat(dst, buf, sizeof dst); } return (dst); } uint64_t text_to_evpid(const char *s) { uint64_t ulval; char *ep; errno = 0; ulval = strtoull(s, &ep, 16); if (s[0] == '\0' || *ep != '\0') return 0; if (errno == ERANGE && ulval == ULLONG_MAX) return 0; if (ulval == 0) return 0; return (ulval); } uint32_t text_to_msgid(const char *s) { uint64_t ulval; char *ep; errno = 0; ulval = strtoull(s, &ep, 16); if (s[0] == '\0' || *ep != '\0') return 0; if (errno == ERANGE && ulval == ULLONG_MAX) return 0; if (ulval == 0) return 0; if (ulval > 0xffffffff) return 0; return (ulval & 0xffffffff); } OpenSMTPD-extras-6.6.0/api/tree.c000066400000000000000000000122121355240505200164130ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include /* for smtpd.h */ #include /* for smtpd.h */ #include /* for smtpd.h */ #include /* for smtpd.h */ #include #include #include #include struct treeentry { SPLAY_ENTRY(treeentry) entry; uint64_t id; void *data; }; static int treeentry_cmp(struct treeentry *, struct treeentry *); SPLAY_PROTOTYPE(_tree, treeentry, entry, treeentry_cmp); int tree_check(struct tree *t, uint64_t id) { struct treeentry key; key.id = id; return (SPLAY_FIND(_tree, &t->tree, &key) != NULL); } void * tree_set(struct tree *t, uint64_t id, void *data) { struct treeentry *entry, key; char *old; key.id = id; if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) { if ((entry = malloc(sizeof *entry)) == NULL) err(1, "tree_set: malloc"); entry->id = id; SPLAY_INSERT(_tree, &t->tree, entry); old = NULL; t->count += 1; } else old = entry->data; entry->data = data; return (old); } void tree_xset(struct tree *t, uint64_t id, void *data) { struct treeentry *entry; if ((entry = malloc(sizeof *entry)) == NULL) err(1, "tree_xset: malloc"); entry->id = id; entry->data = data; if (SPLAY_INSERT(_tree, &t->tree, entry)) errx(1, "tree_xset(%p, 0x%016"PRIx64 ")", t, id); t->count += 1; } void * tree_get(struct tree *t, uint64_t id) { struct treeentry key, *entry; key.id = id; if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) return (NULL); return (entry->data); } void * tree_xget(struct tree *t, uint64_t id) { struct treeentry key, *entry; key.id = id; if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) errx(1, "tree_get(%p, 0x%016"PRIx64 ")", t, id); return (entry->data); } void * tree_pop(struct tree *t, uint64_t id) { struct treeentry key, *entry; void *data; key.id = id; if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) return (NULL); data = entry->data; SPLAY_REMOVE(_tree, &t->tree, entry); free(entry); t->count -= 1; return (data); } void * tree_xpop(struct tree *t, uint64_t id) { struct treeentry key, *entry; void *data; key.id = id; if ((entry = SPLAY_FIND(_tree, &t->tree, &key)) == NULL) errx(1, "tree_xpop(%p, 0x%016" PRIx64 ")", t, id); data = entry->data; SPLAY_REMOVE(_tree, &t->tree, entry); free(entry); t->count -= 1; return (data); } int tree_poproot(struct tree *t, uint64_t *id, void **data) { struct treeentry *entry; entry = SPLAY_ROOT(&t->tree); if (entry == NULL) return (0); if (id) *id = entry->id; if (data) *data = entry->data; SPLAY_REMOVE(_tree, &t->tree, entry); free(entry); t->count -= 1; return (1); } int tree_root(struct tree *t, uint64_t *id, void **data) { struct treeentry *entry; entry = SPLAY_ROOT(&t->tree); if (entry == NULL) return (0); if (id) *id = entry->id; if (data) *data = entry->data; return (1); } int tree_iter(struct tree *t, void **hdl, uint64_t *id, void **data) { struct treeentry *curr = *hdl; if (curr == NULL) curr = SPLAY_MIN(_tree, &t->tree); else curr = SPLAY_NEXT(_tree, &t->tree, curr); if (curr) { *hdl = curr; if (id) *id = curr->id; if (data) *data = curr->data; return (1); } return (0); } int tree_iterfrom(struct tree *t, void **hdl, uint64_t k, uint64_t *id, void **data) { struct treeentry *curr = *hdl, key; if (curr == NULL) { if (k == 0) curr = SPLAY_MIN(_tree, &t->tree); else { key.id = k; curr = SPLAY_FIND(_tree, &t->tree, &key); if (curr == NULL) { SPLAY_INSERT(_tree, &t->tree, &key); curr = SPLAY_NEXT(_tree, &t->tree, &key); SPLAY_REMOVE(_tree, &t->tree, &key); } } } else curr = SPLAY_NEXT(_tree, &t->tree, curr); if (curr) { *hdl = curr; if (id) *id = curr->id; if (data) *data = curr->data; return (1); } return (0); } void tree_merge(struct tree *dst, struct tree *src) { struct treeentry *entry; while (!SPLAY_EMPTY(&src->tree)) { entry = SPLAY_ROOT(&src->tree); SPLAY_REMOVE(_tree, &src->tree, entry); if (SPLAY_INSERT(_tree, &dst->tree, entry)) errx(1, "tree_merge: duplicate"); } dst->count += src->count; src->count = 0; } static int treeentry_cmp(struct treeentry *a, struct treeentry *b) { if (a->id < b->id) return (-1); if (a->id > b->id) return (1); return (0); } SPLAY_GENERATE(_tree, treeentry, entry, treeentry_cmp); OpenSMTPD-extras-6.6.0/api/util.c000066400000000000000000000062021355240505200164330ustar00rootroot00000000000000/* $OpenBSD: util.c,v 1.127 2016/05/16 17:43:18 gilles Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Gilles Chehade * Copyright (c) 2009 Jacek Masiulaniec * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "iobuf.h" #include "log.h" void * xmalloc(size_t size, const char *where) { void *r; if ((r = malloc(size)) == NULL) { log_warnx("%s: malloc(%zu)", where, size); fatalx("exiting"); } return (r); } void * xcalloc(size_t nmemb, size_t size, const char *where) { void *r; if ((r = calloc(nmemb, size)) == NULL) { log_warnx("%s: calloc(%zu, %zu)", where, nmemb, size); fatalx("exiting"); } return (r); } char * xstrdup(const char *str, const char *where) { char *r; if ((r = strdup(str)) == NULL) { log_warnx("%s: strdup(%p)", where, str); fatalx("exiting"); } return (r); } void * xmemdup(const void *ptr, size_t size, const char *where) { void *r; if ((r = malloc(size)) == NULL) { log_warnx("%s: malloc(%zu)", where, size); fatalx("exiting"); } memmove(r, ptr, size); return (r); } void iobuf_xinit(struct iobuf *io, size_t size, size_t max, const char *where) { if (iobuf_init(io, size, max) == -1) { log_warnx("%s: iobuf_init(%p, %zu, %zu)", where, io, size, max); fatalx("exiting"); } } void iobuf_xfqueue(struct iobuf *io, const char *where, const char *fmt, ...) { va_list ap; int len; va_start(ap, fmt); len = iobuf_vfqueue(io, fmt, ap); va_end(ap); if (len == -1) { log_warnx("%s: iobuf_xfqueue(%p, %s, ...)", where, io, fmt); fatalx("exiting"); } } char * strip(char *s) { size_t l; while (isspace((unsigned char)*s)) s++; for (l = strlen(s); l; l--) { if (!isspace((unsigned char)s[l-1])) break; s[l-1] = '\0'; } return (s); } int lowercase(char *buf, const char *s, size_t len) { if (len == 0) return 0; if (strlcpy(buf, s, len) >= len) return 0; while (*buf != '\0') { *buf = tolower((unsigned char)*buf); buf++; } return 1; } int base64_encode(unsigned char const *src, size_t srclen, char *dest, size_t destsize) { return __b64_ntop(src, srclen, dest, destsize); } int base64_decode(char const *src, unsigned char *dest, size_t destsize) { return __b64_pton(src, dest, destsize); } OpenSMTPD-extras-6.6.0/bootstrap000077500000000000000000000100641355240505200165050ustar00rootroot00000000000000#! /bin/sh # bootstrap: generic bootstrap/autogen.sh script for autotools projects # # Copyright (c) 2002-2011 Sam Hocevar # # This program is free software. It comes without any warranty, to # the extent permitted by applicable law. You can redistribute it # and/or modify it under the terms of the Do What The Fuck You Want # To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. # # The latest version of this script can be found at the following place: # http://caca.zoy.org/wiki/build # Die if an error occurs set -e # Guess whether we are using configure.ac or configure.in if test -f configure.ac; then conffile="configure.ac" elif test -f configure.in; then conffile="configure.in" else echo "$0: could not find configure.ac or configure.in" exit 1 fi # Check for needed features auxdir="`sed -ne 's/^[ \t]*A._CONFIG_AUX_DIR *([[ ]*\([^] )]*\).*/\1/p' $conffile`" pkgconfig="`grep '^[ \t]*PKG_PROG_PKG_CONFIG' $conffile >/dev/null 2>&1 && echo yes || echo no`" libtool="`grep '^[ \t]*A._PROG_LIBTOOL' $conffile >/dev/null 2>&1 && echo yes || echo no`" header="`grep '^[ \t]*A._CONFIG_HEADER' $conffile >/dev/null 2>&1 && echo yes || echo no`" makefile="`[ -f Makefile.am ] && echo yes || echo no`" aclocalflags="`sed -ne 's/^[ \t]*ACLOCAL_AMFLAGS[ \t]*=//p' Makefile.am 2>/dev/null || :`" # Check for automake amvers="no" for v in 16 15 14 13; do if automake-1.${v} --version >/dev/null 2>&1; then amvers="-1.${v}" break elif automake1.${v} --version >/dev/null 2>&1; then amvers="1.${v}" break fi done if test "${amvers}" = "no" && automake --version > /dev/null 2>&1; then amvers="`automake --version | sed -e '1s/[^0-9]*//' -e q`" if `echo "$amvers\n1.13" | sort -V | head -n 1 | grep -q "$amvers"`; then amvers="no" else amvers="" fi fi if test "$amvers" = "no"; then echo "$0: you need automake version 1.13 or later" exit 1 fi # Check for autoconf acvers="no" for v in "" "269" "-2.69"; do if autoconf${v} --version >/dev/null 2>&1; then acvers="${v}" break fi done if test "$acvers" = "no"; then echo "$0: you need autoconf" exit 1 fi # Check for libtool if test "$libtool" = "yes"; then libtoolize="no" if glibtoolize --version >/dev/null 2>&1; then libtoolize="glibtoolize" else for v in "16" "15" "" "14"; do if libtoolize${v} --version >/dev/null 2>&1; then libtoolize="libtoolize${v}" break fi done fi if test "$libtoolize" = "no"; then echo "$0: you need libtool" exit 1 fi fi # Check for pkg-config if test "$pkgconfig" = "yes"; then if ! pkg-config --version >/dev/null 2>&1; then echo "$0: you need pkg-config" exit 1 fi fi # Remove old cruft for x in aclocal.m4 configure config.guess config.log config.sub config.cache config.h.in config.h compile libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 ltmain.sh libtool ltconfig missing mkinstalldirs depcomp install-sh; do rm -f $x autotools/$x; if test -n "$auxdir"; then rm -f "$auxdir/$x"; fi; done rm -Rf autom4te.cache if test -n "$auxdir"; then if test ! -d "$auxdir"; then mkdir "$auxdir" fi aclocalflags="${aclocalflags} -I $auxdir -I ." fi # Honour M4PATH because sometimes M4 doesn't save_IFS=$IFS IFS=: tmp="$M4PATH" for x in $tmp; do if test -n "$x"; then aclocalflags="${aclocalflags} -I $x" fi done IFS=$save_IFS # Explain what we are doing from now set -x # Bootstrap package if test "$libtool" = "yes"; then ${libtoolize} --copy --force if test -n "$auxdir" -a ! "$auxdir" = "." -a -f "ltmain.sh"; then echo "$0: working around a minor libtool issue" mv ltmain.sh "$auxdir/" fi fi aclocal${amvers} ${aclocalflags} autoconf${acvers} if test "$header" = "yes"; then autoheader${acvers} fi if test "$makefile" = "yes"; then #add --include-deps if you want to bootstrap with any other compiler than gcc #automake${amvers} --add-missing --copy --include-deps automake${amvers} --foreign --add-missing --copy fi # Remove cruft that we no longer want rm -Rf autom4te.cache OpenSMTPD-extras-6.6.0/configure.ac000066400000000000000000001202111355240505200170240ustar00rootroot00000000000000# $Id: configure.ac,v 1.519 2013/03/22 01:49:15 dtucker Exp $ # # Copyright (c) 1999-2004 Damien Miller # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.69) dnl AC_INIT([OpenSMTPD-extras], m4_esyscmd(date +%Y%m%d%H%M%S | sed -e 's/^\(.*\)$/5.7-\1/' | tr -d '\n'), [bugs@opensmtpd.org], [opensmtpd-extras], [https://github.com/OpenSMTPD/OpenSMTPD-extras]) AC_INIT([OpenSMTPD-extras], [5.7.2], [bugs@opensmtpd.org], [opensmtpd-extras], [https://github.com/OpenSMTPD/OpenSMTPD-extras]) AC_LANG([C]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADER([config.h]) AC_PROG_CC AC_CANONICAL_HOST AC_C_BIGENDIAN # Checks for programs. AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LIBTOOL AC_PATH_PROG([AR], [ar]) AC_PATH_PROG([CAT], [cat]) AC_PATH_PROG([SED], [sed]) AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) AC_PATH_PROG([SH], [sh]) AC_PATH_PROG([GROFF], [groff]) AC_PATH_PROG([NROFF], [nroff]) AC_PATH_PROG([MANDOC], [mandoc]) AM_INIT_AUTOMAKE([subdir-objects no-dependencies]) LT_INIT #here we should test for variables set by libtool detection if test "x$with_pic" != "xno" ; then CFLAGS="$CFLAGS ${pic_flag}" fi #from here everything comes from portable openssh configure.ac script #l50 dnl select manpage formatter if test "x$MANDOC" != "x" ; then MANFMT="$MANDOC" elif test "x$NROFF" != "x" ; then MANFMT="$NROFF -mandoc" elif test "x$GROFF" != "x" ; then MANFMT="$GROFF -mandoc -Tascii" else AC_MSG_WARN([no manpage formatted found]) MANFMT="false" fi AC_SUBST([MANFMT]) #l61 #l78 if test -z "$AR" ; then AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) fi #l80 #l101 if test -z "$LD" ; then LD=$CC fi AC_SUBST([LD]) AC_C_INLINE AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) #l108 #l150 (without _FORTIFY_SOURCE=2) if test "$GCC" = "yes" || test "$GCC" = "egcs"; then OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) OSSH_CHECK_CFLAG_COMPILE([-Wall]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) # OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) AC_MSG_CHECKING([gcc version]) GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` AC_MSG_RESULT([$GCC_VER]) AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fno-builtin-memset" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char b[10]; memset(b, 0, sizeof(b)); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ] ) # -fstack-protector-all doesn't always work for some GCC versions # and/or platforms, so we test if we can. If it's not supported # on a given platform gcc will emit a warning so we use -Werror. if test "x$use_stack_protector" = "x1"; then for t in -fstack-protector-strong -fstack-protector-all \ -fstack-protector; do AC_MSG_CHECKING([if $CC supports $t]) saved_CFLAGS="$CFLAGS" saved_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS $t -Werror" LDFLAGS="$LDFLAGS $t -Werror" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX"); ]])], [ AC_MSG_RESULT([yes]) CFLAGS="$saved_CFLAGS $t" LDFLAGS="$saved_LDFLAGS $t" AC_MSG_CHECKING([if $t works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX"); ]])], [ AC_MSG_RESULT([yes]) break ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross compiling: cannot test]) break ] ) ], [ AC_MSG_RESULT([no]) ] ) CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" done fi if test -z "$have_llong_max"; then # retry LLONG_MAX with -std=gnu99, needed on some Linuxes unset ac_cv_have_decl_LLONG_MAX saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -std=gnu99" AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], [CFLAGS="$saved_CFLAGS"], [#include ] ) fi fi AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include __attribute__((__unused__)) static void foo(void){return;}]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, [compiler does not accept __attribute__ on return types]) ] ) AC_ARG_WITH([rpath], [ --without-rpath Disable auto-added -R linker paths], [ if test "x$withval" = "xno" ; then need_dash_r="" fi if test "x$withval" = "xyes" ; then need_dash_r=1 fi ] ) # Allow user to specify flags AC_ARG_WITH([cflags], [ --with-cflags Specify additional flags to pass to compiler], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS="$CFLAGS $withval" fi ] ) AC_ARG_WITH([cppflags], [ --with-cppflags Specify additional flags to pass to preprocessor] , [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CPPFLAGS="$CPPFLAGS $withval" fi ] ) AC_ARG_WITH([ldflags], [ --with-ldflags Specify additional flags to pass to linker], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS="$LDFLAGS $withval" fi ] ) AC_ARG_WITH([libs], [ --with-libs Specify additional libraries to link with], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LIBS="$LIBS $withval" fi ] ) AC_ARG_WITH([Werror], [ --with-Werror Build main code with -Werror], [ if test -n "$withval" && test "x$withval" != "xno"; then werror_flags="-Werror" if test "x${withval}" != "xyes"; then werror_flags="$withval" fi fi ] ) #l325 AC_CHECK_HEADERS([ \ arpa/inet.h \ ctype.h \ dirent.h \ fcntl.h \ grp.h \ inttypes.h \ limits.h \ mach/mach_time.h \ ndir.h \ netdb.h \ netinet/in.h \ pam/pam_appl.h \ paths.h \ security/pam_appl.h \ stdint.h \ stdlib.h \ string.h \ strings.h \ sys/dir.h \ sys/ndir.h \ sys/param.h \ sys/pstat.h \ sys/socket.h \ sys/time.h \ sys/un.h \ syslog.h \ time.h \ ucred.h \ unistd.h ]) #l432 (customized) # Check for some target-specific stuff case "$host" in *-*-darwin*) use_pie=auto AC_MSG_CHECKING([if we have working getaddrinfo]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) exit(0); else exit(1); } ]])], [AC_MSG_RESULT([working])], [AC_MSG_RESULT([buggy]) ], [AC_MSG_RESULT([assume it is working])]) ;; *-*-dragonfly*) SMTPDLIBS="$SMTPDLIBS -lcrypt" ;; *-*-linux* | *-gnu* | *-k*bsd*-gnu* ) use_pie=auto CFLAGS="$CFLAGS -D_BSD_SOURCE" ;; *-*-netbsd*) if test "x$withval" != "xno" ; then need_dash_r=1 fi ;; *-*-openbsd*) use_pie=auto asr_in_libc=1 ;; esac AM_CONDITIONAL([NO_LIBASR], [test x$asr_in_libc = x1]) AC_MSG_CHECKING([compiler and flags for sanity]) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) ], [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] ) dnl Checks for header files. # Checks for libraries. AC_CHECK_FUNC([setsockopt], , [AC_CHECK_LIB([socket], [setsockopt])]) #l1525 AC_ARG_WITH([pie], [ --with-pie Build Position Independent Executables if possible], [ if test "x$withval" = "xno"; then use_pie=no fi if test "x$withval" = "xyes"; then use_pie=yes fi ] ) if test "x$use_pie" = "x"; then use_pie=no fi if test "x$use_pie" = "xauto"; then # Automatic PIE requires gcc >= 4.x AC_MSG_CHECKING([for gcc >= 4.x]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error gcc is too old #endif ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) use_pie=no ] ) fi if test "x$use_pie" != "xno"; then SAVED_CFLAGS="$CFLAGS" SAVED_LDFLAGS="$LDFLAGS" OSSH_CHECK_CFLAG_COMPILE([-fPIE]) OSSH_CHECK_LDFLAG_LINK([-pie]) # We use both -fPIE and -pie or neither. AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) CFLAGS="$SAVED_CFLAGS" LDFLAGS="$SAVED_LDFLAGS" fi fi #l1570 # Checks for library functions. AC_FUNC_FORK AC_FUNC_FSEEKO AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([ \ arc4random \ arc4random_uniform \ asprintf \ closefrom \ dirfd \ freeaddrinfo \ getaddrinfo \ getline \ gethostname \ getnameinfo \ getopt \ memmove \ memchr \ memset \ regcomp \ socketpair \ strdup \ strerror \ strncasecmp \ strpbrk \ strrchr \ strtol \ strtoumax \ strtoull \ strlcat \ strlcpy \ strtonum \ sysconf \ time \ tzset \ vasprintf \ vsnprintf ]) #l1723 AC_CHECK_DECL([strsep], [AC_CHECK_FUNCS([strsep])], [], [ #ifdef HAVE_STRING_H # include #endif ]) #l1730 #l1747 AC_CHECK_DECLS([O_NONBLOCK], , , [ #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif ]) AC_CHECK_DECLS([writev], , , [ #include #include #include ]) #l1873 # Check for broken snprintf if test "x$ac_cv_func_snprintf" = "xyes" ; then AC_MSG_CHECKING([whether snprintf correctly terminates long strings]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char b[5]; snprintf(b,5,"123456789"); exit(b[4]!='\0'); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your snprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] ) fi # If we don't have a working asprintf, then we strongly depend on vsnprintf # returning the right thing on overflow: the number of characters it tried to # create (as per SUSv3) if test "x$ac_cv_func_asprintf" != "xyes" && \ test "x$ac_cv_func_vsnprintf" = "xyes" ; then AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include int x_snprintf(char *str,size_t count,const char *fmt,...) { size_t ret; va_list ap; va_start(ap, fmt); ret = vsnprintf(str, count, fmt, ap); va_end(ap); return ret; } ]], [[ char x[1]; exit(x_snprintf(x, 1, "%s %d", "hello", 12345) == 11 ? 0 : 1); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] ) fi # On systems where [v]snprintf is broken, but is declared in stdio, # check that the fmt argument is const char * or just char *. # This is only useful for when BROKEN_SNPRINTF AC_MSG_CHECKING([whether snprintf can declare const char *fmt]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include int snprintf(char *a, size_t b, const char *c, ...) { return 0; } ]], [[ snprintf(0, 0, 0); ]])], [AC_MSG_RESULT([yes]) AC_DEFINE([SNPRINTF_CONST], [const], [Define as const if snprintf() can declare const char *fmt])], [AC_MSG_RESULT([no]) AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) dnl see whether mkstemp() requires XXXXXX AC_MSG_CHECKING([for (overly) strict mkstemp]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char template[]="conftest.mkstemp-test"; if (mkstemp(template) == -1) exit(1); unlink(template); exit(0); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_STRICT_MKSTEMP], [1], [Silly mkstemp()]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_STRICT_MKSTEMP]) ] ) #l1982 #l2174 (customized, bu adding -lssl to LIBS) # Search for OpenSSL saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" AC_ARG_WITH([libssl], [ --with-libssl=PATH Specify path to libssl installation ], [ if test "x$withval" != "xno" ; then case "$withval" in # Relative paths ./*|../*) withval="`pwd`/$withval" esac if test -d "$withval/lib"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi elif test -d "$withval/lib64"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" else LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" fi else if test -n "${need_dash_r}"; then LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi ] ) ## XXX chl -lssl manually added LIBS="-lcrypto -lssl $LIBS" AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], [Define if your ssl headers are included with #include ])], [ dnl Check default openssl install dir if test -n "${need_dash_r}"; then LDFLAGS="-L/usr/local/ssl/lib -R/usr/local/ssl/lib ${saved_LDFLAGS}" else LDFLAGS="-L/usr/local/ssl/lib ${saved_LDFLAGS}" fi CPPFLAGS="-I/usr/local/ssl/include ${saved_CPPFLAGS}" AC_CHECK_HEADER([openssl/opensslv.h], , [AC_MSG_ERROR([*** OpenSSL headers missing - please install first or check config.log ***])]) AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL])], [ AC_MSG_ERROR([*** Can't find recent OpenSSL libcrypto (see config.log for details) ***]) ] ) ] ) # XXX make --without-openssl work AC_DEFINE_UNQUOTED([WITH_OPENSSL], [1], [use libcrypto for cryptography]) AC_MSG_CHECKING([if programs using OpenSSL functions will link]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ SSLeay_add_all_algorithms(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) saved_LIBS="$LIBS" LIBS="$LIBS -ldl" AC_MSG_CHECKING([if programs using OpenSSL need -ldl]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ SSLeay_add_all_algorithms(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) LIBS="$saved_LIBS" ] ) ] ) SMTPD_USER=_smtpd AC_ARG_WITH([user-smtpd], [ --with-user-smtpd=user Specify non-privileged user for privilege separation (default=_smtpd)], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then SMTPD_USER=$withval fi ] ) AC_DEFINE_UNQUOTED([SMTPD_USER], ["$SMTPD_USER"], [non-privileged user for privilege separation]) AC_SUBST([SMTPD_USER]) # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_TYPE_UID_T AC_TYPE_UINT8_T AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_CHECK_TYPES([long long, unsigned long long]) AC_CHECK_SIZEOF([short int], [2]) AC_CHECK_SIZEOF([int], [4]) AC_CHECK_SIZEOF([long int], [4]) AC_CHECK_SIZEOF([long long int], [8]) # Sanity check long long for some platforms (AIX) if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then ac_cv_sizeof_long_long_int=0 fi # compute LLONG_MIN and LLONG_MAX if we don't know them. if test -z "$have_llong_max"; then AC_MSG_CHECKING([for max value of long long]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include /* Why is this so damn hard? */ #ifdef __GNUC__ # undef __GNUC__ #endif #define __USE_ISOC99 #include #define DATA "conftest.llminmax" #define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) /* * printf in libc on some platforms (eg old Tru64) does not understand %lld so * we do this the hard way. */ static int fprint_ll(FILE *f, long long n) { unsigned int i; int l[sizeof(long long) * 8]; if (n < 0) if (fprintf(f, "-") < 0) return -1; for (i = 0; n != 0; i++) { l[i] = my_abs(n % 10); n /= 10; } do { if (fprintf(f, "%d", l[--i]) < 0) return -1; } while (i != 0); if (fprintf(f, " ") < 0) return -1; return 0; } ]], [[ FILE *f; long long i, llmin, llmax = 0; if((f = fopen(DATA,"w")) == NULL) exit(1); #if defined(LLONG_MIN) && defined(LLONG_MAX) fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); llmin = LLONG_MIN; llmax = LLONG_MAX; #else fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); /* This will work on one's complement and two's complement */ for (i = 1; i > llmax; i <<= 1, i++) llmax = i; llmin = llmax + 1LL; /* wrap */ #endif /* Sanity check */ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax || llmax - 1 > llmax || llmin == llmax || llmin == 0 || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { fprintf(f, "unknown unknown\n"); exit(2); } if (fprint_ll(f, llmin) < 0) exit(3); if (fprint_ll(f, llmax) < 0) exit(4); if (fclose(f) < 0) exit(5); exit(0); ]])], [ llong_min=`$AWK '{print $1}' conftest.llminmax` llong_max=`$AWK '{print $2}' conftest.llminmax` AC_MSG_RESULT([$llong_max]) AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], [max value of long long calculated by configure]) AC_MSG_CHECKING([for min value of long long]) AC_MSG_RESULT([$llong_min]) AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], [min value of long long calculated by configure]) ], [ AC_MSG_RESULT([not found]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) fi # More checks for data types TYPE_SOCKLEN_T AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; ]])], [ ac_cv_have_struct_sockaddr_storage="yes" ], [ ac_cv_have_struct_sockaddr_storage="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], [1], [define if you have struct sockaddr_storage data type]) fi AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], [ ac_cv_have_struct_sockaddr_in6="yes" ], [ ac_cv_have_struct_sockaddr_in6="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_IN6], [1], [define if you have struct sockaddr_in6 data type]) fi AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], [ ac_cv_have_struct_in6_addr="yes" ], [ ac_cv_have_struct_in6_addr="no" ]) ]) if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_IN6_ADDR], [1], [define if you have struct in6_addr data type]) dnl Now check for sin6_scope_id AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], , , [ #ifdef HAVE_SYS_TYPES_H #include #endif #include ]) fi AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], [ ac_cv_have_struct_addrinfo="yes" ], [ ac_cv_have_struct_addrinfo="no" ]) ]) if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_ADDRINFO], [1], [define if you have struct addrinfo data type]) fi AC_CACHE_CHECK([for struct timeval], ac_cv_have_struct_timeval, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ struct timeval tv; tv.tv_sec = 1;]])], [ ac_cv_have_struct_timeval="yes" ], [ ac_cv_have_struct_timeval="no" ]) ]) if test "x$ac_cv_have_struct_timeval" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_TIMEVAL], [1], [define if you have struct timeval]) have_struct_timeval=1 fi #l3561 AC_CHECK_MEMBERS([struct passwd.pw_gecos], [], [], [[ #include #include ]]) AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], ac_cv_have_ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.ss_family = 1; ]])], [ ac_cv_have_ss_family_in_struct_ss="yes" ], [ ac_cv_have_ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE_SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi AC_CACHE_CHECK([for __ss_family field in struct sockaddr_storage], ac_cv_have___ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.__ss_family = 1; ]])], [ ac_cv_have___ss_family_in_struct_ss="yes" ], [ ac_cv_have___ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE___SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi dnl make sure we're using the real structure members and not defines AC_CACHE_CHECK([for msg_control field in struct msghdr], ac_cv_have_control_in_msghdr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ #ifdef msg_control #error "msg_control is a macro" exit(1); #endif struct msghdr m; m.msg_control = 0; exit(0); ]])], [ ac_cv_have_control_in_msghdr="yes" ], [ ac_cv_have_control_in_msghdr="no" ] ) ]) if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], [Define if your system uses ancillary data style file descriptor passing]) fi AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ extern char *__progname; printf("%s", __progname); ]])], [ ac_cv_libc_defines___progname="yes" ], [ ac_cv_libc_defines___progname="no" ]) ]) if test "x$ac_cv_libc_defines___progname" = "xyes" ; then AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) fi #l4010 # Looking for programs, paths and files PRIVSEP_PATH=/var/empty AC_ARG_WITH([path-empty], [ --with-path-empty=PATH Specify path to empty directory (default=/var/empty)], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then PRIVSEP_PATH=$withval fi ] ) AC_SUBST([PRIVSEP_PATH]) #l4022 #l4045 STRIP_OPT=-s AC_ARG_ENABLE([strip], [ --disable-strip Disable calling strip(1) on install], [ if test "x$enableval" = "xno" ; then STRIP_OPT= fi ] ) AC_SUBST([STRIP_OPT]) #l4054 #l4176 (customized s/ssh.1/smtpd/smtpd.8/) # Options from here on. Some of these are preset by platform above AC_ARG_WITH([mantype], [ --with-mantype=man|cat|doc Set man page type], [ case "$withval" in man|cat|doc) MANTYPE=$withval ;; *) AC_MSG_ERROR([invalid man type: $withval]) ;; esac ] ) if test -z "$MANTYPE"; then TestPath="/usr/bin${PATH_SEPARATOR}/usr/ucb" AC_PATH_PROGS([NROFF], [nroff awf], [/bin/false], [$TestPath]) if ${NROFF} -mdoc ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -man ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then MANTYPE=man else MANTYPE=cat fi fi AC_SUBST([MANTYPE]) if test "$MANTYPE" = "doc"; then mansubdir=man; else mansubdir=$MANTYPE; fi AC_SUBST([mansubdir]) #l4207 case "$host" in *-*-openbsd*) pkglibexecdir="$libexecdir/smtpd" ;; *) pkglibexecdir="$libexecdir/opensmtpd" ;; esac AC_SUBST([pkglibexecdir]) # # individual modules # HAVE_FILTER_MONKEY=no AC_ARG_WITH([filter-monkey], [ --with-filter-monkey Enable filter monkey], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_FILTER_MONKEY], [1], [Define if you have filter monkey]) HAVE_FILTER_MONKEY=yes fi ] ) AM_CONDITIONAL([HAVE_FILTER_MONKEY], [test $HAVE_FILTER_MONKEY = yes]) HAVE_FILTER_STUB=no AC_ARG_WITH([filter-stub], [ --with-filter-stub Enable filter stub], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_FILTER_STUB], [1], [Define if you have filter stub]) HAVE_FILTER_STUB=yes fi ] ) AM_CONDITIONAL([HAVE_FILTER_STUB], [test $HAVE_FILTER_STUB = yes]) HAVE_FILTER_TRACE=no AC_ARG_WITH([filter-trace], [ --with-filter-trace Enable filter trace], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_FILTER_TRACE], [1], [Define if you have filter trace]) HAVE_FILTER_TRACE=yes fi ] ) AM_CONDITIONAL([HAVE_FILTER_TRACE], [test $HAVE_FILTER_TRACE = yes]) HAVE_FILTER_VOID=no AC_ARG_WITH([filter-void], [ --with-filter-void Enable filter void], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_FILTER_VOID], [1], [Define if you have filter void]) HAVE_FILTER_VOID=yes fi ] ) AM_CONDITIONAL([HAVE_FILTER_VOID], [test $HAVE_FILTER_VOID = yes]) # # QUEUES # HAVE_QUEUE_NULL=no AC_ARG_WITH([queue-null], [ --with-queue-null Enable queue null], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_QUEUE_NULL], [1], [Define if you have queue null]) HAVE_QUEUE_NULL=yes fi ] ) AM_CONDITIONAL([HAVE_QUEUE_NULL], [test $HAVE_QUEUE_NULL = yes]) HAVE_QUEUE_PYTHON=no AC_ARG_WITH([queue-python], [ --with-queue-python Enable queue python], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_QUEUE_PYTHON], [1], [Define if you have queue python]) HAVE_QUEUE_PYTHON=yes need_python=yes fi ] ) AM_CONDITIONAL([HAVE_QUEUE_PYTHON], [test $HAVE_QUEUE_PYTHON = yes]) HAVE_QUEUE_RAM=no AC_ARG_WITH([queue-ram], [ --with-queue-ram Enable queue ram], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_QUEUE_RAM], [1], [Define if you have queue ram]) HAVE_QUEUE_RAM=yes fi ] ) AM_CONDITIONAL([HAVE_QUEUE_RAM], [test $HAVE_QUEUE_RAM = yes]) HAVE_QUEUE_STUB=no AC_ARG_WITH([queue-stub], [ --with-queue-stub Enable queue stub], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_QUEUE_STUB], [1], [Define if you have queue stub]) HAVE_QUEUE_STUB=yes fi ] ) AM_CONDITIONAL([HAVE_QUEUE_STUB], [test $HAVE_QUEUE_STUB = yes]) # # TABLES # # Whether to enable *experimental* LDAP support HAVE_TABLE_LDAP=no AC_ARG_WITH([table-ldap], [ --with-table-ldap Enable table LDAP], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_LDAP], [1], [Define if you have experimental LDAP support]) HAVE_TABLE_LDAP=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_LDAP], [test $HAVE_TABLE_LDAP = yes]) # Whether to enable *experimental* MySQL support HAVE_TABLE_MYSQL=no AC_ARG_WITH([table-mysql], [ --with-table-mysql Enable table MySQL], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_MYSQL], [1], [Define if you have experimental MySQL support]) HAVE_TABLE_MYSQL=yes AC_PATH_PROG([MYSQL_CONFIG], [mysql_config], , $PATH) if test "x$MYSQL_CONFIG" = "x"; then AC_MSG_RESULT([mysql_config not found, proceeding with default settings]) else AC_MSG_CHECKING([for MySQL lib path]) MYSQL_LIBS=`"$MYSQL_CONFIG" --libs | sed -n 's#.*-L\(\"/[^\"]*\"\).*#\1#p;s#.*-L\(/[^[:space:]]*\).*#\1#p;'` if test "x$MYSQL_LIBS" = "x"; then AC_MSG_RESULT([not found, proceeding with default settings]) else AC_MSG_RESULT([found $MYSQL_LIBS]) LDFLAGS="-L${MYSQL_LIBS} ${LDFLAGS}" fi fi fi ] ) AM_CONDITIONAL([HAVE_TABLE_MYSQL], [test $HAVE_TABLE_MYSQL = yes]) # Whether to enable *experimental* PostgreSQL support HAVE_TABLE_POSTGRES=no AC_ARG_WITH([table-postgres], [ --with-table-postgres Enable table PostgreSQL], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_POSTGRES], [1], [Define if you have experimental PostgreSQL support]) HAVE_TABLE_POSTGRES=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_POSTGRES], [test $HAVE_TABLE_POSTGRES = yes]) # Whether to enable *experimental* Redis support HAVE_TABLE_REDIS=no AC_ARG_WITH([table-redis], [ --with-table-redis Enable table Redis], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_REDIS], [1], [Define if you have experimental Redis support]) HAVE_TABLE_REDIS=yes need_redis=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_REDIS], [test $HAVE_TABLE_REDIS = yes]) # Whether to enable *experimental* Redis support HAVE_TABLE_SOCKETMAP=no AC_ARG_WITH([table-socketmap], [ --with-table-socketmap Enable table socketmap], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_SOCKETMAP], [1], [Define if you have experimental socketmap support]) HAVE_TABLE_SOCKETMAP=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_SOCKETMAP], [test $HAVE_TABLE_SOCKETMAP = yes]) # Whether to enable table_passwd HAVE_TABLE_PASSWD=no AC_ARG_WITH([table-passwd], [ --with-table-passwd Enable table passwd], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_PASSWD], [1], [Define if you want to enable table passwd]) HAVE_TABLE_PASSWD=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_PASSWD], [test $HAVE_TABLE_PASSWD = yes]) # Whether to enable table_python HAVE_TABLE_PYTHON=no AC_ARG_WITH([table-python], [ --with-table-python Enable table python], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_PYTHON], [1], [Define if you want to enable table python]) HAVE_TABLE_PYTHON=yes need_python=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_PYTHON], [test $HAVE_TABLE_PYTHON = yes]) HAVE_TABLE_SQLITE=no AC_ARG_WITH([table-sqlite], [ --with-table-sqlite Enable table SQLite], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_SQLITE], [1], [Define if you have SQLite support]) HAVE_TABLE_SQLITE=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_SQLITE], [test $HAVE_TABLE_SQLITE = yes]) HAVE_TABLE_STUB=no AC_ARG_WITH([table-stub], [ --with-table-stub Enable table stub], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TABLE_STUB], [1], [Define if you have stub support]) HAVE_TABLE_STUB=yes fi ] ) AM_CONDITIONAL([HAVE_TABLE_STUB], [test $HAVE_TABLE_STUB = yes]) # # TOOLS # HAVE_TOOL_STATS=no AC_ARG_WITH([tool-stats], [ --with-tool-stats Enable tool stats], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_TOOL_STATS], [1], [Define if you have stats support]) HAVE_TOOL_STATS=yes fi ] ) AM_CONDITIONAL([HAVE_TOOL_STATS], [test $HAVE_TOOL_STATS = yes]) # # SCHEDULERS # HAVE_SCHEDULER_RAM=no AC_ARG_WITH([scheduler-ram], [ --with-scheduler-ram Enable scheduler ram], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_SCHEDULER_RAM], [1], [Define if you have scheduler ram]) HAVE_SCHEDULER_RAM=yes fi ] ) AM_CONDITIONAL([HAVE_SCHEDULER_RAM], [test $HAVE_SCHEDULER_RAM = yes]) HAVE_SCHEDULER_STUB=no AC_ARG_WITH([scheduler-stub], [ --with-scheduler-stub Enable scheduler stub], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_SCHEDULER_STUB], [1], [Define if you have scheduler stub]) HAVE_SCHEDULER_STUB=yes fi ] ) AM_CONDITIONAL([HAVE_SCHEDULER_STUB], [test $HAVE_SCHEDULER_STUB = yes]) HAVE_SCHEDULER_PYTHON=no AC_ARG_WITH([scheduler-python], [ --with-scheduler-python Enable scheduler python], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_SCHEDULER_PYTHON], [1], [Define if you have scheduler python]) HAVE_SCHEDULER_PYTHON=yes need_python=yes fi ] ) AM_CONDITIONAL([HAVE_SCHEDULER_PYTHON], [test $HAVE_SCHEDULER_PYTHON = yes]) AC_ARG_WITH([python], [ --with-python=PATH Specify prefix of python installation ], [ if test "x$withval" != "xno" ; then python_prefix="${withval}" fi ] ) AC_ARG_WITH([python-type], [ --with-python-type=PATH Specify the type of python used ], [ if test "x$withval" != "xno" ; then python_type="${withval}" fi ] ) AC_ARG_WITH([perl], [ --with-perl=PATH Specify prefix of perl installation ], [ if test "x$withval" != "xno" ; then perl_prefix="${withval}" fi ] ) AC_ARG_WITH([lua], [ --with-lua=PATH Specify prefix of lua installation ], [ if test "x$withval" != "xno" ; then lua_prefix="${withval}" fi ] ) AC_ARG_WITH([lua-type], [ --with-lua-type=PATH Specify the type of lua used (automatic if unspecified, otherwise luajit, lua, or another version) ], [ if test "x$withval" != "xno" ; then lua_type="${withval}" fi ] ) # # check for python # PYTHON_CPPFLAGS= PYTHON_LDFLAGS= if test x"${need_python}" = x"yes"; then PYTHON_CONFIG= if test x"${python_type}" = x""; then python_type="python" fi for path in /usr/local/bin /usr/bin; do if test -f ${path}/${python_type}-config; then PYTHON_CONFIG=${path}/${python_type}-config break fi done # if python provided --with-python, override if test x"${with_python}" != x""; then PYTHON_CONFIG="${python_prefix}/bin/${python_type}-config" fi if ! test -f $PYTHON_CONFIG; then AC_MSG_ERROR([*** $PYTHON_CONFIG could not be found, please install or use --with-python ***]) fi PYTHON_CPPFLAGS="`$PYTHON_CONFIG --includes`" PYTHON_LDFLAGS="-L`$PYTHON_CONFIG --prefix`/lib `$PYTHON_CONFIG --libs`" fi AC_SUBST([PYTHON_CPPFLAGS]) AC_SUBST([PYTHON_LDFLAGS]) # # check for perl # PERL_CPPFLAGS= PERL_LDFLAGS= if test x"${need_perl}" = x"yes"; then PERL_CONFIG= for path in /usr/local/bin /usr/bin; do if test -f ${path}/perl; then PERL_CONFIG=${path}/perl break fi done # if perl provided --with-perl, override if test x"${with_perl}" != x""; then PERL_CONFIG="${perl_prefix}/bin/perl" fi if ! test -f $PERL_CONFIG; then AC_MSG_ERROR([*** $PERL_CONFIG could not be found, please install or use --with-perl ***]) fi PERL_CPPFLAGS="`perl -MExtUtils::Embed -e ccopts`" PERL_LDFLAGS="`perl -MExtUtils::Embed -e ldopts`" fi AC_SUBST([PERL_CPPFLAGS]) AC_SUBST([PERL_LDFLAGS]) # # check for lua # LUA_CPPFLAGS= LUA_LDFLAGS= if test x"${need_lua}" = x"yes"; then LUA_CONFIG= for path in /usr/local/bin /usr/bin; do if test -f ${path}/pkg-config; then LUA_CONFIG=${path}/pkg-config break fi done # if lua provided --with-lua, override if test x"${with_lua}" != x""; then LUA_CONFIG="${lua_prefix}/bin/pkg-config" fi if ! test -f $LUA_CONFIG; then if test x"${lua_type}" = x""; then for i in luajit lua lua5.2 lua-5.2 lua5.1 lua-5.1; do if test -d ${lua_prefix:-/usr/local}/include/${i}*; then lua_type=$i break fi done fi LUA_CPPFLAGS="-I${lua_prefix:-/usr/local}/include/${lua_type}" LUA_LDFLAGS="-L${lua_prefix:-/usr/local}/lib -l${lua_type} -lm" else if test x"${lua_type}" = x""; then for i in luajit lua lua5.2 lua-5.2 lua5.1 lua-5.1; do if $LUA_CONFIG --exists $i 2>/dev/null; then lua_type=$i break fi done fi LUA_CPPFLAGS="`$LUA_CONFIG --cflags $lua_type`" LUA_LDFLAGS="`$LUA_CONFIG --libs $lua_type`" fi fi AC_SUBST([LUA_CPPFLAGS]) AC_SUBST([LUA_LDFLAGS]) # # check for hiredis # REDIS_CPPFLAGS= REDIS_LDFLAGS= if test x"${need_redis}" = x"yes"; then REDIS_CONFIG= for path in /usr/local/bin /usr/bin; do if test -f ${path}/pkg-config; then REDIS_CONFIG=${path}/pkg-config break fi done # if redis provided --with-redis, override if test x"${with_hiredis}" != x""; then REDIS_CONFIG="${redis_prefix}/bin/pkg-config" fi if ! test -f $REDIS_CONFIG; then AC_MSG_ERROR([*** $REDIS_CONFIG could not be found, please install or use --with-redis ***]) fi REDIS_CPPFLAGS="`$REDIS_CONFIG --cflags hiredis`" REDIS_LDFLAGS="`$REDIS_CONFIG --libs hiredis`" fi AC_SUBST([REDIS_CPPFLAGS]) AC_SUBST([REDIS_LDFLAGS]) ##chl (based on OpenSSL checks, see above) # Search for libevent saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" AC_ARG_WITH([libevent], [ --with-libevent=PATH Specify path to libevent installation ], [ if test "x$withval" != "xno" ; then case "$withval" in # Relative paths ./*|../*) withval="`pwd`/$withval" esac if test -d "$withval/lib"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi elif test -d "$withval/lib64"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" else LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" fi else if test -n "${need_dash_r}"; then LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi need_libevent_autodetect=no fi ] ) if test x"${need_libevent_autodetect}" != x"no"; then for path in /usr/local /usr; do if test -f "${path}/include/event.h"; then CPPFLAGS="-I${path}/include ${CPPFLAGS}" LDFLAGS="-L${path}/lib ${LDFLAGS}" fi done fi LIBS="-levent $LIBS" AC_MSG_CHECKING([if programs using libevent functions will link]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ event_base_new(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ] ) LIBS="$LIBS ${SMTPDLIBS}" ##end of chl AC_CONFIG_FILES([Makefile openbsd-compat/Makefile extras/Makefile extras/queues/Makefile extras/queues/queue-null/Makefile extras/queues/queue-python/Makefile extras/queues/queue-ram/Makefile extras/queues/queue-stub/Makefile extras/schedulers/Makefile extras/schedulers/scheduler-python/Makefile extras/schedulers/scheduler-ram/Makefile extras/schedulers/scheduler-stub/Makefile extras/tables/Makefile extras/tables/table-passwd/Makefile extras/tables/table-ldap/Makefile extras/tables/table-mysql/Makefile extras/tables/table-postgres/Makefile extras/tables/table-python/Makefile extras/tables/table-redis/Makefile extras/tables/table-socketmap/Makefile extras/tables/table-sqlite/Makefile extras/tables/table-stub/Makefile ]) #l4761 AC_OUTPUT # Print summary of options # Someone please show me a better way :) A=`eval echo ${prefix}` ; A=`eval echo ${A}` B=`eval echo ${bindir}` ; B=`eval echo ${B}` C=`eval echo ${sbindir}` ; C=`eval echo ${C}` D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` E=`eval echo ${mandir}/${mansubdir}X` ; E=`eval echo ${E}` F=`eval echo ${PRIVSEP_PATH}` ; F=`eval echo ${F}` echo "" echo "OpenSMTPD-extras have been configured with the following options:" echo " User binaries: $B" echo " System binaries: $C" echo " Configuration files: $D" echo " Manual pages: $E" echo " Privilege separation chroot path: $F" echo " Package libexecdir: ${pkglibexecdir}" echo " Manpage format: $MANTYPE" echo "" echo " Host: ${host}" echo " Compiler: ${CC}" echo " Compiler flags: ${CFLAGS}" echo "Preprocessor flags: ${CPPFLAGS}" echo " Linker flags: ${LDFLAGS}" echo " Libraries: ${LIBS}" echo "" if test x"${need_python}" = x"yes"; then echo "Built with Python support:" echo " CPPFLAGS: ${PYTHON_CPPFLAGS}" echo " LDFLAGS: ${PYTHON_LDFLAGS}" echo "" fi if test x"${need_perl}" = x"yes"; then echo "Built with Perl support:" echo " CPPFLAGS: ${PERL_CPPFLAGS}" echo " LDFLAGS: ${PERL_LDFLAGS}" echo "" fi if test x"${need_lua}" = x"yes"; then echo "Built with Lua support:" echo " CPPFLAGS: ${LUA_CPPFLAGS}" echo " LDFLAGS: ${LUA_LDFLAGS}" echo "" fi if test x"${need_redis}" = x"yes"; then echo "Built with redis support:" echo " CPPFLAGS: ${REDIS_CPPFLAGS}" echo " LDFLAGS: ${REDIS_LDFLAGS}" echo "" fi OpenSMTPD-extras-6.6.0/extras/000077500000000000000000000000001355240505200160475ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/Makefile.am000066400000000000000000000000661355240505200201050ustar00rootroot00000000000000SUBDIRS= queues SUBDIRS+= schedulers SUBDIRS+= tables OpenSMTPD-extras-6.6.0/extras/queues/000077500000000000000000000000001355240505200173565ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/Makefile.am000066400000000000000000000003111355240505200214050ustar00rootroot00000000000000SUBDIRS = if HAVE_QUEUE_NULL SUBDIRS+= queue-null endif if HAVE_QUEUE_PYTHON SUBDIRS+= queue-python endif if HAVE_QUEUE_RAM SUBDIRS+= queue-ram endif if HAVE_QUEUE_STUB SUBDIRS+= queue-stub endif OpenSMTPD-extras-6.6.0/extras/queues/README000066400000000000000000000000001355240505200202240ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-null/000077500000000000000000000000001355240505200214525ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-null/Makefile.am000066400000000000000000000003051355240505200235040ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/queue.mk pkglibexec_PROGRAMS = queue-null queue_null_SOURCES = $(SRCS) queue_null_SOURCES += $(queues_srcdir)/queue-null/queue_null.c OpenSMTPD-extras-6.6.0/extras/queues/queue-null/queue_null.c000066400000000000000000000057631355240505200240070ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include static int queue_null_message_create(uint32_t *msgid) { *msgid = queue_generate_msgid(); return 1; } static int queue_null_message_commit(uint32_t msgid, const char *path) { return 1; } static int queue_null_message_delete(uint32_t msgid) { return 1; } static int queue_null_message_fd_r(uint32_t msgid) { return -1; } static int queue_null_message_corrupt(uint32_t msgid) { return 0; } static int queue_null_message_uncorrupt(uint32_t msgid) { return 0; } static int queue_null_envelope_create(uint32_t msgid, const char *buf, size_t len, uint64_t *evpid) { *evpid = queue_generate_evpid(msgid); return 1; } static int queue_null_envelope_delete(uint64_t evpid) { return 1; } static int queue_null_envelope_update(uint64_t evpid, const char *buf, size_t len) { return 1; } static int queue_null_envelope_load(uint64_t evpid, char *buf, size_t len) { return 0; } static int queue_null_envelope_walk(uint64_t *evpid, char *buf, size_t len) { return -1; } static int queue_null_message_walk(uint64_t *evpid, char *buf, size_t len, uint32_t msgid, int *done, void **data) { return -1; } static int queue_null_init(int server) { queue_api_on_message_create(queue_null_message_create); queue_api_on_message_commit(queue_null_message_commit); queue_api_on_message_delete(queue_null_message_delete); queue_api_on_message_fd_r(queue_null_message_fd_r); queue_api_on_message_corrupt(queue_null_message_corrupt); queue_api_on_message_uncorrupt(queue_null_message_uncorrupt); queue_api_on_envelope_create(queue_null_envelope_create); queue_api_on_envelope_delete(queue_null_envelope_delete); queue_api_on_envelope_update(queue_null_envelope_update); queue_api_on_envelope_load(queue_null_envelope_load); queue_api_on_envelope_walk(queue_null_envelope_walk); queue_api_on_message_walk(queue_null_message_walk); return 1; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; queue_null_init(1); queue_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/queues/queue-python/000077500000000000000000000000001355240505200220215ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-python/Makefile.am000066400000000000000000000004121355240505200240520ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/queue.mk pkglibexec_PROGRAMS = queue-python queue_python_SOURCES = $(SRCS) queue_python_SOURCES += $(queues_srcdir)/queue-python/queue_python.c CFLAGS += ${PYTHON_CPPFLAGS} LDFLAGS += ${PYTHON_LDFLAGS} OpenSMTPD-extras-6.6.0/extras/queues/queue-python/queue_python.c000066400000000000000000000255111355240505200247160ustar00rootroot00000000000000/* * Copyright (c) 2014 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include /* _GNU_SOURCE is not properly protected in Python.h ... */ #undef _GNU_SOURCE #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include static PyObject *py_message_create; static PyObject *py_message_commit; static PyObject *py_message_delete; static PyObject *py_message_fd_r; static PyObject *py_message_corrupt; static PyObject *py_message_uncorrupt; static PyObject *py_envelope_create; static PyObject *py_envelope_delete; static PyObject *py_envelope_update; static PyObject *py_envelope_load; static PyObject *py_envelope_walk; static PyObject *py_message_walk; static void check_err(const char *name) { if (PyErr_Occurred()) { PyErr_Print(); fatalx("fatal: error in %s handler", name); } } static PyObject * dispatch(PyObject *handler, PyObject *args) { PyObject *ret; ret = PyObject_CallObject(handler, args); Py_DECREF(args); if (PyErr_Occurred()) { PyErr_Print(); fatalx("fatal: exception"); } return ret; } static int get_int(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsLong(o); if (PyInt_Check(o)) return PyInt_AsLong(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static size_t get_uint32_t(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsUnsignedLong(o); if (PyInt_Check(o)) return PyInt_AsUnsignedLongMask(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static size_t get_uint64_t(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsUnsignedLongLong(o); if (PyInt_Check(o)) return PyInt_AsUnsignedLongLongMask(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static int queue_python_message_create(uint32_t *msgid) { PyObject *py_ret; py_ret = dispatch(py_message_create, Py_BuildValue("()")); *msgid = get_uint32_t(py_ret); Py_DECREF(py_ret); check_err("message_create"); return *msgid ? 1 : 0; } static int queue_python_message_commit(uint32_t msgid, const char *path) { PyObject *py_ret; int ret; py_ret = dispatch(py_message_commit, Py_BuildValue("ks", (unsigned long)msgid, path)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("message_commit"); return ret ? 1 : 0; } static int queue_python_message_delete(uint32_t msgid) { PyObject *py_ret; int ret; py_ret = dispatch(py_message_delete, Py_BuildValue("(k)", (unsigned long)msgid)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("message_delete"); return ret ? 1 : 0; } static int queue_python_message_fd_r(uint32_t msgid) { PyObject *py_ret; int ret; py_ret = dispatch(py_message_fd_r, Py_BuildValue("(k)", (unsigned long)msgid)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("message_fd_r"); return ret; } static int queue_python_message_corrupt(uint32_t msgid) { PyObject *py_ret; int ret; py_ret = dispatch(py_message_corrupt, Py_BuildValue("(k)", (unsigned long)msgid)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("message_corrupt"); return ret; } static int queue_python_message_uncorrupt(uint32_t msgid) { PyObject *py_ret; int ret; py_ret = dispatch(py_message_uncorrupt, Py_BuildValue("(k)", (unsigned long)msgid)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("message_uncorrupt"); return ret; } static int queue_python_envelope_create(uint32_t msgid, const char *buf, size_t len, uint64_t *evpid) { PyObject *py_ret; py_ret = dispatch(py_envelope_create, Py_BuildValue("ks#", (unsigned long)msgid, (const char *)buf, (int)len)); *evpid = get_uint64_t(py_ret); Py_DECREF(py_ret); check_err("envelope_create"); return *evpid ? 1 : 0; } static int queue_python_envelope_delete(uint64_t evpid) { PyObject *py_ret; int ret; py_ret = dispatch(py_envelope_delete, Py_BuildValue("(K)", (unsigned long)evpid)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("envelope_delete"); return ret ? 1 : 0; } static int queue_python_envelope_update(uint64_t evpid, const char *buf, size_t len) { PyObject *py_ret; int ret; py_ret = dispatch(py_envelope_update, Py_BuildValue("Ks#", (unsigned long long)evpid, (const char *)buf, (int)len)); ret = get_int(py_ret); Py_DECREF(py_ret); check_err("envelope_create"); return ret ? 1 : 0; } static int queue_python_envelope_load(uint64_t evpid, char *buf, size_t len) { PyObject *py_ret; Py_buffer view; int ret; py_ret = dispatch(py_envelope_load, Py_BuildValue("K", (unsigned long long)evpid)); ret = PyObject_GetBuffer(py_ret, &view, PyBUF_SIMPLE); Py_DECREF(py_ret); if (ret == -1) return 0; if ((size_t)view.len >= len) { PyBuffer_Release(&view); return 0; } memset(buf, 0, len); memcpy(buf, view.buf, view.len); ret = view.len; PyBuffer_Release(&view); check_err("envelope_load"); return ret; } static int queue_python_envelope_walk(uint64_t *evpid, char *buf, size_t len) { static uint64_t curevpid = 0; PyObject *py_ret; Py_buffer py_view; int ret; py_ret = dispatch(py_envelope_walk, Py_BuildValue("(K)", (unsigned long)curevpid)); if (py_ret == Py_None) return -1; if (!PyTuple_Check(py_ret) || PyTuple_Size(py_ret) != 2) { PyErr_SetString(PyExc_TypeError, "2-elements tuple expected"); ret = -1; } else { curevpid = *evpid = get_uint64_t(PyTuple_GetItem(py_ret, 0)); ret = PyObject_GetBuffer(PyTuple_GetItem(py_ret, 1), &py_view, PyBUF_SIMPLE); } Py_DECREF(py_ret); if (ret == -1) return 0; if ((size_t)py_view.len >= len) { PyBuffer_Release(&py_view); return 0; } memset(buf, 0, len); memcpy(buf, py_view.buf, py_view.len); ret = py_view.len; PyBuffer_Release(&py_view); check_err("envelope_walk"); return ret; } static int queue_python_message_walk(uint64_t *evpid, char *buf, size_t len, uint32_t msgid, int *done, void **data) { static uint64_t curevpid = 0; PyObject *py_ret; Py_buffer py_view; int ret; py_ret = dispatch(py_envelope_walk, Py_BuildValue("(K)", (unsigned long)curevpid)); if (py_ret == Py_None) return -1; if (!PyTuple_Check(py_ret) || PyTuple_Size(py_ret) != 2) { PyErr_SetString(PyExc_TypeError, "2-elements tuple expected"); ret = -1; } else { curevpid = *evpid = get_uint64_t(PyTuple_GetItem(py_ret, 0)); ret = PyObject_GetBuffer(PyTuple_GetItem(py_ret, 1), &py_view, PyBUF_SIMPLE); } Py_DECREF(py_ret); if (ret == -1) return 0; if ((size_t)py_view.len >= len) { PyBuffer_Release(&py_view); return 0; } memset(buf, 0, len); memcpy(buf, py_view.buf, py_view.len); ret = py_view.len; PyBuffer_Release(&py_view); check_err("message_walk"); return ret; } static int queue_python_init(int server) { queue_api_on_message_create(queue_python_message_create); queue_api_on_message_commit(queue_python_message_commit); queue_api_on_message_delete(queue_python_message_delete); queue_api_on_message_fd_r(queue_python_message_fd_r); queue_api_on_message_corrupt(queue_python_message_corrupt); queue_api_on_message_uncorrupt(queue_python_message_uncorrupt); queue_api_on_envelope_create(queue_python_envelope_create); queue_api_on_envelope_delete(queue_python_envelope_delete); queue_api_on_envelope_update(queue_python_envelope_update); queue_api_on_envelope_load(queue_python_envelope_load); queue_api_on_envelope_walk(queue_python_envelope_walk); queue_api_on_message_walk(queue_python_message_walk); return 1; } static PyMethodDef py_methods[] = { { NULL, NULL, 0, NULL } }; static char * loadfile(const char * path) { FILE *f; off_t oz; size_t sz; char *buf; if ((f = fopen(path, "r")) == NULL) fatal("fopen"); if (fseek(f, 0, SEEK_END) == -1) fatal("fseek"); oz = ftello(f); if (fseek(f, 0, SEEK_SET) == -1) fatal("fseek"); if (oz >= (off_t)SSIZE_MAX) fatal("too big"); sz = oz; buf = xmalloc(sz + 1, "loadfile"); if (fread(buf, 1, sz, f) != sz) fatal("fread"); buf[sz] = '\0'; fclose(f); return buf; } int main(int argc, char **argv) { int ch; char *path, *buf; PyObject *self, *code, *module; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc == 0) fatalx("missing path"); path = argv[0]; Py_Initialize(); self = Py_InitModule("queue", py_methods); buf = loadfile(path); code = Py_CompileString(buf, path, Py_file_input); free(buf); if (code == NULL) { PyErr_Print(); fatalx("failed to compile %s", path); } module = PyImport_ExecCodeModuleEx("queue_python", code, path); if (module == NULL) { PyErr_Print(); fatalx("failed to install module %s", path); } log_debug("debug: starting..."); if ((py_message_create = PyObject_GetAttrString(module, "message_create")) == NULL) goto nosuchmethod; if ((py_message_commit = PyObject_GetAttrString(module, "message_commit")) == NULL) goto nosuchmethod; if ((py_message_delete = PyObject_GetAttrString(module, "message_delete")) == NULL) goto nosuchmethod; if ((py_message_fd_r = PyObject_GetAttrString(module, "message_fd_r")) == NULL) goto nosuchmethod; if ((py_message_corrupt = PyObject_GetAttrString(module, "message_corrupt")) == NULL) goto nosuchmethod; if ((py_message_uncorrupt = PyObject_GetAttrString(module, "message_uncorrupt")) == NULL) goto nosuchmethod; if ((py_envelope_create = PyObject_GetAttrString(module, "envelope_create")) == NULL) goto nosuchmethod; if ((py_envelope_delete = PyObject_GetAttrString(module, "envelope_delete")) == NULL) goto nosuchmethod; if ((py_envelope_update = PyObject_GetAttrString(module, "envelope_update")) == NULL) goto nosuchmethod; if ((py_envelope_load = PyObject_GetAttrString(module, "envelope_load")) == NULL) goto nosuchmethod; if ((py_envelope_walk = PyObject_GetAttrString(module, "envelope_walk")) == NULL) goto nosuchmethod; if ((py_message_walk = PyObject_GetAttrString(module, "message_walk")) == NULL) goto nosuchmethod; queue_python_init(1); queue_api_no_chroot(); queue_api_dispatch(); log_debug("debug: exiting"); return 0; nosuchmethod: PyErr_Print(); return 1; } OpenSMTPD-extras-6.6.0/extras/queues/queue-python/scripts/000077500000000000000000000000001355240505200235105ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-python/scripts/queue_ram.py000077500000000000000000000062321355240505200260530ustar00rootroot00000000000000# # Copyright (c) 2014 Gilles Chehade # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import random import tempfile # import time import os queue = {} def generate_msgid(): while True: msgid = random.randint(1, 0xffffffff) if msgid not in queue: return msgid def generate_evpid(msgid): if msgid not in queue: return 0 while True: evpid = random.randint(1, 0xffffffff) if evpid not in queue[msgid]: return (msgid << 32) | evpid # message_create must allocate a message entry and return a stricly positive # 32-bit unique message identifier. # def message_create(): msgid = generate_msgid() queue[msgid] = {'envelopes': {}, 'message': None} return msgid # message_commit must write content of "path" into message and return # either 0 for failure, or 1 for success. # def message_commit(msgid, path): queue[msgid]['message'] = open(path, 'rb').read() os.unlink(path) return 1 # message_delete must remove a message and all associate envelopes, # returns either 0 for failure, or 1 for success # def message_delete(msgid): del queue[msgid] return 1 # message_fd_r must return a readable file descriptor pointing to the # content of the message, or -1 in case of failure # def message_fd_r(msgid): tmp = tempfile.TemporaryFile(mode="w+") tmp.write(queue[msgid]['message']) tmp.flush() tmp.seek(0, os.SEEK_SET) return os.dup(tmp.fileno()) def message_corrupt(): return 1 # envelope_create must create an envelope within a message and return a # 64-bit unique envelope identifier where upper 32-bit == msgid # def envelope_create(msgid, envelope): evpid = generate_evpid(msgid) if evpid == 0: return 0 queue[msgid]['envelopes'][evpid] = envelope return evpid def envelope_delete(evpid): msgid = (evpid >> 32) & 0xffffffff del queue[msgid]['envelopes'][evpid] if len(queue[msgid]['envelopes']) == 0: del queue[msgid] return 1 # envelope_update must create an envelope within a message and return a # 64-bit unique envelope identifier where upper 32-bit == msgid # def envelope_update(evpid, envelope): queue[(evpid >> 32) & 0xffffffff]['envelopes'][evpid] = envelope return 1 def envelope_load(evpid): msgid = (evpid >> 32) & 0xffffffff if msgid not in queue: return 0 if evpid not in queue[msgid]['envelopes']: return 0 return queue[msgid]['envelopes'][evpid] def envelope_walk(): return -1 OpenSMTPD-extras-6.6.0/extras/queues/queue-ram/000077500000000000000000000000001355240505200212575ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-ram/Makefile.am000066400000000000000000000003001355240505200233040ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/queue.mk pkglibexec_PROGRAMS = queue-ram queue_ram_SOURCES = $(SRCS) queue_ram_SOURCES += $(queues_srcdir)/queue-ram/queue_ram.c OpenSMTPD-extras-6.6.0/extras/queues/queue-ram/queue_ram.c000066400000000000000000000163731355240505200234200ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #define stat_increment(a, b) do {} while(0) #define stat_decrement(a, b) do {} while(0) struct qr_envelope { char *buf; size_t len; }; struct qr_message { char *buf; size_t len; struct tree envelopes; }; static struct tree messages; static struct qr_message * get_message(uint32_t msgid) { struct qr_message *msg; if ((msg = tree_get(&messages, msgid)) == NULL) log_warn("warn: message not found"); return msg; } static int queue_ram_message_create(uint32_t *msgid) { struct qr_message *msg; msg = calloc(1, sizeof(*msg)); if (msg == NULL) { log_warn("warn: calloc"); return 0; } tree_init(&msg->envelopes); do { *msgid = queue_generate_msgid(); } while (tree_check(&messages, *msgid)); tree_xset(&messages, *msgid, msg); return 1; } static int queue_ram_message_commit(uint32_t msgid, const char *path) { struct qr_message *msg; struct stat sb; size_t n; FILE *f; int ret; if ((msg = tree_get(&messages, msgid)) == NULL) { log_warnx("warn: msgid not found"); return 0; } if ((f = fopen(path, "rb")) == NULL) { log_warn("warn: fopen: \"%s\"", path); return 0; } if (fstat(fileno(f), &sb) == -1) { log_warn("warn: fstat"); fclose(f); return 0; } msg->len = sb.st_size; msg->buf = malloc(msg->len); if (msg->buf == NULL) { log_warn("warn: malloc"); fclose(f); return 0; } ret = 0; n = fread(msg->buf, 1, msg->len, f); if (ferror(f)) log_warn("warn: fread"); else if ((off_t)n != sb.st_size) log_warnx("warn: bad read"); else { ret = 1; stat_increment("queue.ram.message.size", msg->len); } fclose(f); return ret; } static int queue_ram_message_delete(uint32_t msgid) { struct qr_message *msg; struct qr_envelope *evp; uint64_t evpid; if ((msg = tree_pop(&messages, msgid)) == NULL) { log_warnx("warn: not found"); return 0; } while (tree_poproot(&messages, &evpid, (void**)&evp)) { stat_decrement("queue.ram.envelope.size", evp->len); free(evp->buf); free(evp); } stat_decrement("queue.ram.message.size", msg->len); free(msg->buf); free(msg); return 0; } static int queue_ram_message_fd_r(uint32_t msgid) { struct qr_message *msg; size_t n; FILE *f; int fd, fd2; if ((msg = tree_get(&messages, msgid)) == NULL) { log_warnx("warn: not found"); return -1; } fd = mktmpfile(); if (fd == -1) { log_warn("warn: mktmpfile"); return -1; } fd2 = dup(fd); if (fd2 == -1) { log_warn("warn: dup"); close(fd); return -1; } f = fdopen(fd2, "w"); if (f == NULL) { log_warn("warn: fdopen"); close(fd); close(fd2); return -1; } n = fwrite(msg->buf, 1, msg->len, f); if (n != msg->len) { log_warn("warn: write"); close(fd); fclose(f); return -1; } fclose(f); lseek(fd, 0, SEEK_SET); return fd; } static int queue_ram_message_corrupt(uint32_t msgid) { return queue_ram_message_delete(msgid); } static int queue_ram_message_uncorrupt(uint32_t msgid) { return 0; } static int queue_ram_envelope_create(uint32_t msgid, const char *buf, size_t len, uint64_t *evpid) { struct qr_envelope *evp; struct qr_message *msg; if ((msg = get_message(msgid)) == NULL) return 0; do { *evpid = queue_generate_evpid(msgid); } while (tree_check(&msg->envelopes, *evpid)); evp = calloc(1, sizeof *evp); if (evp == NULL) { log_warn("warn: calloc"); return 0; } evp->len = len; evp->buf = malloc(len); if (evp->buf == NULL) { log_warn("warn: malloc"); free(evp); return 0; } memmove(evp->buf, buf, len); tree_xset(&msg->envelopes, *evpid, evp); stat_increment("queue.ram.envelope.size", len); return 1; } static int queue_ram_envelope_delete(uint64_t evpid) { struct qr_envelope *evp; struct qr_message *msg; if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) return 0; if ((evp = tree_pop(&msg->envelopes, evpid)) == NULL) { log_warnx("warn: not found"); return 0; } stat_decrement("queue.ram.envelope.size", evp->len); free(evp->buf); free(evp); if (tree_empty(&msg->envelopes)) { tree_xpop(&messages, evpid_to_msgid(evpid)); stat_decrement("queue.ram.message.size", msg->len); free(msg->buf); free(msg); } return 1; } static int queue_ram_envelope_update(uint64_t evpid, const char *buf, size_t len) { struct qr_envelope *evp; struct qr_message *msg; void *tmp; if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) { log_warn("warn: not found"); return 0; } if ((tmp = malloc(len)) == NULL) { log_warn("warn: malloc"); return 0; } memmove(tmp, buf, len); free(evp->buf); evp->len = len; evp->buf = tmp; stat_decrement("queue.ram.envelope.size", evp->len); stat_increment("queue.ram.envelope.size", len); return 1; } static int queue_ram_envelope_load(uint64_t evpid, char *buf, size_t len) { struct qr_envelope *evp; struct qr_message *msg; if ((msg = get_message(evpid_to_msgid(evpid))) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) { log_warn("warn: not found"); return 0; } if (len < evp->len) { log_warnx("warn: buffer too small"); return 0; } memmove(buf, evp->buf, evp->len); return evp->len; } static int queue_ram_envelope_walk(uint64_t *evpid, char *buf, size_t len) { return -1; } static int queue_ram_message_walk(uint64_t *evpid, char *buf, size_t len, uint32_t msgid, int *done, void **data) { return -1; } static int queue_ram_init(int server) { tree_init(&messages); queue_api_on_message_create(queue_ram_message_create); queue_api_on_message_commit(queue_ram_message_commit); queue_api_on_message_delete(queue_ram_message_delete); queue_api_on_message_fd_r(queue_ram_message_fd_r); queue_api_on_message_corrupt(queue_ram_message_corrupt); queue_api_on_message_uncorrupt(queue_ram_message_uncorrupt); queue_api_on_envelope_create(queue_ram_envelope_create); queue_api_on_envelope_delete(queue_ram_envelope_delete); queue_api_on_envelope_update(queue_ram_envelope_update); queue_api_on_envelope_load(queue_ram_envelope_load); queue_api_on_envelope_walk(queue_ram_envelope_walk); queue_api_on_message_walk(queue_ram_message_walk); return 1; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; queue_ram_init(1); queue_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/queues/queue-stub/000077500000000000000000000000001355240505200214555ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/queues/queue-stub/Makefile.am000066400000000000000000000003051355240505200235070ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/queue.mk pkglibexec_PROGRAMS = queue-stub queue_stub_SOURCES = $(SRCS) queue_stub_SOURCES += $(queues_srcdir)/queue-stub/queue_stub.c OpenSMTPD-extras-6.6.0/extras/queues/queue-stub/queue_stub.c000066400000000000000000000056031355240505200240060ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include static int queue_stub_message_create(uint32_t *msgid) { return 0; } static int queue_stub_message_commit(uint32_t msgid, const char * path) { return 0; } static int queue_stub_message_delete(uint32_t msgid) { return 0; } static int queue_stub_message_fd_r(uint32_t msgid) { return -1; } static int queue_stub_message_corrupt(uint32_t msgid) { return 0; } static int queue_stub_message_uncorrupt(uint32_t msgid) { return 0; } static int queue_stub_envelope_create(uint32_t msgid, const char *buf, size_t len, uint64_t *evpid) { return 0; } static int queue_stub_envelope_delete(uint64_t evpid) { return 0; } static int queue_stub_envelope_update(uint64_t evpid, const char *buf, size_t len) { return 0; } static int queue_stub_envelope_load(uint64_t evpid, char *buf, size_t len) { return 0; } static int queue_stub_envelope_walk(uint64_t *evpid, char *buf, size_t len) { return 0; } static int queue_stub_message_walk(uint64_t *evpid, char *buf, size_t len, uint32_t msgid, int *done, void **data) { return 0; } static int queue_stub_init(int server) { queue_api_on_message_create(queue_stub_message_create); queue_api_on_message_commit(queue_stub_message_commit); queue_api_on_message_delete(queue_stub_message_delete); queue_api_on_message_fd_r(queue_stub_message_fd_r); queue_api_on_message_corrupt(queue_stub_message_corrupt); queue_api_on_message_uncorrupt(queue_stub_message_uncorrupt); queue_api_on_envelope_create(queue_stub_envelope_create); queue_api_on_envelope_delete(queue_stub_envelope_delete); queue_api_on_envelope_update(queue_stub_envelope_update); queue_api_on_envelope_load(queue_stub_envelope_load); queue_api_on_envelope_walk(queue_stub_envelope_walk); queue_api_on_message_walk(queue_stub_message_walk); return 1; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; queue_stub_init(1); queue_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/schedulers/000077500000000000000000000000001355240505200202105ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/schedulers/Makefile.am000066400000000000000000000002611355240505200222430ustar00rootroot00000000000000SUBDIRS = if HAVE_SCHEDULER_STUB SUBDIRS+= scheduler-stub endif if HAVE_SCHEDULER_RAM SUBDIRS+= scheduler-ram endif if HAVE_SCHEDULER_PYTHON SUBDIRS+= scheduler-python endif OpenSMTPD-extras-6.6.0/extras/schedulers/README000066400000000000000000000000001355240505200210560ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-python/000077500000000000000000000000001355240505200235055ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-python/Makefile.am000066400000000000000000000004461355240505200255450ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/scheduler.mk pkglibexec_PROGRAMS = scheduler-python scheduler_python_SOURCES = $(SRCS) scheduler_python_SOURCES += $(schedulers_srcdir)/scheduler-python/scheduler_python.c CFLAGS += ${PYTHON_CPPFLAGS} LDFLAGS += ${PYTHON_LDFLAGS} OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-python/scheduler_python.c000066400000000000000000000306151355240505200272350ustar00rootroot00000000000000/* * Copyright (c) 2014 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include /* _GNU_SOURCE is not properly protected in Python.h ... */ #undef _GNU_SOURCE #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include static PyObject *py_on_init; static PyObject *py_on_insert; static PyObject *py_on_commit; static PyObject *py_on_rollback; static PyObject *py_on_update; static PyObject *py_on_delete; static PyObject *py_on_hold; static PyObject *py_on_release; static PyObject *py_on_batch; static PyObject *py_on_messages; static PyObject *py_on_envelopes; static PyObject *py_on_schedule; static PyObject *py_on_remove; static PyObject *py_on_suspend; static PyObject *py_on_resume; static void check_err(const char *name) { if (PyErr_Occurred()) { PyErr_Print(); fatalx("error in %s handler", name); } } static PyObject * dispatch(PyObject *handler, PyObject *args) { PyObject *ret; ret = PyObject_CallObject(handler, args); Py_DECREF(args); if (PyErr_Occurred()) { PyErr_Print(); fatalx("exception"); } return ret; } static int get_int(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsLong(o); if (PyInt_Check(o)) return PyInt_AsLong(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static size_t get_size_t(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsUnsignedLongLong(o); if (PyInt_Check(o)) return PyInt_AsUnsignedLongLongMask(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static size_t get_uint32_t(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsUnsignedLong(o); if (PyInt_Check(o)) return PyInt_AsUnsignedLongMask(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static time_t get_time_t(PyObject *o) { if (PyLong_Check(o)) return PyLong_AsUnsignedLongLong(o); if (PyInt_Check(o)) return PyInt_AsUnsignedLongLongMask(o); PyErr_SetString(PyExc_TypeError, "int type expected"); return 0; } static int scheduler_python_init(void) { PyObject *py_ret; int r; py_ret = dispatch(py_on_init, PyTuple_New(0)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("init"); return r; } static int scheduler_python_insert(struct scheduler_info *info) { PyObject *py_ret; int r; py_ret = dispatch(py_on_insert, Py_BuildValue("KllLLLLL", (unsigned long long)info->evpid, (long)info->type, (long)info->retry, (long long)info->creation, (long long)info->expire, (long long)info->lasttry, (long long)info->lastbounce, (long long)info->nexttry)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("insert"); return r; } static size_t scheduler_python_commit(uint32_t msgid) { PyObject *py_ret; size_t r; py_ret = dispatch(py_on_commit, Py_BuildValue("(k)", (unsigned long)msgid)); r = get_size_t(py_ret); Py_DECREF(py_ret); check_err("commit"); return r; } static size_t scheduler_python_rollback(uint32_t msgid) { PyObject *py_ret; size_t r; py_ret = dispatch(py_on_rollback, Py_BuildValue("(k)", (unsigned long)msgid)); r = get_size_t(py_ret); Py_DECREF(py_ret); check_err("rollback"); return r; } static int scheduler_python_update(struct scheduler_info *info) { PyObject *py_ret; time_t nexttry; py_ret = dispatch(py_on_update, Py_BuildValue("KllLLLLL", (unsigned long long)info->evpid, (long)info->type, (long)info->retry, (long long)info->creation, (long long)info->expire, (long long)info->lasttry, (long long)info->lastbounce, (long long)info->nexttry)); nexttry = get_time_t(py_ret); Py_DECREF(py_ret); check_err("update"); if (nexttry == -1) return -1; if (nexttry == 0) return 0; info->nexttry = nexttry; return 1; } static int scheduler_python_delete(uint64_t evpid) { PyObject *py_ret; int r; py_ret = dispatch(py_on_delete, Py_BuildValue("(K)", (unsigned long long)evpid)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("delete"); return r; } static int scheduler_python_hold(uint64_t evpid, uint64_t holdq) { PyObject *py_ret; int r; py_ret = dispatch(py_on_hold, Py_BuildValue("KK", (unsigned long long)evpid, (unsigned long long)holdq)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("hold"); return r; } static int scheduler_python_release(int type, uint64_t holdq, int count) { PyObject *py_ret; int r; py_ret = dispatch(py_on_release, Py_BuildValue("lKl", (long)type, (unsigned long long)holdq, (long)count)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("release"); return r; } static int scheduler_python_batch(int mask, int *delay, size_t *count, uint64_t *evpids, int *types) { PyObject *py_ret, *o; int type; unsigned long long evpid; size_t n, i; ssize_t r; n = *count; py_ret = dispatch(py_on_batch, Py_BuildValue("lK", (long)mask, (unsigned long long)n)); if (PyInt_Check(py_ret)) { *delay = PyInt_AsLong(py_ret); *count = 0; Py_DECREF(py_ret); return 0; } if (PyLong_Check(py_ret)) { *delay = PyLong_AsLong(py_ret); *count = 0; Py_DECREF(py_ret); return 0; } *delay = 0; r = PySequence_Length(py_ret); check_err("batch1"); if (r <= 0 || (size_t)r > n) fatalx("bad length"); for (i = 0; i < (size_t)r; i++) { o = PySequence_GetItem(py_ret, i); PyArg_ParseTuple(o, "Ki", &evpid, &type); evpids[i] = evpid; types[i] = type; Py_DECREF(o); } Py_DECREF(py_ret); check_err("batch"); return 1; } static size_t scheduler_python_messages(uint32_t msgid, uint32_t *dst, size_t sz) { PyObject *py_ret, *o; ssize_t r; size_t i; py_ret = dispatch(py_on_messages, Py_BuildValue("kK", (unsigned long)msgid, (unsigned long long)sz)); r = PySequence_Length(py_ret); if (r < 0 || (size_t)r > sz) fatalx("bad length"); for (i = 0; i < (size_t)r; i++) { o = PySequence_ITEM(py_ret, i); dst[i] = get_uint32_t(o); Py_DECREF(o); } Py_DECREF(py_ret); check_err("messages"); return r; } static size_t scheduler_python_envelopes(uint64_t evpid, struct evpstate *dst, size_t sz) { PyObject *py_ret, *o; unsigned int flags, retry; unsigned long long tevpid; long long timestamp; ssize_t r; size_t i; py_ret = dispatch(py_on_envelopes, Py_BuildValue("KK", (unsigned long long)evpid, (unsigned long long)sz)); check_err("envelopes"); r = PySequence_Length(py_ret); if (r < 0 || (size_t)r > sz) fatalx("bad length"); check_err("envelopes"); for (i = 0; i < (size_t)r; i++) { o = PySequence_ITEM(py_ret, i); check_err("envelopes"); PyArg_ParseTuple(o, "KIIL", &tevpid, &flags, &retry, ×tamp); check_err("envelopes"); dst[i].evpid = tevpid; dst[i].flags = flags; dst[i].retry = retry; dst[i].time = timestamp; check_err("envelopes"); Py_DECREF(o); } Py_DECREF(py_ret); check_err("envelopes"); return r; } static int scheduler_python_schedule(uint64_t evpid) { PyObject *py_ret; int r; py_ret = dispatch(py_on_schedule, Py_BuildValue("(K)", (unsigned long long)evpid)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("schedule"); return r; } static int scheduler_python_remove(uint64_t evpid) { PyObject *py_ret; int r; py_ret = dispatch(py_on_remove, Py_BuildValue("(K)", (unsigned long long)evpid)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("remove"); return r; } static int scheduler_python_suspend(uint64_t evpid) { PyObject *py_ret; int r; py_ret = dispatch(py_on_suspend, Py_BuildValue("(K)", (unsigned long long)evpid)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("suspend"); return r; } static int scheduler_python_resume(uint64_t evpid) { PyObject *py_ret; int r; py_ret = dispatch(py_on_resume, Py_BuildValue("(K)", (unsigned long long)evpid)); r = get_int(py_ret); Py_DECREF(py_ret); check_err("resume"); return r; } static char * loadfile(const char * path) { FILE *f; off_t oz; size_t sz; char *buf; if ((f = fopen(path, "r")) == NULL) fatal("fopen"); if (fseek(f, 0, SEEK_END) == -1) fatal("fseek"); oz = ftello(f); if (fseek(f, 0, SEEK_SET) == -1) fatal("fseek"); if ((size_t)oz >= SIZE_MAX) fatal("too big"); sz = oz; buf = xmalloc(sz + 1, "loadfile"); if (fread(buf, 1, sz, f) != sz) fatal("fread"); buf[sz] = '\0'; fclose(f); return buf; } static PyMethodDef py_methods[] = { { NULL, NULL, 0, NULL } }; int main(int argc, char **argv) { int ch; char *path, *buf; PyObject *self, *code, *module; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc == 0) fatalx("missing path"); path = argv[0]; Py_Initialize(); self = Py_InitModule("scheduler", py_methods); PyModule_AddIntConstant(self, "SCHED_REMOVE", SCHED_REMOVE); PyModule_AddIntConstant(self, "SCHED_EXPIRE", SCHED_EXPIRE); PyModule_AddIntConstant(self, "SCHED_UPDATE", SCHED_UPDATE); PyModule_AddIntConstant(self, "SCHED_BOUNCE", SCHED_BOUNCE); PyModule_AddIntConstant(self, "SCHED_MDA", SCHED_MDA); PyModule_AddIntConstant(self, "SCHED_MTA", SCHED_MTA); PyModule_AddIntConstant(self, "D_BOUNCE", D_BOUNCE); PyModule_AddIntConstant(self, "D_MDA", D_MDA); PyModule_AddIntConstant(self, "D_MTA", D_MTA); PyModule_AddIntConstant(self, "EF_PENDING", EF_PENDING); PyModule_AddIntConstant(self, "EF_INFLIGHT", EF_INFLIGHT); PyModule_AddIntConstant(self, "EF_SUSPEND", EF_SUSPEND); PyModule_AddIntConstant(self, "EF_HOLD", EF_HOLD); buf = loadfile(path); code = Py_CompileString(buf, path, Py_file_input); free(buf); if (code == NULL) { PyErr_Print(); fatalx("failed to compile %s", path); } module = PyImport_ExecCodeModuleEx("myscheduler", code, path); if (module == NULL) { PyErr_Print(); fatalx("failed to install module %s", path); } log_debug("debug: starting..."); py_on_init = PyObject_GetAttrString(module, "scheduler_init"); py_on_insert = PyObject_GetAttrString(module, "scheduler_insert"); py_on_commit = PyObject_GetAttrString(module, "scheduler_commit"); py_on_rollback = PyObject_GetAttrString(module, "scheduler_rollback"); py_on_update = PyObject_GetAttrString(module, "scheduler_update"); py_on_delete = PyObject_GetAttrString(module, "scheduler_delete"); py_on_hold = PyObject_GetAttrString(module, "scheduler_hold"); py_on_release = PyObject_GetAttrString(module, "scheduler_release"); py_on_batch = PyObject_GetAttrString(module, "scheduler_batch"); py_on_messages = PyObject_GetAttrString(module, "scheduler_messages"); py_on_envelopes = PyObject_GetAttrString(module, "scheduler_envelopes"); py_on_schedule = PyObject_GetAttrString(module, "scheduler_schedule"); py_on_remove = PyObject_GetAttrString(module, "scheduler_remove"); py_on_suspend = PyObject_GetAttrString(module, "scheduler_suspend"); py_on_resume = PyObject_GetAttrString(module, "scheduler_resume"); scheduler_api_on_init(scheduler_python_init); scheduler_api_on_insert(scheduler_python_insert); scheduler_api_on_commit(scheduler_python_commit); scheduler_api_on_rollback(scheduler_python_rollback); scheduler_api_on_update(scheduler_python_update); scheduler_api_on_delete(scheduler_python_delete); scheduler_api_on_hold(scheduler_python_hold); scheduler_api_on_release(scheduler_python_release); scheduler_api_on_batch(scheduler_python_batch); scheduler_api_on_messages(scheduler_python_messages); scheduler_api_on_envelopes(scheduler_python_envelopes); scheduler_api_on_schedule(scheduler_python_schedule); scheduler_api_on_remove(scheduler_python_remove); scheduler_api_on_suspend(scheduler_python_suspend); scheduler_api_on_resume(scheduler_python_resume); scheduler_api_no_chroot(); scheduler_api_dispatch(); log_debug("debug: exiting"); Py_Finalize(); return 1; } OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-ram/000077500000000000000000000000001355240505200227435ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-ram/Makefile.am000066400000000000000000000003341355240505200247770ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/scheduler.mk pkglibexec_PROGRAMS = scheduler-ram scheduler_ram_SOURCES = $(SRCS) scheduler_ram_SOURCES += $(schedulers_srcdir)/scheduler-ram/scheduler_ram.c OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-ram/scheduler_ram.c000066400000000000000000000700361355240505200257320ustar00rootroot00000000000000/* * Copyright (c) 2012 Gilles Chehade * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define TRACE_SCHEDULER 0x0080 #define stat_increment(a, b) do {} while(0) #define stat_decrement(a, b) do {} while(0) #define xcalloc(a, b, c) calloc(a, b) TAILQ_HEAD(evplist, rq_envelope); struct rq_message { uint32_t msgid; struct tree envelopes; }; struct rq_envelope { TAILQ_ENTRY(rq_envelope) entry; SPLAY_ENTRY(rq_envelope) t_entry; uint64_t evpid; uint64_t holdq; enum delivery_type type; #define RQ_EVPSTATE_PENDING 0 #define RQ_EVPSTATE_SCHEDULED 1 #define RQ_EVPSTATE_INFLIGHT 2 #define RQ_EVPSTATE_HELD 3 uint8_t state; #define RQ_ENVELOPE_EXPIRED 0x01 #define RQ_ENVELOPE_REMOVED 0x02 #define RQ_ENVELOPE_SUSPEND 0x04 #define RQ_ENVELOPE_UPDATE 0x08 #define RQ_ENVELOPE_OVERFLOW 0x10 uint8_t flags; time_t ctime; time_t sched; time_t expire; struct rq_message *message; time_t t_inflight; time_t t_scheduled; }; struct rq_holdq { struct evplist q; size_t count; }; struct rq_queue { size_t evpcount; struct tree messages; SPLAY_HEAD(prioqtree, rq_envelope) q_priotree; struct evplist q_pending; struct evplist q_inflight; struct evplist q_mta; struct evplist q_mda; struct evplist q_bounce; struct evplist q_update; struct evplist q_expired; struct evplist q_removed; }; static int rq_envelope_cmp(struct rq_envelope *, struct rq_envelope *); SPLAY_PROTOTYPE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp); static void sorted_insert(struct rq_queue *, struct rq_envelope *); static void rq_queue_init(struct rq_queue *); static void rq_queue_merge(struct rq_queue *, struct rq_queue *); static void rq_queue_dump(struct rq_queue *, const char *); static void rq_queue_schedule(struct rq_queue *rq); static struct evplist *rq_envelope_list(struct rq_queue *, struct rq_envelope *); static void rq_envelope_schedule(struct rq_queue *, struct rq_envelope *); static int rq_envelope_remove(struct rq_queue *, struct rq_envelope *); static int rq_envelope_suspend(struct rq_queue *, struct rq_envelope *); static int rq_envelope_resume(struct rq_queue *, struct rq_envelope *); static void rq_envelope_delete(struct rq_queue *, struct rq_envelope *); static const char *rq_envelope_to_text(struct rq_envelope *); static struct rq_queue ramqueue; static struct tree updates; static struct tree holdqs[3]; /* delivery type */ static time_t currtime; int verbose; /* XXX from to.c */ static const char * duration_to_text(time_t t) { static char dst[64]; char buf[64]; int h, m, s; long long d; if (t == 0) { (void)strlcpy(dst, "0s", sizeof dst); return dst; } dst[0] = '\0'; if (t < 0) { (void)strlcpy(dst, "-", sizeof dst); t = -t; } s = t % 60; t /= 60; m = t % 60; t /= 60; h = t % 24; d = t / 24; if (d) { (void)snprintf(buf, sizeof buf, "%lldd", d); (void)strlcat(dst, buf, sizeof dst); } if (h) { (void)snprintf(buf, sizeof buf, "%dh", h); (void)strlcat(dst, buf, sizeof dst); } if (m) { (void)snprintf(buf, sizeof buf, "%dm", m); (void)strlcat(dst, buf, sizeof dst); } if (s) { (void)snprintf(buf, sizeof buf, "%ds", s); (void)strlcat(dst, buf, sizeof dst); } return dst; } #define BACKOFF_TRANSFER 400 #define BACKOFF_DELIVERY 10 #define BACKOFF_OVERFLOW 3 static time_t scheduler_backoff(time_t t0, time_t base, uint32_t step) { return (t0 + base * step * step); } static time_t scheduler_next(time_t t0, time_t base, uint32_t step) { time_t t; /* XXX be more efficient */ while ((t = scheduler_backoff(t0, base, step)) <= currtime) step++; return t; } static int scheduler_ram_init(void) { rq_queue_init(&ramqueue); tree_init(&updates); tree_init(&holdqs[D_MDA]); tree_init(&holdqs[D_MTA]); tree_init(&holdqs[D_BOUNCE]); return 1; } static int scheduler_ram_insert(struct scheduler_info *si) { struct rq_queue *update; struct rq_message *message; struct rq_envelope *envelope; uint32_t msgid; currtime = time(NULL); msgid = evpid_to_msgid(si->evpid); /* find/prepare a ramqueue update */ if ((update = tree_get(&updates, msgid)) == NULL) { update = xcalloc(1, sizeof *update, "scheduler_insert"); stat_increment("scheduler.ramqueue.update", 1); rq_queue_init(update); tree_xset(&updates, msgid, update); } /* find/prepare the msgtree message in ramqueue update */ if ((message = tree_get(&update->messages, msgid)) == NULL) { message = xcalloc(1, sizeof *message, "scheduler_insert"); message->msgid = msgid; tree_init(&message->envelopes); tree_xset(&update->messages, msgid, message); stat_increment("scheduler.ramqueue.message", 1); } /* create envelope in ramqueue message */ envelope = xcalloc(1, sizeof *envelope, "scheduler_insert"); envelope->evpid = si->evpid; envelope->type = si->type; envelope->message = message; envelope->ctime = si->creation; envelope->expire = si->creation + si->expire; envelope->sched = scheduler_backoff(si->creation, (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry); tree_xset(&message->envelopes, envelope->evpid, envelope); update->evpcount++; stat_increment("scheduler.ramqueue.envelope", 1); envelope->state = RQ_EVPSTATE_PENDING; TAILQ_INSERT_TAIL(&update->q_pending, envelope, entry); si->nexttry = envelope->sched; return 1; } static size_t scheduler_ram_commit(uint32_t msgid) { struct rq_queue *update; size_t r; currtime = time(NULL); update = tree_xpop(&updates, msgid); r = update->evpcount; if (verbose & TRACE_SCHEDULER) rq_queue_dump(update, "update to commit"); rq_queue_merge(&ramqueue, update); if (verbose & TRACE_SCHEDULER) rq_queue_dump(&ramqueue, "resulting queue"); rq_queue_schedule(&ramqueue); free(update); stat_decrement("scheduler.ramqueue.update", 1); return r; } static size_t scheduler_ram_rollback(uint32_t msgid) { struct rq_queue *update; struct rq_envelope *evp; size_t r; currtime = time(NULL); if ((update = tree_pop(&updates, msgid)) == NULL) return 0; r = update->evpcount; while ((evp = TAILQ_FIRST(&update->q_pending))) { TAILQ_REMOVE(&update->q_pending, evp, entry); rq_envelope_delete(update, evp); } free(update); stat_decrement("scheduler.ramqueue.update", 1); return r; } static int scheduler_ram_update(struct scheduler_info *si) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; currtime = time(NULL); msgid = evpid_to_msgid(si->evpid); msg = tree_xget(&ramqueue.messages, msgid); evp = tree_xget(&msg->envelopes, si->evpid); /* it *must* be in-flight */ if (evp->state != RQ_EVPSTATE_INFLIGHT) fatalx("evp:%016" PRIx64 " not in-flight", si->evpid); TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); /* * If the envelope was removed while inflight, schedule it for * removal immediatly. */ if (evp->flags & RQ_ENVELOPE_REMOVED) { TAILQ_INSERT_TAIL(&ramqueue.q_removed, evp, entry); evp->state = RQ_EVPSTATE_SCHEDULED; evp->t_scheduled = currtime; return 1; } evp->sched = scheduler_next(evp->ctime, (si->type == D_MTA) ? BACKOFF_TRANSFER : BACKOFF_DELIVERY, si->retry); evp->state = RQ_EVPSTATE_PENDING; if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) sorted_insert(&ramqueue, evp); si->nexttry = evp->sched; return 1; } static int scheduler_ram_delete(uint64_t evpid) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; currtime = time(NULL); msgid = evpid_to_msgid(evpid); msg = tree_xget(&ramqueue.messages, msgid); evp = tree_xget(&msg->envelopes, evpid); /* it *must* be in-flight */ if (evp->state != RQ_EVPSTATE_INFLIGHT) fatalx("evp:%016" PRIx64 " not in-flight", evpid); TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); rq_envelope_delete(&ramqueue, evp); return 1; } #define HOLDQ_MAXSIZE 1000 static int scheduler_ram_hold(uint64_t evpid, uint64_t holdq) { struct rq_holdq *hq; struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; currtime = time(NULL); msgid = evpid_to_msgid(evpid); msg = tree_xget(&ramqueue.messages, msgid); evp = tree_xget(&msg->envelopes, evpid); /* it *must* be in-flight */ if (evp->state != RQ_EVPSTATE_INFLIGHT) fatalx("evp:%016" PRIx64 " not in-flight", evpid); TAILQ_REMOVE(&ramqueue.q_inflight, evp, entry); /* If the envelope is suspended, just mark it as pending */ if (evp->flags & RQ_ENVELOPE_SUSPEND) { evp->state = RQ_EVPSTATE_PENDING; return 0; } hq = tree_get(&holdqs[evp->type], holdq); if (hq == NULL) { hq = xcalloc(1, sizeof(*hq), "scheduler_hold"); TAILQ_INIT(&hq->q); tree_xset(&holdqs[evp->type], holdq, hq); stat_increment("scheduler.ramqueue.holdq", 1); } /* If the holdq is full, just "tempfail" the envelope */ if (hq->count >= HOLDQ_MAXSIZE) { evp->state = RQ_EVPSTATE_PENDING; evp->flags |= RQ_ENVELOPE_UPDATE; evp->flags |= RQ_ENVELOPE_OVERFLOW; sorted_insert(&ramqueue, evp); stat_increment("scheduler.ramqueue.hold-overflow", 1); return 0; } evp->state = RQ_EVPSTATE_HELD; evp->holdq = holdq; /* This is an optimization: upon release, the envelopes will be * inserted in the pending queue from the first element to the last. * Since elements already in the queue were received first, they * were scheduled first, so they will be reinserted before the * current element. */ TAILQ_INSERT_HEAD(&hq->q, evp, entry); hq->count += 1; stat_increment("scheduler.ramqueue.hold", 1); return 1; } static int scheduler_ram_release(int type, uint64_t holdq, int n) { struct rq_holdq *hq; struct rq_envelope *evp; int i, update; currtime = time(NULL); hq = tree_get(&holdqs[type], holdq); if (hq == NULL) return 0; if (n == -1) { n = 0; update = 1; } else update = 0; for (i = 0; n == 0 || i < n; i++) { evp = TAILQ_FIRST(&hq->q); if (evp == NULL) break; TAILQ_REMOVE(&hq->q, evp, entry); hq->count -= 1; evp->holdq = 0; /* When released, all envelopes are put in the pending queue * and will be rescheduled immediatly. As an optimization, * we could just schedule them directly. */ evp->state = RQ_EVPSTATE_PENDING; if (update) evp->flags |= RQ_ENVELOPE_UPDATE; sorted_insert(&ramqueue, evp); } if (TAILQ_EMPTY(&hq->q)) { tree_xpop(&holdqs[type], holdq); free(hq); stat_decrement("scheduler.ramqueue.holdq", 1); } stat_decrement("scheduler.ramqueue.hold", i); return i; } static int scheduler_ram_batch(int mask, int *delay, size_t *count, uint64_t *evpids, int *types) { struct rq_envelope *evp; size_t i, n; time_t t; currtime = time(NULL); rq_queue_schedule(&ramqueue); if (verbose & TRACE_SCHEDULER) rq_queue_dump(&ramqueue, "scheduler_ram_batch()"); i = 0; n = 0; for (;;) { if (mask & SCHED_REMOVE && (evp = TAILQ_FIRST(&ramqueue.q_removed))) { TAILQ_REMOVE(&ramqueue.q_removed, evp, entry); types[i] = SCHED_REMOVE; evpids[i] = evp->evpid; rq_envelope_delete(&ramqueue, evp); if (++i == *count) break; } if (mask & SCHED_EXPIRE && (evp = TAILQ_FIRST(&ramqueue.q_expired))) { TAILQ_REMOVE(&ramqueue.q_expired, evp, entry); types[i] = SCHED_EXPIRE; evpids[i] = evp->evpid; rq_envelope_delete(&ramqueue, evp); if (++i == *count) break; } if (mask & SCHED_UPDATE && (evp = TAILQ_FIRST(&ramqueue.q_update))) { TAILQ_REMOVE(&ramqueue.q_update, evp, entry); types[i] = SCHED_UPDATE; evpids[i] = evp->evpid; if (evp->flags & RQ_ENVELOPE_OVERFLOW) t = BACKOFF_OVERFLOW; else if (evp->type == D_MTA) t = BACKOFF_TRANSFER; else t = BACKOFF_DELIVERY; evp->sched = scheduler_next(evp->ctime, t, 0); evp->flags &= ~(RQ_ENVELOPE_UPDATE|RQ_ENVELOPE_OVERFLOW); evp->state = RQ_EVPSTATE_PENDING; if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) sorted_insert(&ramqueue, evp); if (++i == *count) break; } if (mask & SCHED_BOUNCE && (evp = TAILQ_FIRST(&ramqueue.q_bounce))) { TAILQ_REMOVE(&ramqueue.q_bounce, evp, entry); types[i] = SCHED_BOUNCE; evpids[i] = evp->evpid; TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); evp->state = RQ_EVPSTATE_INFLIGHT; evp->t_inflight = currtime; if (++i == *count) break; } if (mask & SCHED_MDA && (evp = TAILQ_FIRST(&ramqueue.q_mda))) { TAILQ_REMOVE(&ramqueue.q_mda, evp, entry); types[i] = SCHED_MDA; evpids[i] = evp->evpid; TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); evp->state = RQ_EVPSTATE_INFLIGHT; evp->t_inflight = currtime; if (++i == *count) break; } if (mask & SCHED_MTA && (evp = TAILQ_FIRST(&ramqueue.q_mta))) { TAILQ_REMOVE(&ramqueue.q_mta, evp, entry); types[i] = SCHED_MTA; evpids[i] = evp->evpid; TAILQ_INSERT_TAIL(&ramqueue.q_inflight, evp, entry); evp->state = RQ_EVPSTATE_INFLIGHT; evp->t_inflight = currtime; if (++i == *count) break; } /* nothing seen this round */ if (i == n) break; n = i; } if (i) { *count = i; return 1; } if ((evp = TAILQ_FIRST(&ramqueue.q_pending))) { if (evp->sched < evp->expire) t = evp->sched; else t = evp->expire; *delay = (t < currtime) ? 0 : (t - currtime); } else *delay = -1; return 0; } static size_t scheduler_ram_messages(uint32_t from, uint32_t *dst, size_t size) { uint64_t id; size_t n; void *i; for (n = 0, i = NULL; n < size; n++) { if (tree_iterfrom(&ramqueue.messages, &i, from, &id, NULL) == 0) break; dst[n] = id; } return n; } static size_t scheduler_ram_envelopes(uint64_t from, struct evpstate *dst, size_t size) { struct rq_message *msg; struct rq_envelope *evp; void *i; size_t n; if ((msg = tree_get(&ramqueue.messages, evpid_to_msgid(from))) == NULL) return 0; for (n = 0, i = NULL; n < size; ) { if (tree_iterfrom(&msg->envelopes, &i, from, NULL, (void**)&evp) == 0) break; if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED)) continue; dst[n].evpid = evp->evpid; dst[n].flags = 0; dst[n].retry = 0; dst[n].time = 0; if (evp->state == RQ_EVPSTATE_PENDING) { dst[n].time = evp->sched; dst[n].flags = EF_PENDING; } else if (evp->state == RQ_EVPSTATE_SCHEDULED) { dst[n].time = evp->t_scheduled; dst[n].flags = EF_PENDING; } else if (evp->state == RQ_EVPSTATE_INFLIGHT) { dst[n].time = evp->t_inflight; dst[n].flags = EF_INFLIGHT; } else if (evp->state == RQ_EVPSTATE_HELD) { /* same as scheduled */ dst[n].time = evp->t_scheduled; dst[n].flags = EF_PENDING; dst[n].flags |= EF_HOLD; } if (evp->flags & RQ_ENVELOPE_SUSPEND) dst[n].flags |= EF_SUSPEND; n++; } return n; } static int scheduler_ram_schedule(uint64_t evpid) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; void *i; int r; currtime = time(NULL); if (evpid > 0xffffffff) { msgid = evpid_to_msgid(evpid); if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) return 0; if (evp->state == RQ_EVPSTATE_INFLIGHT) return 0; rq_envelope_schedule(&ramqueue, evp); return 1; } else { msgid = evpid; if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; i = NULL; r = 0; while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) { if (evp->state == RQ_EVPSTATE_INFLIGHT) continue; rq_envelope_schedule(&ramqueue, evp); r++; } return r; } } static int scheduler_ram_remove(uint64_t evpid) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; void *i; int r; currtime = time(NULL); if (evpid > 0xffffffff) { msgid = evpid_to_msgid(evpid); if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) return 0; if (rq_envelope_remove(&ramqueue, evp)) return 1; return 0; } else { msgid = evpid; if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; i = NULL; r = 0; while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) if (rq_envelope_remove(&ramqueue, evp)) r++; return r; } } static int scheduler_ram_suspend(uint64_t evpid) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; void *i; int r; currtime = time(NULL); if (evpid > 0xffffffff) { msgid = evpid_to_msgid(evpid); if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) return 0; if (rq_envelope_suspend(&ramqueue, evp)) return 1; return 0; } else { msgid = evpid; if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; i = NULL; r = 0; while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) if (rq_envelope_suspend(&ramqueue, evp)) r++; return r; } } static int scheduler_ram_resume(uint64_t evpid) { struct rq_message *msg; struct rq_envelope *evp; uint32_t msgid; void *i; int r; currtime = time(NULL); if (evpid > 0xffffffff) { msgid = evpid_to_msgid(evpid); if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) return 0; if (rq_envelope_resume(&ramqueue, evp)) return 1; return 0; } else { msgid = evpid; if ((msg = tree_get(&ramqueue.messages, msgid)) == NULL) return 0; i = NULL; r = 0; while (tree_iter(&msg->envelopes, &i, NULL, (void*)(&evp))) if (rq_envelope_resume(&ramqueue, evp)) r++; return r; } } static void sorted_insert(struct rq_queue *rq, struct rq_envelope *evp) { struct rq_envelope *evp2; SPLAY_INSERT(prioqtree, &rq->q_priotree, evp); evp2 = SPLAY_NEXT(prioqtree, &rq->q_priotree, evp); if (evp2) TAILQ_INSERT_BEFORE(evp2, evp, entry); else TAILQ_INSERT_TAIL(&rq->q_pending, evp, entry); } static void rq_queue_init(struct rq_queue *rq) { memset(rq, 0, sizeof *rq); tree_init(&rq->messages); TAILQ_INIT(&rq->q_pending); TAILQ_INIT(&rq->q_inflight); TAILQ_INIT(&rq->q_mta); TAILQ_INIT(&rq->q_mda); TAILQ_INIT(&rq->q_bounce); TAILQ_INIT(&rq->q_update); TAILQ_INIT(&rq->q_expired); TAILQ_INIT(&rq->q_removed); SPLAY_INIT(&rq->q_priotree); } static void rq_queue_merge(struct rq_queue *rq, struct rq_queue *update) { struct rq_message *message, *tomessage; struct rq_envelope *envelope; uint64_t id; void *i; while (tree_poproot(&update->messages, &id, (void*)&message)) { if ((tomessage = tree_get(&rq->messages, id)) == NULL) { /* message does not exist. re-use structure */ tree_xset(&rq->messages, id, message); continue; } /* need to re-link all envelopes before merging them */ i = NULL; while ((tree_iter(&message->envelopes, &i, &id, (void*)&envelope))) envelope->message = tomessage; tree_merge(&tomessage->envelopes, &message->envelopes); free(message); stat_decrement("scheduler.ramqueue.message", 1); } /* sorted insert in the pending queue */ while ((envelope = TAILQ_FIRST(&update->q_pending))) { TAILQ_REMOVE(&update->q_pending, envelope, entry); sorted_insert(rq, envelope); } rq->evpcount += update->evpcount; } #define SCHEDULEMAX 1024 static void rq_queue_schedule(struct rq_queue *rq) { struct rq_envelope *evp; size_t n; n = 0; while ((evp = TAILQ_FIRST(&rq->q_pending))) { if (evp->sched > currtime && evp->expire > currtime) break; if (n == SCHEDULEMAX) break; if (evp->state != RQ_EVPSTATE_PENDING) fatalx("evp:%016" PRIx64 " flags=0x%x", evp->evpid, evp->flags); if (evp->expire <= currtime) { TAILQ_REMOVE(&rq->q_pending, evp, entry); SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); TAILQ_INSERT_TAIL(&rq->q_expired, evp, entry); evp->state = RQ_EVPSTATE_SCHEDULED; evp->flags |= RQ_ENVELOPE_EXPIRED; evp->t_scheduled = currtime; continue; } rq_envelope_schedule(rq, evp); n += 1; } } static struct evplist * rq_envelope_list(struct rq_queue *rq, struct rq_envelope *evp) { switch (evp->state) { case RQ_EVPSTATE_PENDING: return &rq->q_pending; case RQ_EVPSTATE_SCHEDULED: if (evp->flags & RQ_ENVELOPE_EXPIRED) return &rq->q_expired; if (evp->flags & RQ_ENVELOPE_REMOVED) return &rq->q_removed; if (evp->flags & RQ_ENVELOPE_UPDATE) return &rq->q_update; if (evp->type == D_MTA) return &rq->q_mta; if (evp->type == D_MDA) return &rq->q_mda; if (evp->type == D_BOUNCE) return &rq->q_bounce; fatalx("%016" PRIx64 " bad evp type %d", evp->evpid, evp->type); case RQ_EVPSTATE_INFLIGHT: return &rq->q_inflight; case RQ_EVPSTATE_HELD: return NULL; } fatalx("%016" PRIx64 " bad state %d", evp->evpid, evp->state); return NULL; } static void rq_envelope_schedule(struct rq_queue *rq, struct rq_envelope *evp) { struct rq_holdq *hq; struct evplist *q = NULL; switch (evp->type) { case D_MTA: q = &rq->q_mta; break; case D_MDA: q = &rq->q_mda; break; case D_BOUNCE: q = &rq->q_bounce; break; } if (evp->flags & RQ_ENVELOPE_UPDATE) q = &rq->q_update; if (evp->state == RQ_EVPSTATE_HELD) { hq = tree_xget(&holdqs[evp->type], evp->holdq); TAILQ_REMOVE(&hq->q, evp, entry); hq->count -= 1; if (TAILQ_EMPTY(&hq->q)) { tree_xpop(&holdqs[evp->type], evp->holdq); free(hq); } evp->holdq = 0; stat_decrement("scheduler.ramqueue.hold", 1); } else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) { TAILQ_REMOVE(&rq->q_pending, evp, entry); SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); } TAILQ_INSERT_TAIL(q, evp, entry); evp->state = RQ_EVPSTATE_SCHEDULED; evp->t_scheduled = currtime; } static int rq_envelope_remove(struct rq_queue *rq, struct rq_envelope *evp) { struct rq_holdq *hq; struct evplist *evl; if (evp->flags & (RQ_ENVELOPE_REMOVED | RQ_ENVELOPE_EXPIRED)) return 0; /* * If envelope is inflight, mark it envelope for removal. */ if (evp->state == RQ_EVPSTATE_INFLIGHT) { evp->flags |= RQ_ENVELOPE_REMOVED; return 1; } if (evp->state == RQ_EVPSTATE_HELD) { hq = tree_xget(&holdqs[evp->type], evp->holdq); TAILQ_REMOVE(&hq->q, evp, entry); hq->count -= 1; if (TAILQ_EMPTY(&hq->q)) { tree_xpop(&holdqs[evp->type], evp->holdq); free(hq); } evp->holdq = 0; stat_decrement("scheduler.ramqueue.hold", 1); } else if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) { evl = rq_envelope_list(rq, evp); TAILQ_REMOVE(evl, evp, entry); if (evl == &rq->q_pending) SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); } TAILQ_INSERT_TAIL(&rq->q_removed, evp, entry); evp->state = RQ_EVPSTATE_SCHEDULED; evp->flags |= RQ_ENVELOPE_REMOVED; evp->t_scheduled = currtime; return 1; } static int rq_envelope_suspend(struct rq_queue *rq, struct rq_envelope *evp) { struct rq_holdq *hq; struct evplist *evl; if (evp->flags & RQ_ENVELOPE_SUSPEND) return 0; if (evp->state == RQ_EVPSTATE_HELD) { hq = tree_xget(&holdqs[evp->type], evp->holdq); TAILQ_REMOVE(&hq->q, evp, entry); hq->count -= 1; if (TAILQ_EMPTY(&hq->q)) { tree_xpop(&holdqs[evp->type], evp->holdq); free(hq); } evp->holdq = 0; evp->state = RQ_EVPSTATE_PENDING; stat_decrement("scheduler.ramqueue.hold", 1); } else if (evp->state != RQ_EVPSTATE_INFLIGHT) { evl = rq_envelope_list(rq, evp); TAILQ_REMOVE(evl, evp, entry); if (evl == &rq->q_pending) SPLAY_REMOVE(prioqtree, &rq->q_priotree, evp); } evp->flags |= RQ_ENVELOPE_SUSPEND; return 1; } static int rq_envelope_resume(struct rq_queue *rq, struct rq_envelope *evp) { struct evplist *evl; if (!(evp->flags & RQ_ENVELOPE_SUSPEND)) return 0; if (evp->state != RQ_EVPSTATE_INFLIGHT) { evl = rq_envelope_list(rq, evp); if (evl == &rq->q_pending) sorted_insert(rq, evp); else TAILQ_INSERT_TAIL(evl, evp, entry); } evp->flags &= ~RQ_ENVELOPE_SUSPEND; return 1; } static void rq_envelope_delete(struct rq_queue *rq, struct rq_envelope *evp) { tree_xpop(&evp->message->envelopes, evp->evpid); if (tree_empty(&evp->message->envelopes)) { tree_xpop(&rq->messages, evp->message->msgid); free(evp->message); stat_decrement("scheduler.ramqueue.message", 1); } free(evp); rq->evpcount--; stat_decrement("scheduler.ramqueue.envelope", 1); } static const char * rq_envelope_to_text(struct rq_envelope *e) { static char buf[256]; char t[64]; (void)snprintf(buf, sizeof buf, "evp:%016" PRIx64 " [", e->evpid); if (e->type == D_BOUNCE) (void)strlcat(buf, "bounce", sizeof buf); else if (e->type == D_MDA) (void)strlcat(buf, "mda", sizeof buf); else if (e->type == D_MTA) (void)strlcat(buf, "mta", sizeof buf); (void)snprintf(t, sizeof t, ",expire=%s", duration_to_text(e->expire - currtime)); (void)strlcat(buf, t, sizeof buf); switch (e->state) { case RQ_EVPSTATE_PENDING: (void)snprintf(t, sizeof t, ",pending=%s", duration_to_text(e->sched - currtime)); (void)strlcat(buf, t, sizeof buf); break; case RQ_EVPSTATE_SCHEDULED: (void)snprintf(t, sizeof t, ",scheduled=%s", duration_to_text(currtime - e->t_scheduled)); (void)strlcat(buf, t, sizeof buf); break; case RQ_EVPSTATE_INFLIGHT: (void)snprintf(t, sizeof t, ",inflight=%s", duration_to_text(currtime - e->t_inflight)); (void)strlcat(buf, t, sizeof buf); break; case RQ_EVPSTATE_HELD: (void)snprintf(t, sizeof t, ",held=%s", duration_to_text(currtime - e->t_inflight)); (void)strlcat(buf, t, sizeof buf); break; default: fatalx("%016" PRIx64 " bad state %d", e->evpid, e->state); } if (e->flags & RQ_ENVELOPE_REMOVED) (void)strlcat(buf, ",removed", sizeof buf); if (e->flags & RQ_ENVELOPE_EXPIRED) (void)strlcat(buf, ",expired", sizeof buf); if (e->flags & RQ_ENVELOPE_SUSPEND) (void)strlcat(buf, ",suspended", sizeof buf); (void)strlcat(buf, "]", sizeof buf); return buf; } static void rq_queue_dump(struct rq_queue *rq, const char * name) { struct rq_message *message; struct rq_envelope *envelope; void *i, *j; uint64_t id; log_debug("debug: /--- ramqueue: %s", name); i = NULL; while ((tree_iter(&rq->messages, &i, &id, (void*)&message))) { log_debug("debug: | msg:%08" PRIx32, message->msgid); j = NULL; while ((tree_iter(&message->envelopes, &j, &id, (void*)&envelope))) log_debug("debug: | %s", rq_envelope_to_text(envelope)); } log_debug("debug: \\---"); } static int rq_envelope_cmp(struct rq_envelope *e1, struct rq_envelope *e2) { time_t ref1, ref2; ref1 = (e1->sched < e1->expire) ? e1->sched : e1->expire; ref2 = (e2->sched < e2->expire) ? e2->sched : e2->expire; if (ref1 != ref2) return (ref1 < ref2) ? -1 : 1; if (e1->evpid != e2->evpid) return (e1->evpid < e2->evpid) ? -1 : 1; return 0; } SPLAY_GENERATE(prioqtree, rq_envelope, t_entry, rq_envelope_cmp); int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; scheduler_api_on_init(scheduler_ram_init); scheduler_api_on_insert(scheduler_ram_insert); scheduler_api_on_commit(scheduler_ram_commit); scheduler_api_on_rollback(scheduler_ram_rollback); scheduler_api_on_update(scheduler_ram_update); scheduler_api_on_delete(scheduler_ram_delete); scheduler_api_on_hold(scheduler_ram_hold); scheduler_api_on_release(scheduler_ram_release); scheduler_api_on_batch(scheduler_ram_batch); scheduler_api_on_messages(scheduler_ram_messages); scheduler_api_on_envelopes(scheduler_ram_envelopes); scheduler_api_on_schedule(scheduler_ram_schedule); scheduler_api_on_remove(scheduler_ram_remove); scheduler_api_on_suspend(scheduler_ram_suspend); scheduler_api_on_resume(scheduler_ram_resume); scheduler_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-stub/000077500000000000000000000000001355240505200231415ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-stub/Makefile.am000066400000000000000000000003411355240505200251730ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/scheduler.mk pkglibexec_PROGRAMS = scheduler-stub scheduler_stub_SOURCES = $(SRCS) scheduler_stub_SOURCES += $(schedulers_srcdir)/scheduler-stub/scheduler_stub.c OpenSMTPD-extras-6.6.0/extras/schedulers/scheduler-stub/scheduler_stub.c000066400000000000000000000060111355240505200263160ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include static int scheduler_stub_init(void) { return 1; } static int scheduler_stub_insert(struct scheduler_info *info) { return 0; } static size_t scheduler_stub_commit(uint32_t msgid) { return 0; } static size_t scheduler_stub_rollback(uint32_t msgid) { return 0; } static int scheduler_stub_update(struct scheduler_info *info) { return 0; } static int scheduler_stub_delete(uint64_t evpid) { return 0; } static int scheduler_stub_hold(uint64_t evpid, uint64_t holdq) { return 0; } static int scheduler_stub_release(int type, uint64_t holdq, int count) { return 0; } static int scheduler_stub_batch(int mask, int *delay, size_t *count, uint64_t *evpids, int *types) { *delay = -1; return 0; } static size_t scheduler_stub_messages(uint32_t msgid, uint32_t *dst, size_t sz) { return 0; } static size_t scheduler_stub_envelopes(uint64_t evpid, struct evpstate *dst, size_t sz) { return 0; } static int scheduler_stub_schedule(uint64_t evpid) { return 0; } static int scheduler_stub_remove(uint64_t evpid) { return 0; } static int scheduler_stub_suspend(uint64_t evpid) { return 0; } static int scheduler_stub_resume(uint64_t evpid) { return 0; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; scheduler_api_on_init(scheduler_stub_init); scheduler_api_on_insert(scheduler_stub_insert); scheduler_api_on_commit(scheduler_stub_commit); scheduler_api_on_rollback(scheduler_stub_rollback); scheduler_api_on_update(scheduler_stub_update); scheduler_api_on_delete(scheduler_stub_delete); scheduler_api_on_hold(scheduler_stub_hold); scheduler_api_on_release(scheduler_stub_release); scheduler_api_on_batch(scheduler_stub_batch); scheduler_api_on_messages(scheduler_stub_messages); scheduler_api_on_envelopes(scheduler_stub_envelopes); scheduler_api_on_schedule(scheduler_stub_schedule); scheduler_api_on_remove(scheduler_stub_remove); scheduler_api_on_suspend(scheduler_stub_suspend); scheduler_api_on_resume(scheduler_stub_resume); scheduler_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/000077500000000000000000000000001355240505200173215ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/Makefile.am000066400000000000000000000007231355240505200213570ustar00rootroot00000000000000SUBDIRS= if HAVE_TABLE_LDAP SUBDIRS+= table-ldap endif if HAVE_TABLE_POSTGRES SUBDIRS+= table-postgres endif if HAVE_TABLE_PYTHON SUBDIRS+= table-python endif if HAVE_TABLE_MYSQL SUBDIRS+= table-mysql endif if HAVE_TABLE_SQLITE SUBDIRS+= table-sqlite endif if HAVE_TABLE_REDIS SUBDIRS+= table-redis endif if HAVE_TABLE_SOCKETMAP SUBDIRS+= table-socketmap endif if HAVE_TABLE_PASSWD SUBDIRS+= table-passwd endif if HAVE_TABLE_STUB SUBDIRS+= table-stub endif OpenSMTPD-extras-6.6.0/extras/tables/README000066400000000000000000000000001355240505200201670ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/000077500000000000000000000000001355240505200213265ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/Makefile.am000066400000000000000000000003431355240505200233620ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-ldap table_ldap_SOURCES = $(SRCS) table_ldap_SOURCES += table_ldap.c table_ldap_SOURCES += aldap.c table_ldap_SOURCES += ber.c OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/aldap.c000066400000000000000000000626001355240505200225570ustar00rootroot00000000000000/* * Copyright (c) 2008 Alexander Schrijver * Copyright (c) 2006, 2007 Marc Balmer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include "aldap.h" #if 0 #define DEBUG #endif #define ALDAP_VERSION 3 static struct ber_element *ldap_parse_search_filter(struct ber_element *, char *); static struct ber_element *ldap_do_parse_search_filter( struct ber_element *, char **); char **aldap_get_stringset(struct ber_element *); char *utoa(char *); char *parseval(char *, size_t); int aldap_create_page_control(struct ber_element *, int, struct aldap_page_control *); #ifdef DEBUG void ldap_debug_elements(struct ber_element *); #endif #ifdef DEBUG #define DPRINTF(x...) printf(x) #define LDAP_DEBUG(x, y) do { fprintf(stderr, "*** " x "\n"); ldap_debug_elements(y); } while (0) #else #define DPRINTF(x...) do { } while (0) #define LDAP_DEBUG(x, y) do { } while (0) #endif int aldap_close(struct aldap *al) { if (close(al->ber.fd) == -1) return (-1); ber_free(&al->ber); free(al); return (0); } struct aldap * aldap_init(int fd) { struct aldap *a; if ((a = calloc(1, sizeof(*a))) == NULL) return NULL; a->ber.fd = fd; return a; } int aldap_bind(struct aldap *ldap, char *binddn, char *bindcred) { struct ber_element *root = NULL, *elm; int error; if (binddn == NULL) binddn = ""; if (bindcred == NULL) bindcred = ""; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; elm = ber_printf_elements(root, "d{tdsst", ++ldap->msgid, BER_CLASS_APP, (unsigned long)LDAP_REQ_BIND, ALDAP_VERSION, binddn, bindcred, BER_CLASS_CONTEXT, (unsigned long)LDAP_AUTH_SIMPLE); if (elm == NULL) goto fail; LDAP_DEBUG("aldap_bind", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) goto fail; return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); ldap->err = ALDAP_ERR_OPERATION_FAILED; return (-1); } int aldap_unbind(struct aldap *ldap) { struct ber_element *root = NULL, *elm; int error; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; elm = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP, LDAP_REQ_UNBIND_30); if (elm == NULL) goto fail; LDAP_DEBUG("aldap_unbind", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) goto fail; return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); ldap->err = ALDAP_ERR_OPERATION_FAILED; return (-1); } int aldap_search(struct aldap *ldap, char *basedn, enum scope scope, char *filter, char **attrs, int typesonly, int sizelimit, int timelimit, struct aldap_page_control *page) { struct ber_element *root = NULL, *ber, *c; int i, error; if ((root = ber_add_sequence(NULL)) == NULL) goto fail; ber = ber_printf_elements(root, "d{t", ++ldap->msgid, BER_CLASS_APP, (unsigned long) LDAP_REQ_SEARCH); if (ber == NULL) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } c = ber; ber = ber_printf_elements(ber, "sEEddb", basedn, (long long)scope, (long long)LDAP_DEREF_NEVER, sizelimit, timelimit, typesonly); if (ber == NULL) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } if ((ber = ldap_parse_search_filter(ber, filter)) == NULL) { ldap->err = ALDAP_ERR_PARSER_ERROR; goto fail; } if ((ber = ber_add_sequence(ber)) == NULL) goto fail; if (attrs != NULL) for (i = 0; attrs[i] != NULL; i++) { if ((ber = ber_add_string(ber, attrs[i])) == NULL) goto fail; } aldap_create_page_control(c, 100, page); LDAP_DEBUG("aldap_search", root); error = ber_write_elements(&ldap->ber, root); ber_free_elements(root); root = NULL; if (error == -1) { ldap->err = ALDAP_ERR_OPERATION_FAILED; goto fail; } return (ldap->msgid); fail: if (root != NULL) ber_free_elements(root); return (-1); } int aldap_create_page_control(struct ber_element *elm, int size, struct aldap_page_control *page) { int len; struct ber c; struct ber_element *ber = NULL; c.br_wbuf = NULL; c.fd = -1; ber = ber_add_sequence(NULL); if (page == NULL) { if (ber_printf_elements(ber, "ds", 50, "") == NULL) goto fail; } else { if (ber_printf_elements(ber, "dx", 50, page->cookie, page->cookie_len) == NULL) goto fail; } if ((len = ber_write_elements(&c, ber)) < 1) goto fail; if (ber_printf_elements(elm, "{t{sx", 2, 0, LDAP_PAGED_OID, c.br_wbuf, (size_t)len) == NULL) goto fail; ber_free_elements(ber); ber_free(&c); return len; fail: if (ber != NULL) ber_free_elements(ber); ber_free(&c); return (-1); } struct aldap_message * aldap_parse(struct aldap *ldap) { int class; unsigned long type; long long msgid = 0; struct aldap_message *m; struct ber_element *a = NULL, *ep; if ((m = calloc(1, sizeof(struct aldap_message))) == NULL) return NULL; if ((m->msg = ber_read_elements(&ldap->ber, NULL)) == NULL) goto parsefail; LDAP_DEBUG("message", m->msg); if (ber_scanf_elements(m->msg, "{ite", &msgid, &class, &type, &a) != 0) goto parsefail; m->msgid = msgid; m->message_type = type; m->protocol_op = a; switch (m->message_type) { case LDAP_RES_BIND: case LDAP_RES_MODIFY: case LDAP_RES_ADD: case LDAP_RES_DELETE: case LDAP_RES_MODRDN: case LDAP_RES_COMPARE: case LDAP_RES_SEARCH_RESULT: if (ber_scanf_elements(m->protocol_op, "{EeSeSe", &m->body.res.rescode, &m->dn, &m->body.res.diagmsg, &a) != 0) goto parsefail; if (m->body.res.rescode == LDAP_REFERRAL) if (ber_scanf_elements(a, "{e", &m->references) != 0) goto parsefail; if (m->msg->be_sub) { for (ep = m->msg->be_sub; ep != NULL; ep = ep->be_next) { ber_scanf_elements(ep, "t", &class, &type); if (class == 2 && type == 0) m->page = aldap_parse_page_control(ep->be_sub->be_sub, ep->be_sub->be_sub->be_len); } } else m->page = NULL; break; case LDAP_RES_SEARCH_ENTRY: if (ber_scanf_elements(m->protocol_op, "{eS{e", &m->dn, &m->body.search.attrs) != 0) goto parsefail; break; case LDAP_RES_SEARCH_REFERENCE: if (ber_scanf_elements(m->protocol_op, "{e", &m->references) != 0) goto parsefail; break; } return m; parsefail: ldap->err = ALDAP_ERR_PARSER_ERROR; aldap_freemsg(m); return NULL; } struct aldap_page_control * aldap_parse_page_control(struct ber_element *control, size_t len) { char *oid, *s; char *encoded; struct ber b; struct ber_element *elm; struct aldap_page_control *page; b.br_wbuf = NULL; b.fd = -1; ber_scanf_elements(control, "ss", &oid, &encoded); ber_set_readbuf(&b, encoded, control->be_next->be_len); elm = ber_read_elements(&b, NULL); if (elm == NULL) { ber_free(&b); return NULL; } if ((page = malloc(sizeof(struct aldap_page_control))) == NULL) { ber_free_elements(elm); ber_free(&b); return NULL; } ber_scanf_elements(elm->be_sub, "is", &page->size, &s); page->cookie_len = elm->be_sub->be_next->be_len; if ((page->cookie = malloc(page->cookie_len)) == NULL) { ber_free_elements(elm); ber_free(&b); free(page); return NULL; } memcpy(page->cookie, s, page->cookie_len); ber_free_elements(elm); ber_free(&b); return page; } void aldap_freepage(struct aldap_page_control *page) { if (page->cookie) free(page->cookie); free(page); } void aldap_freemsg(struct aldap_message *msg) { if (msg->msg) ber_free_elements(msg->msg); free(msg); } int aldap_get_resultcode(struct aldap_message *msg) { return msg->body.res.rescode; } char * aldap_get_dn(struct aldap_message *msg) { char *dn; if (msg->dn == NULL) return NULL; if (ber_get_string(msg->dn, &dn) == -1) return NULL; return utoa(dn); } char ** aldap_get_references(struct aldap_message *msg) { if (msg->references == NULL) return NULL; return aldap_get_stringset(msg->references); } void aldap_free_references(char **values) { int i; if (values == NULL) return; for (i = 0; values[i] != NULL; i++) free(values[i]); free(values); } char * aldap_get_diagmsg(struct aldap_message *msg) { char *s; if (msg->body.res.diagmsg == NULL) return NULL; if (ber_get_string(msg->body.res.diagmsg, &s) == -1) return NULL; return utoa(s); } int aldap_count_attrs(struct aldap_message *msg) { int i; struct ber_element *a; if (msg->body.search.attrs == NULL) return (-1); for (i = 0, a = msg->body.search.attrs; a != NULL && ber_get_eoc(a) != 0; i++, a = a->be_next) ; return i; } int aldap_first_attr(struct aldap_message *msg, char **outkey, char ***outvalues) { struct ber_element *b, *c; char *key; char **ret; if (msg->body.search.attrs == NULL) goto fail; if (ber_scanf_elements(msg->body.search.attrs, "{s(e)}e", &key, &b, &c) != 0) goto fail; msg->body.search.iter = msg->body.search.attrs->be_next; if ((ret = aldap_get_stringset(b)) == NULL) goto fail; (*outvalues) = ret; (*outkey) = utoa(key); return (1); fail: (*outkey) = NULL; (*outvalues) = NULL; return (-1); } int aldap_next_attr(struct aldap_message *msg, char **outkey, char ***outvalues) { struct ber_element *a, *b; char *key; char **ret; if (msg->body.search.iter == NULL) goto notfound; LDAP_DEBUG("attr", msg->body.search.iter); if (ber_get_eoc(msg->body.search.iter) == 0) goto notfound; if (ber_scanf_elements(msg->body.search.iter, "{s(e)}e", &key, &a, &b) != 0) goto fail; msg->body.search.iter = msg->body.search.iter->be_next; if ((ret = aldap_get_stringset(a)) == NULL) goto fail; (*outvalues) = ret; (*outkey) = utoa(key); return (1); fail: notfound: (*outkey) = NULL; (*outvalues) = NULL; return (-1); } int aldap_match_attr(struct aldap_message *msg, char *inkey, char ***outvalues) { struct ber_element *a, *b; char *descr = NULL; char **ret; if (msg->body.search.attrs == NULL) goto fail; LDAP_DEBUG("attr", msg->body.search.attrs); for (a = msg->body.search.attrs;;) { if (a == NULL) goto notfound; if (ber_get_eoc(a) == 0) goto notfound; if (ber_scanf_elements(a, "{s(e", &descr, &b) != 0) goto fail; if (strcasecmp(descr, inkey) == 0) goto attrfound; a = a->be_next; } attrfound: if ((ret = aldap_get_stringset(b)) == NULL) goto fail; (*outvalues) = ret; return (1); fail: notfound: (*outvalues) = NULL; return (-1); } int aldap_free_attr(char **values) { int i; if (values == NULL) return -1; for (i = 0; values[i] != NULL; i++) free(values[i]); free(values); return (1); } void aldap_free_url(struct aldap_url *lu) { free(lu->buffer); free(lu->filter); } int aldap_parse_url(char *url, struct aldap_url *lu) { char *p, *forward, *forward2; const char *errstr = NULL; int i; if ((lu->buffer = strdup(url)) == NULL) return (-1); p = lu->buffer; /* protocol */ if (strncasecmp(LDAP_URL, p, strlen(LDAP_URL)) != 0) goto fail; lu->protocol = LDAP; p += strlen(LDAP_URL); /* host and optional port */ if ((forward = strchr(p, '/')) != NULL) *forward = '\0'; /* find the optional port */ if ((forward2 = strchr(p, ':')) != NULL) { *forward2 = '\0'; /* if a port is given */ if (*(forward2+1) != '\0') { #define PORT_MAX UINT16_MAX lu->port = strtonum(++forward2, 0, PORT_MAX, &errstr); if (errstr) goto fail; } } else { lu->port = LDAP_PORT; } /* fail if no host is given */ if (strlen(p) == 0) goto fail; lu->host = p; if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* dn */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; lu->dn = p; if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* attributes */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; for (i = 0; i < MAXATTR; i++) { if ((forward2 = strchr(p, ',')) == NULL) { if (strlen(p) == 0) break; lu->attributes[i] = p; break; } *forward2 = '\0'; lu->attributes[i] = p; p = ++forward2; } if (forward == NULL) goto done; /* p is assigned either a pointer to a character or to '\0' */ p = ++forward; if (strlen(p) == 0) goto done; /* scope */ if ((forward = strchr(p, '?')) != NULL) *forward = '\0'; if (strcmp(p, "base") == 0) lu->scope = LDAP_SCOPE_BASE; else if (strcmp(p, "one") == 0) lu->scope = LDAP_SCOPE_ONELEVEL; else if (strcmp(p, "sub") == 0) lu->scope = LDAP_SCOPE_SUBTREE; else goto fail; if (forward == NULL) goto done; p = ++forward; if (strlen(p) == 0) goto done; /* filter */ if (p) lu->filter = p; done: free(url); return (1); fail: free(lu->buffer); lu->buffer = NULL; return (-1); } #if 0 int aldap_search_url(struct aldap *ldap, char *url, int typesonly, int sizelimit, int timelimit) { struct aldap_url *lu; if ((lu = calloc(1, sizeof(*lu))) == NULL) return (-1); if (aldap_parse_url(url, lu)) goto fail; if (aldap_search(ldap, lu->dn, lu->scope, lu->filter, lu->attributes, typesonly, sizelimit, timelimit) == -1) goto fail; aldap_free_url(lu); return (ldap->msgid); fail: aldap_free_url(lu); return (-1); } #endif /* * internal functions */ char ** aldap_get_stringset(struct ber_element *elm) { struct ber_element *a; int i; char **ret; char *s; if (elm->be_type != BER_TYPE_OCTETSTRING) return NULL; for (a = elm, i = 1; i > 0 && a != NULL && a->be_type == BER_TYPE_OCTETSTRING; a = a->be_next, i++) ; if (i == 1) return NULL; if ((ret = calloc(i + 1, sizeof(char *))) == NULL) return NULL; for (a = elm, i = 0; a != NULL && a->be_type == BER_TYPE_OCTETSTRING; a = a->be_next, i++) { ber_get_string(a, &s); ret[i] = utoa(s); } ret[i + 1] = NULL; return ret; } /* * Base case for ldap_do_parse_search_filter * * returns: * struct ber_element *, ber_element tree * NULL, parse failed */ static struct ber_element * ldap_parse_search_filter(struct ber_element *ber, char *filter) { struct ber_element *elm; char *cp; cp = filter; if (cp == NULL || *cp == '\0') { errno = EINVAL; return (NULL); } if ((elm = ldap_do_parse_search_filter(ber, &cp)) == NULL) return (NULL); if (*cp != '\0') { ber_free_elements(elm); ber_link_elements(ber, NULL); errno = EINVAL; return (NULL); } return (elm); } /* * Translate RFC4515 search filter string into ber_element tree * * returns: * struct ber_element *, ber_element tree * NULL, parse failed * * notes: * when cp is passed to a recursive invocation, it is updated * to point one character beyond the filter that was passed * i.e., cp jumps to "(filter)" upon return * ^ * goto's used to discriminate error-handling based on error type * doesn't handle extended filters (yet) * */ static struct ber_element * ldap_do_parse_search_filter(struct ber_element *prev, char **cpp) { struct ber_element *elm, *root = NULL; char *attr_desc, *attr_val, *parsed_val, *cp; size_t len; unsigned long type; root = NULL; /* cpp should pass in pointer to opening parenthesis of "(filter)" */ cp = *cpp; if (*cp != '(') goto syntaxfail; switch (*++cp) { case '&': /* AND */ case '|': /* OR */ if (*cp == '&') type = LDAP_FILT_AND; else type = LDAP_FILT_OR; if ((elm = ber_add_set(prev)) == NULL) goto callfail; root = elm; ber_set_header(elm, BER_CLASS_CONTEXT, type); if (*++cp != '(') /* opening `(` of filter */ goto syntaxfail; while (*cp == '(') { if ((elm = ldap_do_parse_search_filter(elm, &cp)) == NULL) goto bad; } if (*cp != ')') /* trailing `)` of filter */ goto syntaxfail; break; case '!': /* NOT */ if ((root = ber_add_sequence(prev)) == NULL) goto callfail; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_NOT); cp++; /* now points to sub-filter */ if (ldap_do_parse_search_filter(root, &cp) == NULL) goto bad; if (*cp != ')') /* trailing `)` of filter */ goto syntaxfail; break; default: /* SIMPLE || PRESENCE */ attr_desc = cp; len = strcspn(cp, "()<>~="); cp += len; switch (*cp) { case '~': type = LDAP_FILT_APPR; cp++; break; case '<': type = LDAP_FILT_LE; cp++; break; case '>': type = LDAP_FILT_GE; cp++; break; case '=': type = LDAP_FILT_EQ; /* assume EQ until disproven */ break; case '(': case ')': default: goto syntaxfail; } attr_val = ++cp; /* presence filter */ if (strncmp(attr_val, "*)", 2) == 0) { cp++; /* point to trailing `)` */ if ((root = ber_add_nstring(prev, attr_desc, len)) == NULL) goto bad; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_PRES); break; } if ((root = ber_add_sequence(prev)) == NULL) goto callfail; ber_set_header(root, BER_CLASS_CONTEXT, type); if ((elm = ber_add_nstring(root, attr_desc, len)) == NULL) goto callfail; len = strcspn(attr_val, "*)"); if (len == 0 && *cp != '*') goto syntaxfail; cp += len; if (*cp == '\0') goto syntaxfail; if (*cp == '*') { /* substring filter */ int initial; cp = attr_val; ber_set_header(root, BER_CLASS_CONTEXT, LDAP_FILT_SUBS); if ((elm = ber_add_sequence(elm)) == NULL) goto callfail; for (initial = 1;; cp++, initial = 0) { attr_val = cp; len = strcspn(attr_val, "*)"); if (len == 0) { if (*cp == ')') break; else continue; } cp += len; if (*cp == '\0') goto syntaxfail; if (initial) type = LDAP_FILT_SUBS_INIT; else if (*cp == ')') type = LDAP_FILT_SUBS_FIN; else type = LDAP_FILT_SUBS_ANY; if ((parsed_val = parseval(attr_val, len)) == NULL) goto callfail; elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val)); free(parsed_val); if (elm == NULL) goto callfail; ber_set_header(elm, BER_CLASS_CONTEXT, type); if (type == LDAP_FILT_SUBS_FIN) break; } break; } if ((parsed_val = parseval(attr_val, len)) == NULL) goto callfail; elm = ber_add_nstring(elm, parsed_val, strlen(parsed_val)); free(parsed_val); if (elm == NULL) goto callfail; break; } cp++; /* now points one char beyond the trailing `)` */ *cpp = cp; return (root); syntaxfail: /* XXX -- error reporting */ callfail: bad: if (root != NULL) ber_free_elements(root); ber_link_elements(prev, NULL); return (NULL); } #ifdef DEBUG /* * Display a list of ber elements. * */ void ldap_debug_elements(struct ber_element *root) { static int indent = 0; long long v; int d; char *buf; size_t len; unsigned int i; int constructed; struct ber_oid o; /* calculate lengths */ ber_calc_len(root); switch (root->be_encoding) { case BER_TYPE_SEQUENCE: case BER_TYPE_SET: constructed = root->be_encoding; break; default: constructed = 0; break; } fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); switch (root->be_class) { case BER_CLASS_UNIVERSAL: fprintf(stderr, "class: universal(%u) type: ", root->be_class); switch (root->be_type) { case BER_TYPE_EOC: fprintf(stderr, "end-of-content"); break; case BER_TYPE_BOOLEAN: fprintf(stderr, "boolean"); break; case BER_TYPE_INTEGER: fprintf(stderr, "integer"); break; case BER_TYPE_BITSTRING: fprintf(stderr, "bit-string"); break; case BER_TYPE_OCTETSTRING: fprintf(stderr, "octet-string"); break; case BER_TYPE_NULL: fprintf(stderr, "null"); break; case BER_TYPE_OBJECT: fprintf(stderr, "object"); break; case BER_TYPE_ENUMERATED: fprintf(stderr, "enumerated"); break; case BER_TYPE_SEQUENCE: fprintf(stderr, "sequence"); break; case BER_TYPE_SET: fprintf(stderr, "set"); break; } break; case BER_CLASS_APPLICATION: fprintf(stderr, "class: application(%u) type: ", root->be_class); switch (root->be_type) { case LDAP_REQ_BIND: fprintf(stderr, "bind"); break; case LDAP_RES_BIND: fprintf(stderr, "bind"); break; case LDAP_REQ_UNBIND_30: break; case LDAP_REQ_SEARCH: fprintf(stderr, "search"); break; case LDAP_RES_SEARCH_ENTRY: fprintf(stderr, "search_entry"); break; case LDAP_RES_SEARCH_RESULT: fprintf(stderr, "search_result"); break; case LDAP_REQ_MODIFY: fprintf(stderr, "modify"); break; case LDAP_RES_MODIFY: fprintf(stderr, "modify"); break; case LDAP_REQ_ADD: fprintf(stderr, "add"); break; case LDAP_RES_ADD: fprintf(stderr, "add"); break; case LDAP_REQ_DELETE_30: fprintf(stderr, "delete"); break; case LDAP_RES_DELETE: fprintf(stderr, "delete"); break; case LDAP_REQ_MODRDN: fprintf(stderr, "modrdn"); break; case LDAP_RES_MODRDN: fprintf(stderr, "modrdn"); break; case LDAP_REQ_COMPARE: fprintf(stderr, "compare"); break; case LDAP_RES_COMPARE: fprintf(stderr, "compare"); break; case LDAP_REQ_ABANDON_30: fprintf(stderr, "abandon"); break; } break; case BER_CLASS_PRIVATE: fprintf(stderr, "class: private(%u) type: ", root->be_class); fprintf(stderr, "encoding (%lu) type: ", root->be_encoding); break; case BER_CLASS_CONTEXT: /* XXX: this is not correct */ fprintf(stderr, "class: context(%u) type: ", root->be_class); switch(root->be_type) { case LDAP_AUTH_SIMPLE: fprintf(stderr, "auth simple"); break; } break; default: fprintf(stderr, "class: (%u) type: ", root->be_class); break; } fprintf(stderr, "(%lu) encoding %lu ", root->be_type, root->be_encoding); if (constructed) root->be_encoding = constructed; switch (root->be_encoding) { case BER_TYPE_BOOLEAN: if (ber_get_boolean(root, &d) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "%s(%d)\n", d ? "true" : "false", d); break; case BER_TYPE_INTEGER: if (ber_get_integer(root, &v) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "value %lld\n", v); break; case BER_TYPE_ENUMERATED: if (ber_get_enumerated(root, &v) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "value %lld\n", v); break; case BER_TYPE_BITSTRING: if (ber_get_bitstring(root, (void *)&buf, &len) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "hexdump "); for (i = 0; i < len; i++) fprintf(stderr, "%02x", buf[i]); fprintf(stderr, "\n"); break; case BER_TYPE_OBJECT: if (ber_get_oid(root, &o) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "\n"); break; case BER_TYPE_OCTETSTRING: if (ber_get_nstring(root, (void *)&buf, &len) == -1) { fprintf(stderr, "\n"); break; } fprintf(stderr, "string \"%.*s\"\n", len, buf); break; case BER_TYPE_NULL: /* no payload */ case BER_TYPE_EOC: case BER_TYPE_SEQUENCE: case BER_TYPE_SET: default: fprintf(stderr, "\n"); break; } if (constructed && root->be_sub) { indent += 2; ldap_debug_elements(root->be_sub); indent -= 2; } if (root->be_next) ldap_debug_elements(root->be_next); } #endif /* * Convert UTF-8 to ASCII. * notes: * non-ASCII characters are displayed as '?' * the argument u should be a NULL terminated sequence of UTF-8 bytes. */ char * utoa(char *u) { int len, i, j; char *str; /* calculate the length to allocate */ for (len = 0, i = 0; u[i] != '\0'; ) { if ((u[i] & 0xF0) == 0xF0) i += 4; else if ((u[i] & 0xE0) == 0xE0) i += 3; else if ((u[i] & 0xC0) == 0xC0) i += 2; else i += 1; len++; } if ((str = calloc(len + 1, sizeof(char))) == NULL) return NULL; /* copy the ASCII characters to the newly allocated string */ for (i = 0, j = 0; u[i] != '\0'; j++) { if ((u[i] & 0xF0) == 0xF0) { str[j] = '?'; i += 4; } else if ((u[i] & 0xE0) == 0xE0) { str[j] = '?'; i += 3; } else if ((u[i] & 0xC0) == 0xC0) { str[j] = '?'; i += 2; } else { str[j] = u[i]; i += 1; } } return str; } /* * Parse a LDAP value * notes: * the argument u should be a NULL terminated sequence of ASCII bytes. */ char * parseval(char *p, size_t len) { char hex[3]; char *cp = p, *buffer, *newbuffer; size_t size, newsize, i, j; size = 50; if ((buffer = calloc(1, size)) == NULL) return NULL; for (i = j = 0; j < len; i++) { if (i >= size) { newsize = size + 1024; if ((newbuffer = realloc(buffer, newsize)) == NULL) { free(buffer); return (NULL); } buffer = newbuffer; size = newsize; } if (cp[j] == '\\') { (void)strlcpy(hex, cp + j + 1, sizeof(hex)); buffer[i] = (char)strtoumax(hex, NULL, 16); j += 3; } else { buffer[i] = cp[j]; j++; } } return buffer; } int aldap_get_errno(struct aldap *a, const char **estr) { switch (a->err) { case ALDAP_ERR_SUCCESS: *estr = "success"; break; case ALDAP_ERR_PARSER_ERROR: *estr = "parser failed"; break; case ALDAP_ERR_INVALID_FILTER: *estr = "invalid filter"; break; case ALDAP_ERR_OPERATION_FAILED: *estr = "operation failed"; break; default: *estr = "unknown"; break; } return (a->err); } OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/aldap.h000066400000000000000000000125371355240505200225700ustar00rootroot00000000000000/* * Copyright (c) 2008 Alexander Schrijver * Copyright (c) 2006, 2007 Marc Balmer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "ber.h" #define LDAP_URL "ldap://" #define LDAP_PORT 389 #define LDAP_PAGED_OID "1.2.840.113556.1.4.319" struct aldap { #define ALDAP_ERR_SUCCESS 0 #define ALDAP_ERR_PARSER_ERROR 1 #define ALDAP_ERR_INVALID_FILTER 2 #define ALDAP_ERR_OPERATION_FAILED 3 uint8_t err; int msgid; struct ber ber; }; struct aldap_page_control { int size; char *cookie; unsigned int cookie_len; }; struct aldap_message { int msgid; int message_type; struct ber_element *msg; struct ber_element *header; struct ber_element *protocol_op; struct ber_element *dn; union { struct { long long rescode; struct ber_element *diagmsg; } res; struct { struct ber_element *iter; struct ber_element *attrs; } search; } body; struct ber_element *references; struct aldap_page_control *page; }; enum aldap_protocol { LDAP, LDAPS }; struct aldap_url { int protocol; char *host; in_port_t port; char *dn; #define MAXATTR 1024 char *attributes[MAXATTR]; int scope; char *filter; char *buffer; }; enum protocol_op { LDAP_REQ_BIND = 0, LDAP_RES_BIND = 1, LDAP_REQ_UNBIND_30 = 2, LDAP_REQ_SEARCH = 3, LDAP_RES_SEARCH_ENTRY = 4, LDAP_RES_SEARCH_RESULT = 5, LDAP_REQ_MODIFY = 6, LDAP_RES_MODIFY = 7, LDAP_REQ_ADD = 8, LDAP_RES_ADD = 9, LDAP_REQ_DELETE_30 = 10, LDAP_RES_DELETE = 11, LDAP_REQ_MODRDN = 12, LDAP_RES_MODRDN = 13, LDAP_REQ_COMPARE = 14, LDAP_RES_COMPARE = 15, LDAP_REQ_ABANDON_30 = 16, LDAP_RES_SEARCH_REFERENCE = 19, }; enum deref_aliases { LDAP_DEREF_NEVER = 0, LDAP_DEREF_SEARCHING = 1, LDAP_DEREF_FINDING = 2, LDAP_DEREF_ALWAYS = 3, }; enum authentication_choice { LDAP_AUTH_SIMPLE = 0, }; enum scope { LDAP_SCOPE_BASE = 0, LDAP_SCOPE_ONELEVEL = 1, LDAP_SCOPE_SUBTREE = 2, }; enum result_code { LDAP_SUCCESS = 0, LDAP_OPERATIONS_ERROR = 1, LDAP_PROTOCOL_ERROR = 2, LDAP_TIMELIMIT_EXCEEDED = 3, LDAP_SIZELIMIT_EXCEEDED = 4, LDAP_COMPARE_FALSE = 5, LDAP_COMPARE_TRUE = 6, LDAP_STRONG_AUTH_NOT_SUPPORTED = 7, LDAP_STRONG_AUTH_REQUIRED = 8, LDAP_REFERRAL = 10, LDAP_ADMINLIMIT_EXCEEDED = 11, LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 12, LDAP_CONFIDENTIALITY_REQUIRED = 13, LDAP_SASL_BIND_IN_PROGRESS = 14, LDAP_NO_SUCH_ATTRIBUTE = 16, LDAP_UNDEFINED_TYPE = 17, LDAP_INAPPROPRIATE_MATCHING = 18, LDAP_CONSTRAINT_VIOLATION = 19, LDAP_TYPE_OR_VALUE_EXISTS = 20, LDAP_INVALID_SYNTAX = 21, LDAP_NO_SUCH_OBJECT = 32, LDAP_ALIAS_PROBLEM = 33, LDAP_INVALID_DN_SYNTAX = 34, LDAP_ALIAS_DEREF_PROBLEM = 36, LDAP_INAPPROPRIATE_AUTH = 48, LDAP_INVALID_CREDENTIALS = 49, LDAP_INSUFFICIENT_ACCESS = 50, LDAP_BUSY = 51, LDAP_UNAVAILABLE = 52, LDAP_UNWILLING_TO_PERFORM = 53, LDAP_LOOP_DETECT = 54, LDAP_NAMING_VIOLATION = 64, LDAP_OBJECT_CLASS_VIOLATION = 65, LDAP_NOT_ALLOWED_ON_NONLEAF = 66, LDAP_NOT_ALLOWED_ON_RDN = 67, LDAP_ALREADY_EXISTS = 68, LDAP_NO_OBJECT_CLASS_MODS = 69, LDAP_AFFECTS_MULTIPLE_DSAS = 71, LDAP_OTHER = 80, }; enum ldap_filter { LDAP_FILT_AND = 0, LDAP_FILT_OR = 1, LDAP_FILT_NOT = 2, LDAP_FILT_EQ = 3, LDAP_FILT_SUBS = 4, LDAP_FILT_GE = 5, LDAP_FILT_LE = 6, LDAP_FILT_PRES = 7, LDAP_FILT_APPR = 8, }; enum ldap_subfilter { LDAP_FILT_SUBS_INIT = 0, LDAP_FILT_SUBS_ANY = 1, LDAP_FILT_SUBS_FIN = 2, }; struct aldap *aldap_init(int fd); int aldap_close(struct aldap *); struct aldap_message *aldap_parse(struct aldap *); void aldap_freemsg(struct aldap_message *); int aldap_bind(struct aldap *, char *, char *); int aldap_unbind(struct aldap *); int aldap_search(struct aldap *, char *, enum scope, char *, char **, int, int, int, struct aldap_page_control *); int aldap_get_errno(struct aldap *, const char **); int aldap_get_resultcode(struct aldap_message *); char *aldap_get_dn(struct aldap_message *); char *aldap_get_diagmsg(struct aldap_message *); char **aldap_get_references(struct aldap_message *); void aldap_free_references(char **values); int aldap_parse_url(char *, struct aldap_url *); void aldap_free_url(struct aldap_url *); #if 0 int aldap_search_url(struct aldap *, char *, int, int, int); #endif int aldap_count_attrs(struct aldap_message *); int aldap_match_attr(struct aldap_message *, char *, char ***); int aldap_first_attr(struct aldap_message *, char **, char ***); int aldap_next_attr(struct aldap_message *, char **, char ***); int aldap_free_attr(char **); struct aldap_page_control *aldap_parse_page_control(struct ber_element *, size_t len); void aldap_freepage(struct aldap_page_control *); OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/ber.c000066400000000000000000000627771355240505200222650ustar00rootroot00000000000000/* * Copyright (c) 2007 Reyk Floeter * Copyright (c) 2006, 2007 Claudio Jeker * Copyright (c) 2006, 2007 Marc Balmer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include /* XXX for debug output */ #include /* XXX for debug output */ #include #include #include #include "ber.h" #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) #define BER_TYPE_CONSTRUCTED 0x20 /* otherwise primitive */ #define BER_TYPE_SINGLE_MAX 30 #define BER_TAG_MASK 0x1f #define BER_TAG_MORE 0x80 /* more subsequent octets */ #define BER_TAG_TYPE_MASK 0x7f #define BER_CLASS_SHIFT 6 static int ber_dump_element(struct ber *ber, struct ber_element *root); static void ber_dump_header(struct ber *ber, struct ber_element *root); static void ber_putc(struct ber *ber, unsigned char c); static void ber_write(struct ber *ber, void *buf, size_t len); static ssize_t get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct); static ssize_t get_len(struct ber *b, ssize_t *len); static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm); static ssize_t ber_readbuf(struct ber *b, void *buf, size_t nbytes); static ssize_t ber_getc(struct ber *b, unsigned char *c); static ssize_t ber_read(struct ber *ber, void *buf, size_t len); #ifdef DEBUG #define DPRINTF(...) printf(__VA_ARGS__) #else #define DPRINTF(...) do { } while (0) #endif struct ber_element * ber_get_element(unsigned long encoding) { struct ber_element *elm; if ((elm = calloc(1, sizeof(*elm))) == NULL) return NULL; elm->be_encoding = encoding; ber_set_header(elm, BER_CLASS_UNIVERSAL, BER_TYPE_DEFAULT); return elm; } void ber_set_header(struct ber_element *elm, int class, unsigned long type) { elm->be_class = class & BER_CLASS_MASK; if (type == BER_TYPE_DEFAULT) type = elm->be_encoding; elm->be_type = type; } void ber_link_elements(struct ber_element *prev, struct ber_element *elm) { if (prev != NULL) { if ((prev->be_encoding == BER_TYPE_SEQUENCE || prev->be_encoding == BER_TYPE_SET) && prev->be_sub == NULL) prev->be_sub = elm; else prev->be_next = elm; } } struct ber_element * ber_unlink_elements(struct ber_element *prev) { struct ber_element *elm; if ((prev->be_encoding == BER_TYPE_SEQUENCE || prev->be_encoding == BER_TYPE_SET) && prev->be_sub != NULL) { elm = prev->be_sub; prev->be_sub = NULL; } else { elm = prev->be_next; prev->be_next = NULL; } return (elm); } void ber_replace_elements(struct ber_element *prev, struct ber_element *new) { struct ber_element *ber, *next; ber = ber_unlink_elements(prev); next = ber_unlink_elements(ber); ber_link_elements(new, next); ber_link_elements(prev, new); /* cleanup old element */ ber_free_elements(ber); } struct ber_element * ber_add_sequence(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_SEQUENCE)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_set(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_SET)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_enumerated(struct ber_element *prev, long long val) { struct ber_element *elm; unsigned int i, len = 0; unsigned char cur, last = 0; if ((elm = ber_get_element(BER_TYPE_ENUMERATED)) == NULL) return NULL; elm->be_numeric = val; for (i = 0; i < sizeof(long long); i++) { cur = val & 0xff; if (cur != 0 && cur != 0xff) len = i; if ((cur == 0 && last & 0x80) || (cur == 0xff && (last & 0x80) == 0)) len = i; val >>= 8; last = cur; } elm->be_len = len + 1; ber_link_elements(prev, elm); return elm; } struct ber_element * ber_add_integer(struct ber_element *prev, long long val) { struct ber_element *elm; unsigned int i, len = 0; unsigned char cur, last = 0; if ((elm = ber_get_element(BER_TYPE_INTEGER)) == NULL) return NULL; elm->be_numeric = val; for (i = 0; i < sizeof(long long); i++) { cur = val & 0xff; if (cur != 0 && cur != 0xff) len = i; if ((cur == 0 && last & 0x80) || (cur == 0xff && (last & 0x80) == 0)) len = i; val >>= 8; last = cur; } elm->be_len = len + 1; ber_link_elements(prev, elm); return elm; } int ber_get_integer(struct ber_element *elm, long long *n) { if (elm->be_encoding != BER_TYPE_INTEGER) return -1; *n = elm->be_numeric; return 0; } int ber_get_enumerated(struct ber_element *elm, long long *n) { if (elm->be_encoding != BER_TYPE_ENUMERATED) return -1; *n = elm->be_numeric; return 0; } struct ber_element * ber_add_boolean(struct ber_element *prev, int boolean) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_BOOLEAN)) == NULL) return NULL; elm->be_numeric = boolean ? 0xff : 0; elm->be_len = 1; ber_link_elements(prev, elm); return elm; } int ber_get_boolean(struct ber_element *elm, int *b) { if (elm->be_encoding != BER_TYPE_BOOLEAN) return -1; *b = !(elm->be_numeric == 0); return 0; } struct ber_element * ber_add_string(struct ber_element *prev, const char *string) { return ber_add_nstring(prev, string, strlen(string)); } struct ber_element * ber_add_nstring(struct ber_element *prev, const char *string0, size_t len) { struct ber_element *elm; char *string; if ((string = calloc(1, len)) == NULL) return NULL; if ((elm = ber_get_element(BER_TYPE_OCTETSTRING)) == NULL) { free(string); return NULL; } memmove(string, string0, len); elm->be_val = string; elm->be_len = len; elm->be_free = 1; /* free string on cleanup */ ber_link_elements(prev, elm); return elm; } int ber_get_string(struct ber_element *elm, char **s) { if (elm->be_encoding != BER_TYPE_OCTETSTRING) return -1; *s = elm->be_val; return 0; } int ber_get_nstring(struct ber_element *elm, void **p, size_t *len) { if (elm->be_encoding != BER_TYPE_OCTETSTRING) return -1; *p = elm->be_val; *len = elm->be_len; return 0; } struct ber_element * ber_add_bitstring(struct ber_element *prev, const void *v0, size_t len) { struct ber_element *elm; void *v; if ((v = calloc(1, len)) == NULL) return NULL; if ((elm = ber_get_element(BER_TYPE_BITSTRING)) == NULL) { free(v); return NULL; } memmove(v, v0, len); elm->be_val = v; elm->be_len = len; elm->be_free = 1; /* free string on cleanup */ ber_link_elements(prev, elm); return elm; } int ber_get_bitstring(struct ber_element *elm, void **v, size_t *len) { if (elm->be_encoding != BER_TYPE_BITSTRING) return -1; *v = elm->be_val; *len = elm->be_len; return 0; } struct ber_element * ber_add_null(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_NULL)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } int ber_get_null(struct ber_element *elm) { if (elm->be_encoding != BER_TYPE_NULL) return -1; return 0; } struct ber_element * ber_add_eoc(struct ber_element *prev) { struct ber_element *elm; if ((elm = ber_get_element(BER_TYPE_EOC)) == NULL) return NULL; ber_link_elements(prev, elm); return elm; } int ber_get_eoc(struct ber_element *elm) { if (elm->be_encoding != BER_TYPE_EOC) return -1; return 0; } size_t ber_oid2ber(struct ber_oid *o, uint8_t *buf, size_t len) { uint32_t v; unsigned int i, j = 0, k; if (o->bo_n < BER_MIN_OID_LEN || o->bo_n > BER_MAX_OID_LEN || o->bo_id[0] > 2 || o->bo_id[1] > 40) return (0); v = (o->bo_id[0] * 40) + o->bo_id[1]; for (i = 2, j = 0; i <= o->bo_n; v = o->bo_id[i], i++) { for (k = 28; k >= 7; k -= 7) { if (v >= (unsigned int)(1 << k)) { if (len) buf[j] = v >> k | BER_TAG_MORE; j++; } } if (len) buf[j] = v & BER_TAG_TYPE_MASK; j++; } return (j); } int ber_string2oid(const char *oidstr, struct ber_oid *o) { char *sp, *p, str[BUFSIZ]; const char *errstr; if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) return (-1); memset(o, 0, sizeof(*o)); /* Parse OID strings in the common forms n.n.n, n_n_n_n, or n-n-n */ for (p = sp = str; p != NULL; sp = p) { if ((p = strpbrk(p, "._-")) != NULL) *p++ = '\0'; o->bo_id[o->bo_n++] = strtonum(sp, 0, UINT_MAX, &errstr); if (errstr || o->bo_n > BER_MAX_OID_LEN) return (-1); } return (0); } struct ber_element * ber_add_oid(struct ber_element *prev, struct ber_oid *o) { struct ber_element *elm; uint8_t *buf; size_t len; if ((elm = ber_get_element(BER_TYPE_OBJECT)) == NULL) return (NULL); if ((len = ber_oid2ber(o, NULL, 0)) == 0) goto fail; if ((buf = calloc(1, len)) == NULL) goto fail; elm->be_val = buf; elm->be_len = len; elm->be_free = 1; if (ber_oid2ber(o, buf, len) != len) goto fail; ber_link_elements(prev, elm); return (elm); fail: ber_free_elements(elm); return (NULL); } struct ber_element * ber_add_noid(struct ber_element *prev, struct ber_oid *o, int n) { struct ber_oid no; if (n > BER_MAX_OID_LEN) return (NULL); no.bo_n = n; memmove(&no.bo_id, &o->bo_id, sizeof(no.bo_id)); return (ber_add_oid(prev, &no)); } struct ber_element * ber_add_oidstring(struct ber_element *prev, const char *oidstr) { struct ber_oid o; if (ber_string2oid(oidstr, &o) == -1) return (NULL); return (ber_add_oid(prev, &o)); } int ber_get_oid(struct ber_element *elm, struct ber_oid *o) { uint8_t *buf; size_t len, i = 0, j = 0; if (elm->be_encoding != BER_TYPE_OBJECT) return (-1); buf = elm->be_val; len = elm->be_len; if (!buf[i]) return (-1); memset(o, 0, sizeof(*o)); o->bo_id[j++] = buf[i] / 40; o->bo_id[j++] = buf[i++] % 40; for (; i < len && j < BER_MAX_OID_LEN; i++) { o->bo_id[j] = (o->bo_id[j] << 7) + (buf[i] & ~0x80); if (buf[i] & 0x80) continue; j++; } o->bo_n = j; return (0); } struct ber_element * ber_printf_elements(struct ber_element *ber, char *fmt, ...) { va_list ap; int d, class; size_t len; unsigned long type; long long i; char *s; void *p; struct ber_oid *o; struct ber_element *sub = ber, *e; va_start(ap, fmt); while (*fmt) { switch (*fmt++) { case 'B': p = va_arg(ap, void *); len = va_arg(ap, size_t); if ((ber = ber_add_bitstring(ber, p, len)) == NULL) goto fail; break; case 'b': d = va_arg(ap, int); if ((ber = ber_add_boolean(ber, d)) == NULL) goto fail; break; case 'd': d = va_arg(ap, int); if ((ber = ber_add_integer(ber, d)) == NULL) goto fail; break; case 'e': e = va_arg(ap, struct ber_element *); ber_link_elements(ber, e); break; case 'E': i = va_arg(ap, long long); if ((ber = ber_add_enumerated(ber, i)) == NULL) goto fail; break; case 'i': i = va_arg(ap, long long); if ((ber = ber_add_integer(ber, i)) == NULL) goto fail; break; case 'O': o = va_arg(ap, struct ber_oid *); if ((ber = ber_add_oid(ber, o)) == NULL) goto fail; break; case 'o': s = va_arg(ap, char *); if ((ber = ber_add_oidstring(ber, s)) == NULL) goto fail; break; case 's': s = va_arg(ap, char *); if ((ber = ber_add_string(ber, s)) == NULL) goto fail; break; case 't': class = va_arg(ap, int); type = va_arg(ap, unsigned long); ber_set_header(ber, class, type); break; case 'x': s = va_arg(ap, char *); len = va_arg(ap, size_t); if ((ber = ber_add_nstring(ber, s, len)) == NULL) goto fail; break; case '0': if ((ber = ber_add_null(ber)) == NULL) goto fail; break; case '{': if ((ber = sub = ber_add_sequence(ber)) == NULL) goto fail; break; case '(': if ((ber = sub = ber_add_set(ber)) == NULL) goto fail; break; case '}': case ')': ber = sub; break; case '.': if ((e = ber_add_eoc(ber)) == NULL) goto fail; ber = e; break; default: break; } } va_end(ap); return (ber); fail: ber_free_elements(ber); return (NULL); } int ber_scanf_elements(struct ber_element *ber, char *fmt, ...) { #define _MAX_SEQ 128 va_list ap; int *d, level = -1; unsigned long *t; long long *i; void **ptr; size_t *len, ret = 0, n = strlen(fmt); char **s; struct ber_oid *o; struct ber_element *parent[_MAX_SEQ], **e; memset(parent, 0, sizeof(struct ber_element *) * _MAX_SEQ); va_start(ap, fmt); while (*fmt) { switch (*fmt++) { case 'B': ptr = va_arg(ap, void **); len = va_arg(ap, size_t *); if (ber_get_bitstring(ber, ptr, len) == -1) goto fail; ret++; break; case 'b': d = va_arg(ap, int *); if (ber_get_boolean(ber, d) == -1) goto fail; ret++; break; case 'e': e = va_arg(ap, struct ber_element **); *e = ber; ret++; continue; case 'E': i = va_arg(ap, long long *); if (ber_get_enumerated(ber, i) == -1) goto fail; ret++; break; case 'i': i = va_arg(ap, long long *); if (ber_get_integer(ber, i) == -1) goto fail; ret++; break; case 'o': o = va_arg(ap, struct ber_oid *); if (ber_get_oid(ber, o) == -1) goto fail; ret++; break; case 'S': ret++; break; case 's': s = va_arg(ap, char **); if (ber_get_string(ber, s) == -1) goto fail; ret++; break; case 't': d = va_arg(ap, int *); t = va_arg(ap, unsigned long *); *d = ber->be_class; *t = ber->be_type; ret++; continue; case 'x': ptr = va_arg(ap, void **); len = va_arg(ap, size_t *); if (ber_get_nstring(ber, ptr, len) == -1) goto fail; ret++; break; case '0': if (ber->be_encoding != BER_TYPE_NULL) goto fail; ret++; break; case '.': if (ber->be_encoding != BER_TYPE_EOC) goto fail; ret++; break; case '{': case '(': if (ber->be_encoding != BER_TYPE_SEQUENCE && ber->be_encoding != BER_TYPE_SET) goto fail; if (ber->be_sub == NULL || level >= _MAX_SEQ-1) goto fail; parent[++level] = ber; ber = ber->be_sub; ret++; continue; case '}': case ')': if (parent[level] == NULL) goto fail; ber = parent[level--]; ret++; continue; default: goto fail; } if (ber->be_next == NULL) continue; ber = ber->be_next; } va_end(ap); return (ret == n ? 0 : -1); fail: va_end(ap); return (-1); } /* * write ber elements to the socket * * params: * ber holds the socket * root fully populated element tree * * returns: * >=0 number of bytes written * -1 on failure and sets errno */ int ber_write_elements(struct ber *ber, struct ber_element *root) { size_t len; /* calculate length because only the definite form is required */ len = ber_calc_len(root); DPRINTF("write ber element of %zd bytes length\n", len); if (ber->br_wbuf != NULL && ber->br_wbuf + len > ber->br_wend) { free(ber->br_wbuf); ber->br_wbuf = NULL; } if (ber->br_wbuf == NULL) { if ((ber->br_wbuf = malloc(len)) == NULL) return -1; ber->br_wend = ber->br_wbuf + len; } /* reset write pointer */ ber->br_wptr = ber->br_wbuf; if (ber_dump_element(ber, root) == -1) return -1; /* XXX this should be moved to a different function */ if (ber->fd != -1) return write(ber->fd, ber->br_wbuf, len); return (len); } /* * read ber elements from the socket * * params: * ber holds the socket and lot more * root if NULL, build up an element tree from what we receive on * the wire. If not null, use the specified encoding for the * elements received. * * returns: * !=NULL, elements read and store in the ber_element tree * NULL, type mismatch or read error */ struct ber_element * ber_read_elements(struct ber *ber, struct ber_element *elm) { struct ber_element *root = elm; if (root == NULL) { if ((root = ber_get_element(0)) == NULL) return NULL; } DPRINTF("read ber elements, root %p\n", root); if (ber_read_element(ber, root) == -1) { /* Cleanup if root was allocated by us */ if (elm == NULL) ber_free_elements(root); return NULL; } return root; } void ber_free_elements(struct ber_element *root) { if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET)) ber_free_elements(root->be_sub); if (root->be_next) ber_free_elements(root->be_next); if (root->be_free && (root->be_encoding == BER_TYPE_OCTETSTRING || root->be_encoding == BER_TYPE_BITSTRING || root->be_encoding == BER_TYPE_OBJECT)) free(root->be_val); free(root); } size_t ber_calc_len(struct ber_element *root) { unsigned long t; size_t s; size_t size = 2; /* minimum 1 byte head and 1 byte size */ /* calculate the real length of a sequence or set */ if (root->be_sub && (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET)) root->be_len = ber_calc_len(root->be_sub); /* fix header length for extended types */ if (root->be_type > BER_TYPE_SINGLE_MAX) for (t = root->be_type; t > 0; t >>= 7) size++; if (root->be_len >= BER_TAG_MORE) for (s = root->be_len; s > 0; s >>= 8) size++; /* calculate the length of the following elements */ if (root->be_next) size += ber_calc_len(root->be_next); /* This is an empty element, do not use a minimal size */ if (root->be_type == BER_TYPE_EOC && root->be_len == 0) return (0); return (root->be_len + size); } /* * internal functions */ static int ber_dump_element(struct ber *ber, struct ber_element *root) { unsigned long long l; int i; uint8_t u; ber_dump_header(ber, root); switch (root->be_encoding) { case BER_TYPE_BOOLEAN: case BER_TYPE_INTEGER: case BER_TYPE_ENUMERATED: l = (unsigned long long)root->be_numeric; for (i = root->be_len; i > 0; i--) { u = (l >> ((i - 1) * 8)) & 0xff; ber_putc(ber, u); } break; case BER_TYPE_BITSTRING: return -1; case BER_TYPE_OCTETSTRING: case BER_TYPE_OBJECT: ber_write(ber, root->be_val, root->be_len); break; case BER_TYPE_NULL: /* no payload */ case BER_TYPE_EOC: break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: if (root->be_sub && ber_dump_element(ber, root->be_sub) == -1) return -1; break; } if (root->be_next == NULL) return 0; return ber_dump_element(ber, root->be_next); } static void ber_dump_header(struct ber *ber, struct ber_element *root) { unsigned char id = 0, t, buf[8]; unsigned long type; size_t size; /* class universal, type encoding depending on type value */ /* length encoding */ if (root->be_type <= BER_TYPE_SINGLE_MAX) { id = root->be_type | (root->be_class << BER_CLASS_SHIFT); if (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET) id |= BER_TYPE_CONSTRUCTED; ber_putc(ber, id); } else { id = BER_TAG_MASK | (root->be_class << BER_CLASS_SHIFT); if (root->be_encoding == BER_TYPE_SEQUENCE || root->be_encoding == BER_TYPE_SET) id |= BER_TYPE_CONSTRUCTED; ber_putc(ber, id); for (t = 0, type = root->be_type; type > 0; type >>= 7) buf[t++] = type & ~BER_TAG_MORE; while (t-- > 0) { if (t > 0) buf[t] |= BER_TAG_MORE; ber_putc(ber, buf[t]); } } if (root->be_len < BER_TAG_MORE) { /* short form */ ber_putc(ber, root->be_len); } else { for (t = 0, size = root->be_len; size > 0; size >>= 8) buf[t++] = size & 0xff; ber_putc(ber, t | BER_TAG_MORE); while (t > 0) ber_putc(ber, buf[--t]); } } static void ber_putc(struct ber *ber, unsigned char c) { if (ber->br_wptr + 1 <= ber->br_wend) *ber->br_wptr = c; ber->br_wptr++; } static void ber_write(struct ber *ber, void *buf, size_t len) { if (ber->br_wptr + len <= ber->br_wend) memmove(ber->br_wptr, buf, len); ber->br_wptr += len; } /* * extract a BER encoded tag. There are two types, a short and long form. */ static ssize_t get_id(struct ber *b, unsigned long *tag, int *class, int *cstruct) { unsigned char u; size_t i = 0; unsigned long t = 0; if (ber_getc(b, &u) == -1) return -1; *class = (u >> BER_CLASS_SHIFT) & BER_CLASS_MASK; *cstruct = (u & BER_TYPE_CONSTRUCTED) == BER_TYPE_CONSTRUCTED; if ((u & BER_TAG_MASK) != BER_TAG_MASK) { *tag = u & BER_TAG_MASK; return 1; } do { if (ber_getc(b, &u) == -1) return -1; t = (t << 7) | (u & ~BER_TAG_MORE); i++; } while (u & BER_TAG_MORE); if (i > sizeof(unsigned long)) { errno = ERANGE; return -1; } *tag = t; return i + 1; } /* * extract length of a ber object -- if length is unknown an error is returned. */ static ssize_t get_len(struct ber *b, ssize_t *len) { unsigned char u, n; ssize_t s, r; if (ber_getc(b, &u) == -1) return -1; if ((u & BER_TAG_MORE) == 0) { /* short form */ *len = u; return 1; } n = u & ~BER_TAG_MORE; if (sizeof(ssize_t) < n) { errno = ERANGE; return -1; } r = n + 1; for (s = 0; n > 0; n--) { if (ber_getc(b, &u) == -1) return -1; s = (s << 8) | u; } if (s < 0) { /* overflow */ errno = ERANGE; return -1; } if (s == 0) { /* invalid encoding */ errno = EINVAL; return -1; } *len = s; return r; } static ssize_t ber_read_element(struct ber *ber, struct ber_element *elm) { long long val = 0; struct ber_element *next; unsigned long type; int i, class, cstruct; ssize_t len, r, totlen = 0; unsigned char c; if ((r = get_id(ber, &type, &class, &cstruct)) == -1) return -1; DPRINTF("ber read got class %d type %lu, %s\n", class, type, cstruct ? "constructive" : "primitive"); totlen += r; if ((r = get_len(ber, &len)) == -1) return -1; DPRINTF("ber read element size %zd\n", len); totlen += r + len; elm->be_type = type; elm->be_len = len; elm->be_class = class; if (elm->be_encoding == 0) { /* try to figure out the encoding via class, type and cstruct */ if (cstruct) elm->be_encoding = BER_TYPE_SEQUENCE; else if (class == BER_CLASS_UNIVERSAL) elm->be_encoding = type; else if (ber->br_application != NULL) { /* * Ask the application to map the encoding to a * universal type. For example, a SMI IpAddress * type is defined as 4 byte OCTET STRING. */ elm->be_encoding = (*ber->br_application)(elm); } else /* last resort option */ elm->be_encoding = BER_TYPE_NULL; } switch (elm->be_encoding) { case BER_TYPE_EOC: /* End-Of-Content */ break; case BER_TYPE_BOOLEAN: case BER_TYPE_INTEGER: case BER_TYPE_ENUMERATED: if (len > (ssize_t)sizeof(long long)) return -1; for (i = 0; i < len; i++) { if (ber_getc(ber, &c) != 1) return -1; val <<= 8; val |= c; } /* sign extend if MSB is set */ if (val >> ((i - 1) * 8) & 0x80) val |= ULLONG_MAX << (i * 8); elm->be_numeric = val; break; case BER_TYPE_BITSTRING: elm->be_val = malloc(len); if (elm->be_val == NULL) return -1; elm->be_free = 1; elm->be_len = len; ber_read(ber, elm->be_val, len); break; case BER_TYPE_OCTETSTRING: case BER_TYPE_OBJECT: elm->be_val = malloc(len + 1); if (elm->be_val == NULL) return -1; elm->be_free = 1; elm->be_len = len; ber_read(ber, elm->be_val, len); ((unsigned char *)elm->be_val)[len] = '\0'; break; case BER_TYPE_NULL: /* no payload */ if (len != 0) return -1; break; case BER_TYPE_SEQUENCE: case BER_TYPE_SET: if (elm->be_sub == NULL) { if ((elm->be_sub = ber_get_element(0)) == NULL) return -1; } next = elm->be_sub; while (len > 0) { r = ber_read_element(ber, next); if (r == -1) return -1; len -= r; if (len > 0 && next->be_next == NULL) { if ((next->be_next = ber_get_element(0)) == NULL) return -1; } next = next->be_next; } break; } return totlen; } static ssize_t ber_readbuf(struct ber *b, void *buf, size_t nbytes) { size_t sz; size_t len; if (b->br_rbuf == NULL) return -1; sz = b->br_rend - b->br_rptr; len = MINIMUM(nbytes, sz); if (len == 0) { errno = ECANCELED; return (-1); /* end of buffer and parser wants more data */ } memmove(buf, b->br_rptr, len); b->br_rptr += len; return (len); } void ber_set_readbuf(struct ber *b, void *buf, size_t len) { b->br_rbuf = b->br_rptr = buf; b->br_rend = (uint8_t *)buf + len; } ssize_t ber_get_writebuf(struct ber *b, void **buf) { if (b->br_wbuf == NULL) return -1; *buf = b->br_wbuf; return (b->br_wend - b->br_wbuf); } void ber_set_application(struct ber *b, unsigned long (*cb)(struct ber_element *)) { b->br_application = cb; } void ber_free(struct ber *b) { if (b->br_wbuf != NULL) free (b->br_wbuf); } static ssize_t ber_getc(struct ber *b, unsigned char *c) { ssize_t r; /* * XXX calling read here is wrong in many ways. The most obvious one * being that we will block till data arrives. * But for now it is _good enough_ *gulp* */ if (b->fd == -1) r = ber_readbuf(b, c, 1); else r = read(b->fd, c, 1); return r; } static ssize_t ber_read(struct ber *ber, void *buf, size_t len) { unsigned char *b = buf; ssize_t r, remain = len; /* * XXX calling read here is wrong in many ways. The most obvious one * being that we will block till data arrives. * But for now it is _good enough_ *gulp* */ while (remain > 0) { if (ber->fd == -1) r = ber_readbuf(ber, b, remain); else r = read(ber->fd, b, remain); if (r == -1) { if (errno == EINTR || errno == EAGAIN) continue; return -1; } if (r == 0) return (b - (unsigned char *)buf); b += r; remain -= r; } return (b - (unsigned char *)buf); } OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/ber.h000066400000000000000000000112511355240505200222470ustar00rootroot00000000000000/* * Copyright (c) 2007 Reyk Floeter * Copyright (c) 2006, 2007 Claudio Jeker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ struct ber_element { struct ber_element *be_next; unsigned long be_type; unsigned long be_encoding; size_t be_len; int be_free; uint8_t be_class; union { struct ber_element *bv_sub; void *bv_val; long long bv_numeric; } be_union; #define be_sub be_union.bv_sub #define be_val be_union.bv_val #define be_numeric be_union.bv_numeric }; struct ber { int fd; unsigned char *br_wbuf; unsigned char *br_wptr; unsigned char *br_wend; unsigned char *br_rbuf; unsigned char *br_rptr; unsigned char *br_rend; unsigned long (*br_application)(struct ber_element *); }; /* well-known ber_element types */ #define BER_TYPE_DEFAULT ((unsigned long)-1) #define BER_TYPE_EOC 0 #define BER_TYPE_BOOLEAN 1 #define BER_TYPE_INTEGER 2 #define BER_TYPE_BITSTRING 3 #define BER_TYPE_OCTETSTRING 4 #define BER_TYPE_NULL 5 #define BER_TYPE_OBJECT 6 #define BER_TYPE_ENUMERATED 10 #define BER_TYPE_SEQUENCE 16 #define BER_TYPE_SET 17 /* ber classes */ #define BER_CLASS_UNIVERSAL 0x0 #define BER_CLASS_UNIV BER_CLASS_UNIVERSAL #define BER_CLASS_APPLICATION 0x1 #define BER_CLASS_APP BER_CLASS_APPLICATION #define BER_CLASS_CONTEXT 0x2 #define BER_CLASS_PRIVATE 0x3 #define BER_CLASS_MASK 0x3 /* common definitions */ #define BER_MIN_OID_LEN 2 /* OBJECT */ #define BER_MAX_OID_LEN 32 /* OBJECT */ struct ber_oid { uint32_t bo_id[BER_MAX_OID_LEN + 1]; size_t bo_n; }; __BEGIN_DECLS struct ber_element *ber_get_element(unsigned long); void ber_set_header(struct ber_element *, int, unsigned long); void ber_link_elements(struct ber_element *, struct ber_element *); struct ber_element *ber_unlink_elements(struct ber_element *); void ber_replace_elements(struct ber_element *, struct ber_element *); struct ber_element *ber_add_sequence(struct ber_element *); struct ber_element *ber_add_set(struct ber_element *); struct ber_element *ber_add_integer(struct ber_element *, long long); int ber_get_integer(struct ber_element *, long long *); struct ber_element *ber_add_enumerated(struct ber_element *, long long); int ber_get_enumerated(struct ber_element *, long long *); struct ber_element *ber_add_boolean(struct ber_element *, int); int ber_get_boolean(struct ber_element *, int *); struct ber_element *ber_add_string(struct ber_element *, const char *); struct ber_element *ber_add_nstring(struct ber_element *, const char *, size_t); int ber_get_string(struct ber_element *, char **); int ber_get_nstring(struct ber_element *, void **, size_t *); struct ber_element *ber_add_bitstring(struct ber_element *, const void *, size_t); int ber_get_bitstring(struct ber_element *, void **, size_t *); struct ber_element *ber_add_null(struct ber_element *); int ber_get_null(struct ber_element *); struct ber_element *ber_add_eoc(struct ber_element *); int ber_get_eoc(struct ber_element *); struct ber_element *ber_add_oid(struct ber_element *, struct ber_oid *); struct ber_element *ber_add_noid(struct ber_element *, struct ber_oid *, int); struct ber_element *ber_add_oidstring(struct ber_element *, const char *); int ber_get_oid(struct ber_element *, struct ber_oid *); size_t ber_oid2ber(struct ber_oid *, uint8_t *, size_t); int ber_string2oid(const char *, struct ber_oid *); struct ber_element *ber_printf_elements(struct ber_element *, char *, ...); int ber_scanf_elements(struct ber_element *, char *, ...); ssize_t ber_get_writebuf(struct ber *, void **); int ber_write_elements(struct ber *, struct ber_element *); void ber_set_readbuf(struct ber *, void *, size_t); struct ber_element *ber_read_elements(struct ber *, struct ber_element *); void ber_free_elements(struct ber_element *); size_t ber_calc_len(struct ber_element *); void ber_set_application(struct ber *, unsigned long (*)(struct ber_element *)); void ber_free(struct ber *); __END_DECLS OpenSMTPD-extras-6.6.0/extras/tables/table-ldap/table_ldap.c000066400000000000000000000300241355240505200235600ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include "aldap.h" #define MAX_LDAP_IDENTIFIER 32 #define MAX_LDAP_URL 256 #define MAX_LDAP_USERNAME 256 #define MAX_LDAP_PASSWORD 256 #define MAX_LDAP_BASELEN 128 #define MAX_LDAP_FILTERLEN 1024 #define MAX_LDAP_FIELDLEN 128 enum { LDAP_ALIAS = 0, LDAP_DOMAIN, LDAP_CREDENTIALS, LDAP_NETADDR, LDAP_USERINFO, LDAP_SOURCE, LDAP_MAILADDR, LDAP_ADDRNAME, LDAP_MAX }; #define MAX_ATTRS 6 struct query { char *filter; char *attrs[MAX_ATTRS]; int attrn; }; static int ldap_run_query(int type, const char *, char *, size_t); static char *config, *url, *username, *password, *basedn; static struct aldap *aldap; static struct query queries[LDAP_MAX]; static int table_ldap_update(void) { return 1; } static int table_ldap_fetch(int service, struct dict *params, char *dst, size_t sz) { return -1; } static struct aldap * ldap_connect(const char *addr) { struct aldap_url lu; struct addrinfo hints, *res0, *res; char *buf; int error, fd = -1; if ((buf = strdup(addr)) == NULL) return NULL; /* aldap_parse_url frees buf on success */ if (aldap_parse_url(buf, &lu) != 1) { log_warnx("warn: ldap_parse_url fail"); free(buf); return NULL; } memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; /* DUMMY */ error = getaddrinfo(lu.host, NULL, &hints, &res0); if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) return NULL; if (error) { log_warnx("warn: could not parse \"%s\": %s", lu.host, gai_strerror(error)); return NULL; } for (res = res0; res; res = res->ai_next) { if (res->ai_family != AF_INET && res->ai_family != AF_INET6) continue; fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd == -1) continue; if (res->ai_family == AF_INET) { struct sockaddr_in sin4 = *(struct sockaddr_in *)res->ai_addr; sin4.sin_port = htons(lu.port); if (connect(fd, (struct sockaddr *)&sin4, res->ai_addrlen) == 0) return aldap_init(fd); } else if (res->ai_family == AF_INET6) { struct sockaddr_in6 sin6 = *(struct sockaddr_in6 *)res->ai_addr; sin6.sin6_port = htons(lu.port); if (connect(fd, (struct sockaddr *)&sin6, res->ai_addrlen) == 0) return aldap_init(fd); } close(fd); fd = -1; } return NULL; } static int read_value(char **store, const char *key, const char *value) { log_debug("debug: reading key \"%s\" -> \"%s\"", key, value); if (*store) { log_warnx("warn: duplicate key %s", key); return 0; } if ((*store = strdup(value)) == NULL) { log_warn("warn: strdup"); return 0; } return 1; } static int ldap_parse_attributes(struct query *query, const char *key, const char *line, size_t expect) { char buffer[1024]; char *p; size_t m, n; log_debug("debug: parsing attribute \"%s\" (%zu) -> \"%s\"", key, expect, line); if (strlcpy(buffer, line, sizeof buffer) >= sizeof buffer) return 0; m = 1; for (p = buffer; *p; ++p) { if (*p == ',') { *p = 0; m++; } } if (expect != m) return 0; p = buffer; for (n = 0; n < expect; ++n) query->attrs[n] = NULL; for (n = 0; n < m; ++n) { query->attrs[n] = strdup(p); if (query->attrs[n] == NULL) { log_warnx("warn: strdup"); return 0; /* XXX cleanup */ } p += strlen(p) + 1; query->attrn++; } return 1; } static int ldap_config(void) { size_t sz = 0; ssize_t flen; FILE *fp; char *key, *value, *buf = NULL; if ((fp = fopen(config, "r")) == NULL) { log_warn("warn: \"%s\"", config); return 0; } while ((flen = getline(&buf, &sz, fp)) != -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); continue; } if (!strcmp(key, "url")) read_value(&url, key, value); else if (!strcmp(key, "username")) read_value(&username, key, value); else if (!strcmp(key, "password")) read_value(&password, key, value); else if (!strcmp(key, "basedn")) read_value(&basedn, key, value); else if (!strcmp(key, "alias_filter")) read_value(&queries[LDAP_ALIAS].filter, key, value); else if (!strcmp(key, "alias_attributes")) { ldap_parse_attributes(&queries[LDAP_ALIAS], key, value, 1); } else if (!strcmp(key, "credentials_filter")) read_value(&queries[LDAP_CREDENTIALS].filter, key, value); else if (!strcmp(key, "credentials_attributes")) { ldap_parse_attributes(&queries[LDAP_CREDENTIALS], key, value, 2); } else if (!strcmp(key, "domain_filter")) read_value(&queries[LDAP_DOMAIN].filter, key, value); else if (!strcmp(key, "domain_attributes")) { ldap_parse_attributes(&queries[LDAP_DOMAIN], key, value, 1); } else if (!strcmp(key, "userinfo_filter")) read_value(&queries[LDAP_USERINFO].filter, key, value); else if (!strcmp(key, "userinfo_attributes")) { ldap_parse_attributes(&queries[LDAP_USERINFO], key, value, 3); } else if (!strcmp(key, "mailaddr_filter")) read_value(&queries[LDAP_MAILADDR].filter, key, value); else if (!strcmp(key, "mailaddr_attributes")) { ldap_parse_attributes(&queries[LDAP_MAILADDR], key, value, 1); } else log_warnx("warn: bogus entry \"%s\"", key); } free(buf); fclose(fp); return 1; } static int ldap_open(void) { struct aldap_message *amsg = NULL; if (aldap) { aldap_close(aldap); log_info("info: table-ldap: closed previous connection"); } aldap = ldap_connect(url); if (aldap == NULL) { log_warnx("warn: ldap_connect error"); goto err; } if (aldap_bind(aldap, username, password) == -1) { log_warnx("warn: aldap_bind error"); goto err; } if ((amsg = aldap_parse(aldap)) == NULL) { log_warnx("warn: aldap_parse"); goto err; } switch (aldap_get_resultcode(amsg)) { case LDAP_SUCCESS: log_debug("debug: ldap server accepted credentials"); break; case LDAP_INVALID_CREDENTIALS: log_warnx("warn: ldap server refused credentials"); goto err; default: log_warnx("warn: failed to bind, result #%d", aldap_get_resultcode(amsg)); goto err; } if (amsg) aldap_freemsg(amsg); return 1; err: if (aldap) aldap_close(aldap); if (amsg) aldap_freemsg(amsg); return 0; } static int table_ldap_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { int ret; switch(service) { case K_ALIAS: case K_DOMAIN: case K_CREDENTIALS: case K_USERINFO: case K_MAILADDR: if ((ret = ldap_run_query(service, key, dst, sz)) > 0) { return ret; } log_debug("debug: table-ldap: reconnecting"); if (!(ret = ldap_open())) { log_warnx("warn: table-ldap: failed to connect"); return ret; } return ldap_run_query(service, key, dst, sz); default: return -1; } } static int ldap_query(const char *filter, char **attributes, char ***outp, size_t n) { struct aldap_message *m = NULL; struct aldap_page_control *pg = NULL; int ret, found; size_t i; char basedn__[MAX_LDAP_BASELEN]; char filter__[MAX_LDAP_FILTERLEN]; if (strlcpy(basedn__, basedn, sizeof basedn__) >= sizeof basedn__) return -1; if (strlcpy(filter__, filter, sizeof filter__) >= sizeof filter__) return -1; found = 0; do { if ((ret = aldap_search(aldap, basedn__, LDAP_SCOPE_SUBTREE, filter__, NULL, 0, 0, 0, pg)) == -1) { log_debug("ret=%d", ret); return -1; } if (pg != NULL) { aldap_freepage(pg); pg = NULL; } while ((m = aldap_parse(aldap)) != NULL) { if (aldap->msgid != m->msgid) goto error; if (m->message_type == LDAP_RES_SEARCH_RESULT) { if (m->page != NULL && m->page->cookie_len) pg = m->page; aldap_freemsg(m); m = NULL; break; } if (m->message_type != LDAP_RES_SEARCH_ENTRY) goto error; found = 1; for (i = 0; i < n; ++i) if (aldap_match_attr(m, attributes[i], &outp[i]) != 1) goto error; aldap_freemsg(m); m = NULL; } } while (pg != NULL); ret = found ? 1 : 0; goto end; error: ret = -1; end: if (m) aldap_freemsg(m); log_debug("debug: table_ldap: ldap_query: filter=%s, ret=%d", filter, ret); return ret; } static int ldap_run_query(int type, const char *key, char *dst, size_t sz) { struct query *q; char **res[4], filter[MAX_LDAP_FILTERLEN]; int ret, i; switch (type) { case K_ALIAS: q = &queries[LDAP_ALIAS]; break; case K_DOMAIN: q = &queries[LDAP_DOMAIN]; break; case K_CREDENTIALS: q = &queries[LDAP_CREDENTIALS]; break; case K_NETADDR: q = &queries[LDAP_NETADDR]; break; case K_USERINFO: q = &queries[LDAP_USERINFO]; break; case K_SOURCE: q = &queries[LDAP_SOURCE]; break; case K_MAILADDR: q = &queries[LDAP_MAILADDR]; break; case K_ADDRNAME: q = &queries[LDAP_ADDRNAME]; break; default: return -1; } if (snprintf(filter, sizeof(filter), q->filter, key) >= (int)sizeof(filter)) { log_warnx("warn: filter too large"); return -1; } memset(res, 0, sizeof(res)); ret = ldap_query(filter, q->attrs, res, q->attrn); if (ret <= 0 || dst == NULL) goto end; switch (type) { case K_ALIAS: memset(dst, 0, sz); for (i = 0; res[0][i]; i++) { if (i && strlcat(dst, ", ", sz) >= sz) { ret = -1; break; } if (strlcat(dst, res[0][i], sz) >= sz) { ret = -1; break; } } break; case K_DOMAIN: case K_MAILADDR: if (strlcpy(dst, res[0][0], sz) >= sz) ret = -1; break; case K_CREDENTIALS: if (snprintf(dst, sz, "%s:%s", res[0][0], res[1][0]) >= (int)sz) ret = -1; break; case K_USERINFO: if (snprintf(dst, sz, "%s:%s:%s", res[0][0], res[1][0], res[2][0]) >= (int)sz) ret = -1; break; default: log_warnx("warn: unsupported lookup kind"); ret = -1; } if (ret == -1) log_warnx("warn: could not format result"); end: for (i = 0; i < q->attrn; ++i) if (res[i]) aldap_free_attr(res[i]); return ret; } static int table_ldap_check(int service, struct dict *params, const char *key) { int ret; switch(service) { case K_ALIAS: case K_DOMAIN: case K_CREDENTIALS: case K_USERINFO: case K_MAILADDR: if ((ret = ldap_run_query(service, key, NULL, 0)) >= 0) { return ret; } log_debug("debug: table-ldap: reconnecting"); if (!(ret = ldap_open())) { log_warnx("warn: table-ldap: failed to connect"); } return ret; default: return -1; } } int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); config = argv[0]; if (!ldap_config()) fatalx("could not parse config"); log_debug("debug: done reading config"); if (!ldap_open()) fatalx("failed to connect"); log_debug("debug: connected"); table_api_on_update(table_ldap_update); table_api_on_check(table_ldap_check); table_api_on_lookup(table_ldap_lookup); table_api_on_fetch(table_ldap_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-mysql/000077500000000000000000000000001355240505200215535ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-mysql/Makefile.am000066400000000000000000000003051355240505200236050ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-mysql table_mysql_SOURCES = $(SRCS) table_mysql_SOURCES += table_mysql.c LDADD += -lmysqlclient OpenSMTPD-extras-6.6.0/extras/tables/table-mysql/mysql.conf000066400000000000000000000021101355240505200235610ustar00rootroot00000000000000# # Sample MySQL or MariaDB configuration file # # MySQL or MariaDB example schema # #CREATE TABLE domains ( # id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, # domain VARCHAR(255) NOT NULL DEFAULT '' #); #CREATE TABLE virtuals ( # id INTEGER AUTO_INCREMENT PRIMARY KEY, # email VARCHAR(255) NOT NULL DEFAULT '', # destination VARCHAR(255) NOT NULL DEFAULT '' #); #CREATE TABLE credentials ( # id INTEGER AUTO_INCREMENT PRIMARY KEY, # email VARCHAR(255) NOT NULL DEFAULT '', # password VARCHAR(255) NOT NULL DEFAULT '' #); # Database definition # host localhost username username password password database databasename # Alias lookup query # # rows >= 0 # fields == 1 (email VARCHAR) # query_alias SELECT destination FROM virtuals WHERE email=? # Domain lookup query # # rows == 1 # fields == 1 (domain VARCHAR) # query_domain SELECT domain FROM domains WHERE domain=?; # Credentials lookup query # # rows == 1 # fields == 2 (email VARCHAR, password VARCHAR) # query_credentials SELECT email, password FROM credentials WHERE email=?; OpenSMTPD-extras-6.6.0/extras/tables/table-mysql/table-mysql.5000066400000000000000000000164401355240505200241000ustar00rootroot00000000000000.\" .\" Copyright (c) 2013 Eric Faurot .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: July 4 2016 $ .Dt TABLE_MYSQL 5 .Os .Sh NAME .Nm table_mysql .Nd format description for smtpd MySQL or MariaDB tables .Sh DESCRIPTION This manual page documents the file format of MySQL or MariaDB tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh MYSQL TABLE A mysql table allows the storing of usernames, passwords, aliases, and domains in a format that is shareable across various machines that support .Xr mysql 1 . .Pp The table is used by .Xr smtpd 8 when authenticating a user, when user information such as user-id and/or home directory is required for a delivery, when a domain lookup may be required, and/or when looking for an alias. .Pp A MySQL table consists of one or more .Xr mysql 1 databases with one or more tables. .Pp If the table is used for authentication, the password should be encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Sh MYSQL TABLE CONFIG FILE The following configuration options are available: .Pp .Bl -tag -width Ds .It Xo .Ic host .Ar hostname .Xc This is the host running MySQL or MariaDB. For example: .Bd -literal -offset indent host db.example.com .Ed .Pp .It Xo .Ic username .Ar username .Xc The username required to talk to the MySQL or MariaDB database. For example: .Bd -literal -offset indent username maildba .Ed .Pp .It Xo .Ic password .Ar password .Xc The password required to talk to the MySQL or MariaDB database. For example: .Bd -literal -offset indent password OpenSMTPDRules! .Ed .Pp .It Xo .Ic database .Ar databse .Xc The name of the MySQL or MariaDB database. For example: .Bd -literal -offset indent databse opensmtpdb .Ed .Pp .It Xo .Ic query_alias .Ar SQL statement .Xc This is used to provide a query to look up aliases. The question mark is replaced with the appropriate data. For alias it is the left hand side of the SMTP address. This expects one VARCHAR to be returned with the user name the alias resolves to. .Pp .It Xo .Ic query_credentials .Ar SQL statement .Xc This is used to provide a query for looking up user credentials. The question mark is replaced with the appropriate data. For credentials it is the left hand side of the SMTP address. The query expects that there are two VARCHARS returned, one with a user name and one with a password in .Xr crypt 3 format. .Pp .It Xo .Ic query_domain .Ar SQL statement .Xc This is used to provide a query for looking up a domain. The question mark is replaced with the appropriate data. For the domain it would be the right hand side of the SMTP address. This expects one VARCHAR to be returned with a matching domain name. .Pp .It Xo .Ic query_mailaddrmap .Ar SQL statement .Xc This is used to provide a query to look up senders. The question mark is replaced with the appropriate data. This expects one VARCHAR to be returned with the address the sender is allowed to send mails from. .El A generic SQL statement would be something like: .Bd -literal -offset indent query_ SELECT value FROM table WHERE key=?; .Ed .Sh EXAMPLES .Ss GENERIC EXAMPLE Example based on the OpenSMTPD FAQ: Building a Mail Server The filtering part is excluded in this example. The configuration below is for a medium-size mail server which handles multiple domains with multiple virtual users and is based on several assumptions. One is that a single system user named vmail is used for all virtual users. This user needs to be created: .Bd -literal # useradd -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail # mkdir /var/vmail # chown vmail:vmail /var/vmail .Ed .Ic Pa MySQL schema .Bd -literal -compact CREATE TABLE domains ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, domain VARCHAR(255) NOT NULL DEFAULT '' ); CREATE TABLE virtuals ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL DEFAULT '', destination VARCHAR(255) NOT NULL DEFAULT '' ); CREATE TABLE credentials ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL DEFAULT '', password VARCHAR(255) NOT NULL DEFAULT '' ); INSERT INTO domains VALUES (1, "example.com"); INSERT INTO domains VALUES (2, "example.net"); INSERT INTO domains VALUES (3, "example.org"); INSERT INTO virtuals VALUES (1, "abuse@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (2, "postmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (3, "webmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (4, "bob@example.com", "vmail"); INSERT INTO virtuals VALUES (5, "abuse@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (6, "postmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (7, "webmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (8, "alice@example.net", "vmail"); INSERT INTO credentials VALUES (1, "bob@example.com", "$2b$08$ANGFKBL.BnDLL0bUl7I6aumTCLRJSQluSQLuueWRG.xceworWrUIu"); INSERT INTO credentials VALUES (2, "alice@example.net", "$2b$08$AkHdB37kaj2NEoTcISHSYOCEBA5vyW1RcD8H1HG.XX0P/G1KIYwii"); .Ed .Ic Pa /etc/mail/mysql.conf .Bd -literal -compact host db.example.com username maildba password OpenSMTPDRules! database opensmtpdb query_alias SELECT destination FROM virtuals WHERE email=?; query_credentials SELECT email, password FROM credentials WHERE email=?; query_domain SELECT domain FROM domains WHERE domain=?; .Ed .Ic Pa /etc/mail/smtpd.conf .Bd -literal -compact table domains mysql:/etc/mail/mysql.conf table virtuals mysql:/etc/mail/mysql.conf table credentials mysql:/etc/mail/mysql.conf listen on egress port 25 tls pki mail.example.com listen on egress port 587 tls-require pki mail.example.com auth accept from any for domain virtual deliver to mbox .Ed .Ss MOVING FROM POSTFIX (& POSTFIXADMIN) .Ic Pa /etc/mail/mysql.conf .Bd -literal -compact host db.example.com username postfix password PostfixOutOpenSMTPDin database postfix query_alias SELECT destination FROM alias WHERE email=?; query_credentials SELECT username, password FROM mailbox WHERE username=?; query_domain SELECT domain FROM domain WHERE domain=?; .Ed The rest of the config remains the same. .Sh FILES .Bl -tag -width "/etc/mail/mysql.conf" -compact .It Pa /etc/mail/mysql.conf Default .Xr table-mysql 8 configuration file. .El .Sh TODO Documenting the following query options: .Bd -literal -offset indent -compact .Ic query_netaddr .Ic query_userinfo .Ic query_source .Ic query_mailaddr .Ic query_addrname .Ed .Sh SEE ALSO .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 , .Xr encrypt 1 , .Xr crypt 3 OpenSMTPD-extras-6.6.0/extras/tables/table-mysql/table_mysql.c000066400000000000000000000314061355240505200242370ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include enum { SQL_ALIAS = 0, SQL_DOMAIN, SQL_CREDENTIALS, SQL_NETADDR, SQL_USERINFO, SQL_SOURCE, SQL_MAILADDR, SQL_ADDRNAME, SQL_MAILADDRMAP, SQL_MAX }; struct config { struct dict conf; MYSQL *db; MYSQL_STMT *statements[SQL_MAX]; MYSQL_STMT *stmt_fetch_source; struct dict sources; void *source_iter; size_t source_refresh; size_t source_ncall; int source_expire; time_t source_update; }; static MYSQL_STMT *table_mysql_query(const char *, int); static void config_free(struct config *); #define SQL_MAX_RESULT 5 #define DEFAULT_EXPIRE 60 #define DEFAULT_REFRESH 1000 static MYSQL_BIND results[SQL_MAX_RESULT]; static char results_buffer[SQL_MAX_RESULT][LINE_MAX]; static char *conffile; static struct config *config; static MYSQL_STMT * table_mysql_prepare_stmt(MYSQL *_db, const char *query, unsigned long nparams, unsigned int nfields) { MYSQL_STMT *stmt; if ((stmt = mysql_stmt_init(_db)) == NULL) { log_warnx("warn: mysql_stmt_init: %s", mysql_error(_db)); goto end; } if (mysql_stmt_prepare(stmt, query, strlen(query))) { log_warnx("warn: mysql_stmt_prepare: %s", mysql_stmt_error(stmt)); goto end; } if (mysql_stmt_param_count(stmt) != nparams) { log_warnx("warn: wrong number of params for %s", query); goto end; } if (mysql_stmt_field_count(stmt) != nfields) { log_warnx("warn: wrong number of columns in resultset"); goto end; } if (mysql_stmt_bind_result(stmt, results)) { log_warnx("warn: mysql_stmt_bind_results: %s", mysql_stmt_error(stmt)); goto end; } return stmt; end: if (stmt) mysql_stmt_close(stmt); return NULL; } static struct config * config_load(const char *path) { struct config *conf; FILE *fp; size_t sz = 0; ssize_t flen; char *key, *value, *buf = NULL; const char *e; long long ll; if ((conf = calloc(1, sizeof(*conf))) == NULL) { log_warn("warn: calloc"); return NULL; } dict_init(&conf->conf); dict_init(&conf->sources); conf->source_refresh = DEFAULT_REFRESH; conf->source_expire = DEFAULT_EXPIRE; if ((fp = fopen(path, "r")) == NULL) { log_warn("warn: \"%s\"", path); goto end; } while ((flen = getline(&buf, &sz, fp)) != -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); goto end; } if (dict_check(&conf->conf, key)) { log_warnx("warn: duplicate key %s", key); goto end; } value = strdup(value); if (value == NULL) { log_warn("warn: strdup"); goto end; } dict_set(&conf->conf, key, value); } if ((value = dict_get(&conf->conf, "fetch_source_expire"))) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for fetch_source_expire: %s", e); goto end; } conf->source_expire = ll; } if ((value = dict_get(&conf->conf, "fetch_source_refresh"))) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for fetch_source_refresh: %s", e); goto end; } conf->source_refresh = ll; } free(buf); fclose(fp); return conf; end: free(buf); fclose(fp); config_free(conf); return NULL; } static void config_reset(struct config *conf) { size_t i; for (i = 0; i < SQL_MAX; i++) { if (conf->statements[i]) { mysql_stmt_close(conf->statements[i]); conf->statements[i] = NULL; } } if (conf->stmt_fetch_source) { mysql_stmt_close(conf->stmt_fetch_source); conf->stmt_fetch_source = NULL; } if (conf->db) { mysql_close(conf->db); conf->db = NULL; } } static int config_connect(struct config *conf) { static const struct { const char *name; int cols; } qspec[SQL_MAX] = { { "query_alias", 1 }, { "query_domain", 1 }, { "query_credentials", 2 }, { "query_netaddr", 1 }, { "query_userinfo", 3 }, { "query_source", 1 }, { "query_mailaddr", 1 }, { "query_addrname", 1 }, { "query_mailaddrmap", 1 }, }; my_bool reconn; size_t i; char *host, *username, *password, *database, *q; log_debug("debug: (re)connecting"); /* disconnect first, if needed */ config_reset(conf); host = dict_get(&conf->conf, "host"); username = dict_get(&conf->conf, "username"); database = dict_get(&conf->conf, "database"); password = dict_get(&conf->conf, "password"); conf->db = mysql_init(NULL); if (conf->db == NULL) { log_warnx("warn: mysql_init failed"); goto end; } reconn = 1; if (mysql_options(conf->db, MYSQL_OPT_RECONNECT, &reconn) != 0) { log_warnx("warn: mysql_options: %s", mysql_error(conf->db)); goto end; } if (!mysql_real_connect(conf->db, host, username, password, database, 0, NULL, 0)) { log_warnx("warn: mysql_real_connect: %s", mysql_error(conf->db)); goto end; } for (i = 0; i < SQL_MAX; i++) { q = dict_get(&conf->conf, qspec[i].name); if (q && (conf->statements[i] = table_mysql_prepare_stmt( conf->db, q, 1, qspec[i].cols)) == NULL) goto end; } q = dict_get(&conf->conf, "fetch_source"); if (q && (conf->stmt_fetch_source = table_mysql_prepare_stmt(conf->db, q, 0, 1)) == NULL) goto end; log_debug("debug: connected"); return 1; end: config_reset(conf); return 0; } static void config_free(struct config *conf) { void *value; config_reset(conf); while (dict_poproot(&conf->conf, &value)) free(value); while (dict_poproot(&conf->sources, NULL)) ; free(conf); } static int table_mysql_update(void) { struct config *c; if ((c = config_load(conffile)) == NULL) return 0; if (config_connect(c) == 0) { config_free(c); return 0; } config_free(config); config = c; return 1; } static MYSQL_STMT * table_mysql_query(const char *key, int service) { MYSQL_STMT *stmt; MYSQL_BIND param[1]; unsigned long keylen; char buffer[LINE_MAX]; int i; retry: stmt = NULL; for (i = 0; i < SQL_MAX; i++) { if (service == 1 << i) { stmt = config->statements[i]; break; } } if (stmt == NULL) return NULL; if (strlcpy(buffer, key, sizeof(buffer)) >= sizeof(buffer)) { log_warnx("warn: key too long: \"%s\"", key); return NULL; } keylen = strlen(key); param[0].buffer_type = MYSQL_TYPE_STRING; param[0].buffer = buffer; param[0].buffer_length = sizeof(buffer); param[0].is_null = 0; param[0].length = &keylen; if (mysql_stmt_bind_param(stmt, param)) { log_warnx("warn: mysql_stmt_bind_param: %s", mysql_stmt_error(stmt)); return NULL; } if (mysql_stmt_execute(stmt)) { if (mysql_stmt_errno(stmt) == CR_SERVER_LOST || mysql_stmt_errno(stmt) == CR_SERVER_GONE_ERROR || mysql_stmt_errno(stmt) == CR_COMMANDS_OUT_OF_SYNC) { log_warnx("warn: trying to reconnect after error: %s", mysql_stmt_error(stmt)); if (config_connect(config)) goto retry; return NULL; } log_warnx("warn: mysql_stmt_execute: %s", mysql_stmt_error(stmt)); return NULL; } return stmt; } static int table_mysql_check(int service, struct dict *params, const char *key) { MYSQL_STMT *stmt; int r, s; if (config->db == NULL && config_connect(config) == 0) return -1; stmt = table_mysql_query(key, service); if (stmt == NULL) return -1; r = -1; s = mysql_stmt_fetch(stmt); if (s == 0) r = 1; else if (s == MYSQL_NO_DATA) r = 0; else log_warnx("warn: mysql_stmt_fetch: %s", mysql_stmt_error(stmt)); if (mysql_stmt_free_result(stmt)) log_warnx("warn: mysql_stmt_free_result: %s", mysql_stmt_error(stmt)); return r; } static int table_mysql_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { MYSQL_STMT *stmt; int r, s; if (config->db == NULL && config_connect(config) == 0) return -1; if ((stmt = table_mysql_query(key, service)) == NULL) return -1; if ((s = mysql_stmt_fetch(stmt)) == MYSQL_NO_DATA) { r = 0; goto end; } if (s != 0) { r = -1; log_warnx("warn: mysql_stmt_fetch: %s", mysql_stmt_error(stmt)); goto end; } r = 1; switch(service) { case K_ALIAS: case K_MAILADDRMAP: memset(dst, 0, sz); do { if (dst[0] && strlcat(dst, ", ", sz) >= sz) { log_warnx("warn: result too large"); r = -1; break; } if (strlcat(dst, results_buffer[0], sz) >= sz) { log_warnx("warn: result too large"); r = -1; break; } s = mysql_stmt_fetch(stmt); } while (s == 0); if (s && s != MYSQL_NO_DATA) { log_warnx("warn: mysql_stmt_fetch: %s", mysql_stmt_error(stmt)); r = -1; } break; case K_CREDENTIALS: if (snprintf(dst, sz, "%s:%s", results_buffer[0], results_buffer[1]) > (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_USERINFO: if (snprintf(dst, sz, "%s:%s:%s", results_buffer[0], results_buffer[1], results_buffer[2]) > (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: if (strlcpy(dst, results_buffer[0], sz) >= sz) { log_warnx("warn: result too large"); r = -1; } break; default: log_warnx("warn: unknown service %d", service); r = -1; } end: if (mysql_stmt_free_result(stmt)) log_warnx("warn: mysql_stmt_free_result: %s", mysql_stmt_error(stmt)); return r; } static int table_mysql_fetch(int service, struct dict *params, char *dst, size_t sz) { MYSQL_STMT *stmt; const char *k; int s; if (config->db == NULL && config_connect(config) == 0) return -1; retry: if (service != K_SOURCE) return -1; if ((stmt = config->stmt_fetch_source) == NULL) return -1; if (config->source_ncall < config->source_refresh && time(NULL) - config->source_update < config->source_expire) goto fetch; if (mysql_stmt_execute(stmt)) { if (mysql_stmt_errno(stmt) == CR_SERVER_LOST || mysql_stmt_errno(stmt) == CR_SERVER_GONE_ERROR || mysql_stmt_errno(stmt) == CR_COMMANDS_OUT_OF_SYNC) { log_warnx("warn: trying to reconnect after error: %s", mysql_stmt_error(stmt)); if (config_connect(config)) goto retry; return -1; } log_warnx("warn: mysql_stmt_execute: %s", mysql_stmt_error(stmt)); return -1; } config->source_iter = NULL; while (dict_poproot(&config->sources, NULL)) ; while ((s = mysql_stmt_fetch(stmt)) == 0) dict_set(&config->sources, results_buffer[0], NULL); if (s && s != MYSQL_NO_DATA) log_warnx("warn: mysql_stmt_fetch: %s", mysql_stmt_error(stmt)); if (mysql_stmt_free_result(stmt)) log_warnx("warn: mysql_stmt_free_result: %s", mysql_stmt_error(stmt)); config->source_update = time(NULL); config->source_ncall = 0; fetch: config->source_ncall += 1; if (!dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) { config->source_iter = NULL; if (!dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) return 0; } if (strlcpy(dst, k, sz) >= sz) return -1; return 1; } int main(int argc, char **argv) { int ch, i; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); conffile = argv[0]; for (i = 0; i < SQL_MAX_RESULT; i++) { results[i].buffer_type = MYSQL_TYPE_STRING; results[i].buffer = results_buffer[i]; results[i].buffer_length = LINE_MAX; results[i].is_null = 0; } if ((config = config_load(conffile)) == NULL) fatalx("error parsing config file"); if (config_connect(config) == 0) fatalx("could not connect"); table_api_on_update(table_mysql_update); table_api_on_check(table_mysql_check); table_api_on_lookup(table_mysql_lookup); table_api_on_fetch(table_mysql_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-passwd/000077500000000000000000000000001355240505200217075ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-passwd/Makefile.am000066400000000000000000000003161355240505200237430ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-passwd table_passwd_SOURCES = $(SRCS) table_passwd_SOURCES += table_passwd.c man_MANS = table-passwd.5 OpenSMTPD-extras-6.6.0/extras/tables/table-passwd/table-passwd.5000066400000000000000000000054271355240505200243730ustar00rootroot00000000000000.\" .\" Copyright (c) 2014 Gilles Chehade .\" Copyright (c) 2016 Joerg Jung .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" .Dd $Mdocdate: February 4 2014 $ .Dt TABLE_PASSWD 5 .Os .Sh NAME .Nm table_passwd .Nd format description for smtpd passwd tables .Sh DESCRIPTION This manual page documents the file format of "passwd" tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh PASSWD TABLE A "passwd" table stores information regarding local users. The information is encoded using the traditional .Xr passwd 5 format and allows the sharing of a user database across different software supporting this format. .Pp The table is used by .Xr smtpd 8 when authenticating a user or when user information such as user-id or home directory is required for a delivery. .Pp A "passwd" table consists of a flat file containing the user entries, each one on a line by itself, with fields separated by a colon: .Bd -literal -offset indent gilles:*:1000:1000:Gilles:/home/gilles:/sbin/nologin eric:*:1001:1001:Eric:/home/eric:/sbin/nologin chl:*:1002:1002:Charles:/home/chl:/sbin/nologin .Ed .Pp Besides the first username field and depending on the table type, fields are optional and might be empty. The gecos and the shell field are not used and ignored. .Pp If the table is used for authentication, the second field should contain a password encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Pp If the table is used for user information, user-id, group-id, and home directory fields are required. .Pp In favor of supporting shared authentication with the Dovecot Passwd-file format, extra fields after the last shell field are allowed (and ignored). .Sh SEE ALSO .Xr passwd 5 , .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 .Sh HISTORY The first version of .Nm was written in 2013. .Sh AUTHORS .An -nosplit .Nm was intially written by .An Gilles Chehade Aq Mt gilles@poolp.org and further improved by .An Joerg Jung Aq Mt jung@openbsd.org . OpenSMTPD-extras-6.6.0/extras/tables/table-passwd/table_passwd.c000066400000000000000000000116251355240505200245300ustar00rootroot00000000000000/* * Copyright (c) 2013 Gilles Chehade * Copyright (c) 2016 Joerg Jung * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include static char *config; static struct dict *passwd; static int parse_passwd_entry(int service, struct passwd *pw, char *buf) { const char *e; char *p; /* username */ if (!(pw->pw_name = strsep(&buf, ":")) || !strlen(pw->pw_name)) return 0; /* password */ if (!(pw->pw_passwd = strsep(&buf, ":")) || (service == K_CREDENTIALS && !strlen(pw->pw_passwd))) return 0; /* uid */ if (!(p = strsep(&buf, ":"))) return 0; pw->pw_uid = strtonum(p, 0, UID_MAX, &e); if (service == K_USERINFO && (!strlen(p) || e)) return 0; /* gid */ if (!(p = strsep(&buf, ":"))) return 0; pw->pw_gid = strtonum(p, 0, GID_MAX, &e); if (service == K_USERINFO && (!strlen(p) || e)) return 0; /* gecos */ if (!(pw->pw_gecos = strsep(&buf, ":"))) return 0; /* home */ if (!(pw->pw_dir = strsep(&buf, ":")) || (service == K_USERINFO && !strlen(pw->pw_dir))) return 0; /* shell */ pw->pw_shell = strsep(&buf, ":"); /* * explicitly allow further extra fields to support * shared authentication with Dovecot Passwd-file format */ return 1; } static int table_passwd_update(void) { FILE *fp; char *buf = NULL, tmp[LINE_MAX], *skip, *p; size_t sz = 0; ssize_t len; struct passwd pw; struct dict *npasswd; /* parse configuration */ if ((fp = fopen(config, "r")) == NULL) { log_warn("warn: \"%s\"", config); return 0; } if ((npasswd = calloc(1, sizeof(*passwd))) == NULL) goto err; dict_init(npasswd); while ((len = getline(&buf, &sz, fp)) != -1) { if (buf[len - 1] == '\n') buf[len - 1] = '\0'; /* skip commented entries */ for (skip = buf; *skip; ++skip) { if (*skip == '#') { *skip = '\0'; break; } } /* skip empty lines */ if (strlen(buf) == 0) continue; if (strlcpy(tmp, buf, sizeof(tmp)) >= sizeof(tmp)) { log_warnx("warn: line too long"); goto err; } if (!parse_passwd_entry(K_ANY, &pw, tmp)) { log_warnx("warn: invalid entry"); goto err; } dict_set(npasswd, pw.pw_name, xstrdup(buf, "update")); } free(buf); fclose(fp); /* swap passwd table and release old one*/ if (passwd) while (dict_poproot(passwd, (void**)&p)) free(p); passwd = npasswd; return 1; err: free(buf); fclose(fp); /* release passwd table */ if (npasswd) { while (dict_poproot(npasswd, (void**)&p)) free(p); free(npasswd); } return 0; } static int table_passwd_check(int service, struct dict *params, const char *key) { return -1; } static int table_passwd_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { struct passwd pw; char *line; char tmp[LINE_MAX]; if ((line = dict_get(passwd, key)) == NULL) return 0; (void)strlcpy(tmp, line, sizeof(tmp)); if (!parse_passwd_entry(service, &pw, tmp)) { log_warnx("warn: invalid entry"); return -1; } switch (service) { case K_CREDENTIALS: if (snprintf(dst, sz, "%s:%s", pw.pw_name, pw.pw_passwd) >= (ssize_t)sz) { log_warnx("warn: result too large"); return -1; } break; case K_USERINFO: if (snprintf(dst, sz, "%d:%d:%s", pw.pw_uid, pw.pw_gid, pw.pw_dir) >= (ssize_t)sz) { log_warnx("warn: result too large"); return -1; } break; default: log_warnx("warn: unknown service %d", service); return -1; } return 1; } static int table_passwd_fetch(int service, struct dict *params, char *dst, size_t sz) { return -1; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); config = argv[0]; if (table_passwd_update() == 0) fatalx("error parsing config file"); table_api_on_update(table_passwd_update); table_api_on_check(table_passwd_check); table_api_on_lookup(table_passwd_lookup); table_api_on_fetch(table_passwd_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-postgres/000077500000000000000000000000001355240505200222545ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-postgres/Makefile.am000066400000000000000000000003541355240505200243120ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-postgres table_postgres_SOURCES = $(SRCS) table_postgres_SOURCES += table_postgres.c CFLAGS += -I/usr/include/postgresql LDADD += -lpq OpenSMTPD-extras-6.6.0/extras/tables/table-postgres/table-postgres.5000066400000000000000000000161311355240505200252770ustar00rootroot00000000000000.\" .\" Copyright (c) 2013 Eric Faurot .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd $Mdocdate: September 30 2016 $ .Dt TABLE_POSTGRESQL 5 .Os .Sh NAME .Nm table_postgresql .Nd format description for smtpd PostgreSQL tables .Sh DESCRIPTION This manual page documents the file format of PostgreSQL tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh POSTGRESQL TABLE A postgresql table allows the storing of usernames, passwords, aliases, and domains in a format that is shareable across various machines that support .Xr postgres 1 . .Pp The table is used by .Xr smtpd 8 when authenticating a user, when user information such as user-id and/or home directory is required for a delivery, when a domain lookup may be required, and/or when looking for an alias. .Pp A PostgreSQL table consists of one or more .Xr postgresql 1 databases with one or more tables. .Pp If the table is used for authentication, the password should be encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Sh POSTGRESQL TABLE CONFIG FILE The following configuration options are available: .Pp .Bl -tag -width Ds .It Xo .Ic conninfo .Cm host Ns = Ns Ar 'host' .Cm user Ns = Ns Ar 'user' .Cm password Ns = Ns Ar 'password' .Cm dbname Ns = Ns Ar 'dbname' .Xc Connection info needed to connect to the PostgreSQL database. For example: .Bd -literal -offset indent .Ed .Ic conninfo .Cm host Ns = Ns Ar 'db.example.com' .Cm user Ns = Ns Ar 'maildba' .Cm password Ns = Ns Ar 'OpenSMTPDRules!' .Cm dbname Ns = Ns Ar 'opensmtpdb' .Xc .Pp .It Xo .Ic query_alias .Ar SQL statement .Xc This is used to provide a query to look up aliases. The question mark is replaced with the appropriate data. For alias it is the left hand side of the SMTP address. This expects one VARCHAR to be returned with the user name the alias resolves to. .Pp .It Xo .Ic query_credentials .Ar SQL statement .Xc This is used to provide a query for looking up user credentials. The question mark is replaced with the appropriate data. For credentials it is the left hand side of the SMTP address. The query expects that there are two VARCHARS returned, one with a user name and one with a password in .Xr crypt 3 format. .Pp .It Xo .Ic query_domain .Ar SQL statement .Xc This is used to provide a query for looking up a domain. The question mark is replaced with the appropriate data. For the domain it would be the right hand side of the SMTP address. This expects one VARCHAR to be returned with a matching domain name. .Pp .It Xo .Ic query_mailaddrmap .Ar SQL statement .Xc This is used to provide a query to look up senders. The question mark is replaced with the appropriate data. This expects one VARCHAR to be returned with the address the sender is allowed to send mails from. .El A generic SQL statement would be something like: .Bd -literal -offset indent query_ SELECT value FROM table WHERE key=$1; .Ed .Sh EXAMPLES .Ss GENERIC EXAMPLE Example based on the OpenSMTPD FAQ: Building a Mail Server The filtering part is excluded in this example. The configuration below is for a medium-size mail server which handles multiple domains with multiple virtual users and is based on several assumptions. One is that a single system user named vmail is used for all virtual users. This user needs to be created: .Bd -literal # useradd -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail # mkdir /var/vmail # chown vmail:vmail /var/vmail .Ed .Ic Pa PostgreSQL schema .Bd -literal -compact CREATE TABLE domains ( id SERIAL, domain VARCHAR(255) NOT NULL DEFAULT '' ); CREATE TABLE virtuals ( id SERIAL, email VARCHAR(255) NOT NULL DEFAULT '', destination VARCHAR(255) NOT NULL DEFAULT '' ); CREATE TABLE credentials ( id SERIAL, email VARCHAR(255) NOT NULL DEFAULT '', password VARCHAR(255) NOT NULL DEFAULT '' ); INSERT INTO domains VALUES (1, "example.com"); INSERT INTO domains VALUES (2, "example.net"); INSERT INTO domains VALUES (3, "example.org"); INSERT INTO virtuals VALUES (1, "abuse@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (2, "postmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (3, "webmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (4, "bob@example.com", "vmail"); INSERT INTO virtuals VALUES (5, "abuse@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (6, "postmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (7, "webmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (8, "alice@example.net", "vmail"); INSERT INTO credentials VALUES (1, "bob@example.com", "$2b$08$ANGFKBL.BnDLL0bUl7I6aumTCLRJSQluSQLuueWRG.xceworWrUIu"); INSERT INTO credentials VALUES (2, "alice@example.net", "$2b$08$AkHdB37kaj2NEoTcISHSYOCEBA5vyW1RcD8H1HG.XX0P/G1KIYwii"); .Ed .Ic Pa /etc/mail/postgresql.conf .Bd -literal -compact conninfo host='db.example.com' user='maildba' password='OpenSMTPDRules!' dbname='opensmtpdb' query_alias SELECT destination FROM virtuals WHERE email=$1; query_credentials SELECT email, password FROM credentials WHERE email=$1; query_domain SELECT domain FROM domains WHERE domain=$1; .Ed .Ic Pa /etc/mail/smtpd.conf .Bd -literal -compact table domains postgres:/etc/mail/postgres.conf table virtuals postgres:/etc/mail/postgres.conf table credentials postgres:/etc/mail/postgres.conf listen on egress port 25 tls pki mail.example.com listen on egress port 587 tls-require pki mail.example.com auth accept from any for domain virtual deliver to mbox .Ed .Ss MOVING FROM POSTFIX (& POSTFIXADMIN) .Ic Pa /etc/mail/postgres.conf .Bd -literal -compact conninfo host='db.example.com' user='postfix' password='PostfixOutOpenSMTPDin' dbname='postfix' query_alias SELECT destination FROM alias WHERE email=$1; query_credentials SELECT username, password FROM mailbox WHERE username=$1; query_domain SELECT domain FROM domain WHERE domain=$1; .Ed The rest of the config remains the same. .Sh FILES .Bl -tag -width "/etc/mail/postgres.conf" -compact .It Pa /etc/mail/postgres.conf Default .Xr table-postgresql 8 configuration file. .El .Sh TODO Documenting the following query options: .Bd -literal -offset indent -compact .Ic query_netaddr .Ic query_userinfo .Ic query_source .Ic query_mailaddr .Ic query_addrname .Ed .Sh SEE ALSO .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 , .Xr encrypt 1 , .Xr crypt 3 OpenSMTPD-extras-6.6.0/extras/tables/table-postgres/table_postgres.c000066400000000000000000000253371355240505200254470ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include enum { SQL_ALIAS = 0, SQL_DOMAIN, SQL_CREDENTIALS, SQL_NETADDR, SQL_USERINFO, SQL_SOURCE, SQL_MAILADDR, SQL_ADDRNAME, SQL_MAILADDRMAP, SQL_MAX }; struct config { struct dict conf; PGconn *db; char *statements[SQL_MAX]; char *stmt_fetch_source; struct dict sources; void *source_iter; size_t source_refresh; size_t source_ncall; int source_expire; time_t source_update; }; #define DEFAULT_EXPIRE 60 #define DEFAULT_REFRESH 1000 static char *conffile; static struct config *config; static char * table_postgres_prepare_stmt(PGconn *_db, const char *query, int nparams, unsigned int nfields) { static unsigned int n = 0; PGresult *res; char *stmt; if (asprintf(&stmt, "stmt%u", n++) == -1) { log_warn("warn: asprintf"); return NULL; } res = PQprepare(_db, stmt, query, nparams, NULL); if (PQresultStatus(res) != PGRES_COMMAND_OK) { log_warnx("warn: PQprepare: %s", PQerrorMessage(_db)); free(stmt); stmt = NULL; } PQclear(res); return stmt; } static void config_reset(struct config *conf) { size_t i; for (i = 0; i < SQL_MAX; i++) if (conf->statements[i]) { free(conf->statements[i]); conf->statements[i] = NULL; } if (conf->stmt_fetch_source) { free(conf->stmt_fetch_source); conf->stmt_fetch_source = NULL; } if (conf->db) { PQfinish(conf->db); conf->db = NULL; } } static void config_free(struct config *conf) { void *value; config_reset(conf); while (dict_poproot(&conf->conf, &value)) free(value); while (dict_poproot(&conf->sources, NULL)) ; free(conf); } static struct config * config_load(const char *path) { struct config *conf; FILE *fp; size_t sz = 0; ssize_t flen; char *key, *value, *buf = NULL; const char *e; long long ll; if ((conf = calloc(1, sizeof(*conf))) == NULL) { log_warn("warn: calloc"); return NULL; } dict_init(&conf->conf); dict_init(&conf->sources); conf->source_refresh = DEFAULT_REFRESH; conf->source_expire = DEFAULT_EXPIRE; if ((fp = fopen(path, "r")) == NULL) { log_warn("warn: \"%s\"", path); goto end; } while ((flen = getline(&buf, &sz, fp)) != -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); goto end; } if (dict_check(&conf->conf, key)) { log_warnx("warn: duplicate key %s", key); goto end; } value = strdup(value); if (value == NULL) { log_warn("warn: strdup"); goto end; } dict_set(&conf->conf, key, value); } if ((value = dict_get(&conf->conf, "fetch_source_expire"))) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for fetch_source_expire: %s", e); goto end; } conf->source_expire = ll; } if ((value = dict_get(&conf->conf, "fetch_source_refresh"))) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for fetch_source_refresh: %s", e); goto end; } conf->source_refresh = ll; } free(buf); fclose(fp); return conf; end: free(buf); fclose(fp); config_free(conf); return NULL; } static int config_connect(struct config *conf) { static const struct { const char *name; int cols; } qspec[SQL_MAX] = { { "query_alias", 1 }, { "query_domain", 1 }, { "query_credentials", 2 }, { "query_netaddr", 1 }, { "query_userinfo", 3 }, { "query_source", 1 }, { "query_mailaddr", 1 }, { "query_addrname", 1 }, { "query_mailaddrmap", 1 }, }; size_t i; char *conninfo, *q; log_debug("debug: (re)connecting"); /* Disconnect first, if needed */ config_reset(conf); conninfo = dict_get(&conf->conf, "conninfo"); if (conninfo == NULL) { log_warnx("warn: missing \"conninfo\" configuration directive"); goto end; } conf->db = PQconnectdb(conninfo); if (conf->db == NULL) { log_warnx("warn: PQconnectdb return NULL"); goto end; } if (PQstatus(conf->db) != CONNECTION_OK) { log_warnx("warn: PQconnectdb: %s", PQerrorMessage(conf->db)); goto end; } for (i = 0; i < SQL_MAX; i++) { q = dict_get(&conf->conf, qspec[i].name); if (q && (conf->statements[i] = table_postgres_prepare_stmt( conf->db, q, 1, qspec[i].cols)) == NULL) goto end; } q = dict_get(&conf->conf, "fetch_source"); if (q && (conf->stmt_fetch_source = table_postgres_prepare_stmt(conf->db, q, 0, 1)) == NULL) goto end; log_debug("debug: connected"); return 1; end: config_reset(conf); return 0; } static int table_postgres_update(void) { struct config *c; if ((c = config_load(conffile)) == NULL) return 0; if (config_connect(c) == 0) { config_free(c); return 0; } config_free(config); config = c; return 1; } static PGresult * table_postgres_query(const char *key, int service) { PGresult *res; const char *errfld; char *stmt; int i; retry: stmt = NULL; for (i = 0; i < SQL_MAX; i++) { if (service == 1 << i) { stmt = config->statements[i]; break; } } if (stmt == NULL) return NULL; res = PQexecPrepared(config->db, stmt, 1, &key, NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { errfld = PQresultErrorField(res, PG_DIAG_SQLSTATE); /* PQresultErrorField can return NULL if the connection to the server suddenly closed (e.g. server restart) */ if (errfld == NULL || (errfld[0] == '0' && errfld[1] == '8')) { log_warnx("warn: table-postgres: trying to reconnect after error: %s", PQerrorMessage(config->db)); PQclear(res); if (config_connect(config)) goto retry; return NULL; } log_warnx("warn: PQexecPrepared: %s", PQerrorMessage(config->db)); PQclear(res); return NULL; } return res; } static int table_postgres_check(int service, struct dict *params, const char *key) { PGresult *res; int r; if (config->db == NULL && config_connect(config) == 0) return -1; res = table_postgres_query(key, service); if (res == NULL) return -1; r = (PQntuples(res) == 0) ? 0 : 1; PQclear(res); return r; } static int table_postgres_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { PGresult *res; int r, i; if (config->db == NULL && config_connect(config) == 0) return -1; res = table_postgres_query(key, service); if (res == NULL) return -1; if (PQntuples(res) == 0) { r = 0; goto end; } r = 1; switch(service) { case K_ALIAS: case K_MAILADDRMAP: memset(dst, 0, sz); for (i = 0; i < PQntuples(res); i++) { if (dst[0] && strlcat(dst, ", ", sz) >= sz) { log_warnx("warn: result too large"); r = -1; break; } if (strlcat(dst, PQgetvalue(res, i, 0), sz) >= sz) { log_warnx("warn: esult too large"); r = -1; break; } } break; case K_CREDENTIALS: if (snprintf(dst, sz, "%s:%s", PQgetvalue(res, 0, 0), PQgetvalue(res, 0, 1)) > (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_USERINFO: if (snprintf(dst, sz, "%s:%s:%s", PQgetvalue(res, 0, 0), PQgetvalue(res, 0, 1), PQgetvalue(res, 0, 2)) > (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: if (strlcpy(dst, PQgetvalue(res, 0, 0), sz) >= sz) { log_warnx("warn: result too large"); r = -1; } break; default: log_warnx("warn: unknown service %d", service); r = -1; } end: PQclear(res); return r; } static int table_postgres_fetch(int service, struct dict *params, char *dst, size_t sz) { char *stmt; PGresult *res; const char *k, *errfld; int i; if (config->db == NULL && config_connect(config) == 0) return -1; retry: if (service != K_SOURCE) return -1; if ((stmt = config->stmt_fetch_source) == NULL) return -1; if (config->source_ncall < config->source_refresh && time(NULL) - config->source_update < config->source_expire) goto fetch; res = PQexecPrepared(config->db, stmt, 0, NULL, NULL, NULL, 0); if (PQresultStatus(res) != PGRES_TUPLES_OK) { errfld = PQresultErrorField(res, PG_DIAG_SQLSTATE); if (errfld[0] == '0' && errfld[1] == '8') { log_warnx("warn: trying to reconnect after error: %s", PQerrorMessage(config->db)); PQclear(res); if (config_connect(config)) goto retry; return -1; } log_warnx("warn: PQexecPrepared: %s", PQerrorMessage(config->db)); PQclear(res); return -1; } config->source_iter = NULL; while (dict_poproot(&config->sources, NULL)) ; for (i = 0; i < PQntuples(res); i++) dict_set(&config->sources, PQgetvalue(res, i, 0), NULL); PQclear(res); config->source_update = time(NULL); config->source_ncall = 0; fetch: config->source_ncall += 1; if (!dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) { config->source_iter = NULL; if (!dict_iter(&config->sources, &config->source_iter, &k, (void **)NULL)) return 0; } if (strlcpy(dst, k, sz) >= sz) return -1; return 1; } int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); conffile = argv[0]; if ((config = config_load(conffile)) == NULL) fatalx("error parsing config file"); if (config_connect(config) == 0) fatalx("could not connect"); table_api_on_update(table_postgres_update); table_api_on_check(table_postgres_check); table_api_on_lookup(table_postgres_lookup); table_api_on_fetch(table_postgres_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-python/000077500000000000000000000000001355240505200217275ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-python/Makefile.am000066400000000000000000000003521355240505200237630ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-python table_python_SOURCES = $(SRCS) table_python_SOURCES += table_python.c CFLAGS+= ${PYTHON_CPPFLAGS} LDFLAGS = ${PYTHON_LDFLAGS} OpenSMTPD-extras-6.6.0/extras/tables/table-python/table_python.c000066400000000000000000000144201355240505200245640ustar00rootroot00000000000000/* * Copyright (c) 2014 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include /* _GNU_SOURCE is not properly protected in Python.h ... */ #undef _GNU_SOURCE #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include static PyObject *py_on_update, *py_on_lookup, *py_on_check, *py_on_fetch; static void check_err(const char *name) { if (PyErr_Occurred()) { PyErr_Print(); fatalx("error in %s handler", name); } } static PyObject * dispatch(PyObject *handler, PyObject *args) { PyObject *ret; ret = PyObject_CallObject(handler, args); Py_DECREF(args); if (PyErr_Occurred()) { PyErr_Print(); fatalx("exception"); } return ret; } static PyObject * dict_to_py(struct dict *dict) { PyObject *o, *s; const char *key; char *value; void *iter; o = PyDict_New(); iter = NULL; while (dict_iter(dict, &iter, &key, (void **)&value)) { s = PyString_FromString(value); if (s == NULL) goto fail; if (PyDict_SetItemString(o, key, s) == -1) goto fail; } return o; fail: if (o) Py_DECREF(o); if (s) Py_DECREF(s); return NULL; } static int table_python_update(void) { PyObject *ret; if (py_on_update == NULL) return 0; if ((ret = dispatch(py_on_update, PyTuple_New(0))) == NULL) { log_warnx("warn: update failed"); return -1; } Py_DECREF(ret); check_err("init"); return 1; } static int table_python_check(int service, struct dict *params, const char *key) { PyObject *dict, *args, *ret; int r; if (py_on_check == NULL) return -1; if ((dict = dict_to_py(params)) == NULL) return -1; if ((args = Py_BuildValue("iOs", service, dict, key)) == NULL) { Py_DECREF(dict); return -1; } if ((ret = dispatch(py_on_check, args)) == NULL) return -1; r = PyObject_IsTrue(ret); Py_DECREF(ret); return r; } static int table_python_lookup(int service, struct dict *params, const char *key, char *buf, size_t sz) { PyObject *dict, *args, *ret; char *s; int r; if (py_on_lookup == NULL) return -1; if ((dict = dict_to_py(params)) == NULL) return -1; if ((args = Py_BuildValue("iOs", service, dict, key)) == NULL) { Py_DECREF(dict); return -1; } if ((ret = dispatch(py_on_lookup, args)) == NULL) return -1; if (ret == Py_None) r = 0; else if (PyString_CheckExact(ret)) { r = 1; s = PyString_AS_STRING(ret); if (strlcpy(buf, s, sz) >= sz) { log_warnx("warn: lookup: result too long"); r = -1; } } else { log_warnx("warn: lookup: invalid object returned"); r = -1; } Py_DECREF(ret); return r; } static int table_python_fetch(int service, struct dict *params, char *buf, size_t sz) { PyObject *dict, *args, *ret; char *s; int r; if (py_on_fetch == NULL) return -1; dict = dict_to_py(params); if (dict == NULL) return -1; args = Py_BuildValue("iO", service, dict); if (args == NULL) { Py_DECREF(dict); return -1; } ret = dispatch(py_on_fetch, args); if (ret == NULL) return -1; if (ret == Py_None) r = 0; else if (PyString_CheckExact(ret)) { r = 1; s = PyString_AS_STRING(ret); if (strlcpy(buf, s, sz) >= sz) { log_warnx("warn: lookup: result too long"); r = -1; } } else { log_warnx("warn: lookup: invalid object returned"); r = -1; } Py_DECREF(ret); return r; } static char * loadfile(const char * path) { FILE *f; off_t oz; size_t sz; char *buf; if ((f = fopen(path, "r")) == NULL) fatal("fopen"); if (fseek(f, 0, SEEK_END) == -1) fatal("fseek"); oz = ftello(f); if (fseek(f, 0, SEEK_SET) == -1) fatal("fseek"); if ((size_t)oz >= SIZE_MAX) fatalx("too big"); sz = oz; buf = xmalloc(sz + 1, "loadfile"); if (fread(buf, 1, sz, f) != sz) fatal("fread"); buf[sz] = '\0'; fclose(f); return buf; } static PyMethodDef py_methods[] = { { NULL, NULL, 0, NULL } }; int main(int argc, char **argv) { int ch; char *path, *buf; PyObject *self, *code, *module; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc == 0) fatalx("missing path"); path = argv[0]; Py_Initialize(); self = Py_InitModule("table", py_methods); PyModule_AddIntConstant(self, "K_NONE", K_NONE); PyModule_AddIntConstant(self, "K_ALIAS", K_ALIAS); PyModule_AddIntConstant(self, "K_DOMAIN", K_DOMAIN); PyModule_AddIntConstant(self, "K_CREDENTIALS", K_CREDENTIALS); PyModule_AddIntConstant(self, "K_NETADDR", K_NETADDR); PyModule_AddIntConstant(self, "K_USERINFO", K_USERINFO); PyModule_AddIntConstant(self, "K_SOURCE", K_SOURCE); PyModule_AddIntConstant(self, "K_MAILADDR", K_MAILADDR); PyModule_AddIntConstant(self, "K_ADDRNAME", K_ADDRNAME); buf = loadfile(path); code = Py_CompileString(buf, path, Py_file_input); free(buf); if (code == NULL) { PyErr_Print(); fatalx("failed to compile %s", path); } module = PyImport_ExecCodeModuleEx("mytable", code, path); if (module == NULL) { PyErr_Print(); fatalx("failed to install module %s", path); } log_debug("debug: starting..."); py_on_update = PyObject_GetAttrString(module, "table_update"); py_on_check = PyObject_GetAttrString(module, "table_check"); py_on_lookup = PyObject_GetAttrString(module, "table_lookup"); py_on_fetch = PyObject_GetAttrString(module, "table_fetch"); table_api_on_update(table_python_update); table_api_on_check(table_python_check); table_api_on_lookup(table_python_lookup); table_api_on_fetch(table_python_fetch); table_api_dispatch(); log_debug("debug: exiting"); Py_Finalize(); return 1; } OpenSMTPD-extras-6.6.0/extras/tables/table-redis/000077500000000000000000000000001355240505200215145ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-redis/Makefile.am000066400000000000000000000004431355240505200235510ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-redis table_redis_SOURCES = $(SRCS) table_redis_SOURCES += table_redis.c man_MANS = table-redis.5 CFLAGS += ${REDIS_CPPFLAGS} LDFLAGS += ${REDIS_LDFLAGS} OpenSMTPD-extras-6.6.0/extras/tables/table-redis/table-redis.5000066400000000000000000000122031355240505200237730ustar00rootroot00000000000000.\" Copyright (c) 2015 Emmanuel Vadot .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" .Dd $Mdocdate: May 14 2015 $ .Dt TABLE_REDIS 5 .Os .Sh NAME .Nm table_redis .Nd format description for smtpd redis tables .Sh DESCRIPTION This manual page documents the file format of redis tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh REDIS TABLE A Redis table allows the storing of usernames, passwords, aliases, and domains in a redis server. .Pp The table is used by .Xr smtpd 8 when authenticating a user, when user information such as user-id and/or home directory is required for a delivery, when a domain lookup may be required, and/or when looking for an alias. .Pp A Redis table consists of one Redis Databases with one or more keys. .Pp If the table is used for authentication, the password should be encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Sh REDIS TABLE CONFIG FILE .Cd master .Dl This is the IP of the master redis server. .Dl To connect via an unix socket use unix:/path/to/sock .Dl The default is 127.0.0.1 .Cd master_port .Dl This is the port used to connect to the master redis server. .Dl The default is 6379 .Cd slave .Dl This is the IP of the slave redis server, if any. .Dl To connect via an unix socket use unix:/path/to/sock .Cd slave_port .Dl This is the port used to connect to the slave redis server if any. .Cd database .Dl The database number to use. .Dl The default is 0. .Cd password .Dl The password to use to authenticate to the redis server if any. .Cd query_domain .Dl This is used to provide a query for a domain query call. All the '%s' are replaced .Dl with the appropriate data, in this case it would be the right hand side of the SMTP address. .Dl This expects one string to be returned with a matching domain name. .Cd query_userinfo .Dl This is used to provide a query for looking up user information. .Dl All the '%s' are replaced with the appropriate data, in this case it .Dl would be the left hand side of the SMTP address. .Dl This expects three fields to be returned an int containing a UID, an int containing a GID .Dl and a string containing the home directory for the user. .Cd query_credentials .Dl This is used to provide a query for looking up credentials. All the '%s' are replaced .Dl with the appropriate data, in this case it would be the left hand side of the SMTP address. .Dl the query expects that there are two strings returned one with a .Dl user name one with a password in encrypted format. .Cd query_alias .Dl This is used to provide a query to look up aliases. All the '%s' are replaced .Dl with the appropriate data, in this case it would be the left hand side of the SMTP address. .Dl This expects one string to be returned with the user name the alias resolves to. .Dl If the query returns an array, all the data will be concatenated into one string with ',' as a separator .Cd query_mailaddr .Dl This is used to provide a query to check if a mail address exists. .Dl All the '%s' are replaced with the appropriate data, in this case it would be the SMTP address. .Dl This expects an integer as a reply, 0 = false and 1 = true .Pp .Sh EXAMPLES Due to the nature of redis, multiple schemas can be used. Those provided here a known to work. .Pp .Cd domain .Dl # redis-cli sadd domains example.net .Dl in the redis table configuration file : .Dl query_domain SISMEMBER domains %s .Cd userinfo .Dl Hash works well for users .Dl # redis-cli HSET user:foo uid 1001 .Dl # redis-cli HSET user:foo gid 1001 .Dl # redis-cli HSET user:foo maildir "/mail/foo" .Dl in the redis table configuration file : .Dl query_userinfo HMGET user:%s uid gid maildir .Cd credentials .Dl We can extend the hash for our user to put credential in it .Dl # redis-cli HSET user:foo login foo .Dl # redis-cli HSET user:foo passwd encrypted_password .Dl in the redis table configuration file : .Dl query_credentials HMGET user:%s login passwd .Cd alias .Dl Using redis sorted list : .Dl # redis-cli LPUSH aliases:foo@example.net foo .Dl # redis-cli LPUSH aliases:bar@example.net foo .Dl in the redis table configuration file : .Dl query_alias LRANGE aliases:%s 0 -1 .Cd mailaddr .Dl # redis-cli sadd mailaddr foo@example.net .Dl in the redis table configuration file : .Dl query_mailaddr SISMEMBER mailaddr %s .Sh SEE ALSO .Xr encrypt 1 , .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 OpenSMTPD-extras-6.6.0/extras/tables/table-redis/table_redis.c000066400000000000000000000260551355240505200241450ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * Copyright (c) 2014 Michael Neumann * Copyright (c) 2015 Emmanuel Vadot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include enum { REDIS_ALIAS = 0, REDIS_DOMAIN, REDIS_CREDENTIALS, REDIS_NETADDR, REDIS_USERINFO, REDIS_SOURCE, REDIS_MAILADDR, REDIS_ADDRNAME, REDIS_MAX }; struct config { struct dict conf; redisContext *master; redisContext *slave; char *queries[REDIS_MAX]; }; static void config_free(struct config *); static char *conffile; static struct config *config; static struct config * config_load(const char *path) { struct config *config; FILE *fp; size_t sz = 0; ssize_t flen; char *key, *value, *buf = NULL; if ((config = calloc(1, sizeof(*config))) == NULL) { log_warn("warn: calloc"); return NULL; } dict_init(&config->conf); if ((fp = fopen(path, "r")) == NULL) { log_warn("warn: \"%s\"", path); goto end; } while ((flen = getline(&buf, &sz, fp)) == -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); goto end; } if (dict_check(&config->conf, key)) { log_warnx("warn: duplicate key %s", key); goto end; } if ((value = strdup(value)) == NULL) { log_warn("warn: strdup"); goto end; } dict_set(&config->conf, key, value); } free(buf); fclose(fp); return config; end: free(buf); fclose(fp); config_free(config); return NULL; } static void config_reset(struct config *config) { size_t i; for (i = 0; i < REDIS_MAX; i++) if (config->queries[i]) { free(config->queries[i]); config->queries[i] = NULL; } if (config->master) { redisFree(config->master); config->master = NULL; } } static int config_connect(struct config *config) { static const struct { const char *name; const char *default_query; } qspec[REDIS_MAX] = { { "query_alias", "GET alias:%s" }, { "query_domain", "GET domain:%s" }, { "query_credentials", "GET credentials:%s" }, { "query_netaddr", "GET netaddr:%s" }, { "query_userinfo", "GET userinfo:%s" }, { "query_source", "GET source:%s" }, { "query_mailaddr", "GET mailaddr:%s" }, { "query_addrname", "GET addrname:%s" }, }; size_t i; char *master = "127.0.0.1"; int master_port = 6379; char *slave = "NULL"; int slave_port = 6380; char *password = NULL; int database = 0; char *q; char *value; const char *e; long long ll; redisReply *res = NULL; log_debug("debug: (re)connecting"); /* disconnect first, if needed */ config_reset(config); if ((value = dict_get(&config->conf, "master"))) master = value; if ((value = dict_get(&config->conf, "slave"))) slave = value; if ((value = dict_get(&config->conf, "master_port"))) { e = NULL; ll = strtonum(value, 0, 65535, &e); if (e) { log_warnx("warn: bad value for master_port: %s", e); goto end; } master_port = ll; } if ((value = dict_get(&config->conf, "slave_port"))) { e = NULL; ll = strtonum(value, 0, 65535, &e); if (e) { log_warnx("warn: bad value for slave_port: %s", e); goto end; } slave_port = ll; } if ((value = dict_get(&config->conf, "password"))) password = value; if ((value = dict_get(&config->conf, "database"))) { e = NULL; ll = strtonum(value, 0, 256, &e); if (e) { log_warnx("warn: bad value for database: %s", e); goto end; } database = ll; } if (!strncmp("unix:", master, 5)) { log_debug("debug: connect via unix socket %s", master + 5); config->master = redisConnectUnix(master + 5); } else { log_debug("debug: connect to master via tcp at %s:%d", master, master_port); config->master = redisConnect(master, master_port); } if (config->master == NULL) { log_warnx("warn: can't create redis context for master"); goto end; } if (!config->master->err) { log_debug("debug: connected to master"); if (password) { res = redisCommand(config->master, "AUTH %s", password); if (res->type == REDIS_REPLY_ERROR) { log_warnx("warn: authentication on master failed"); goto end; } freeReplyObject(res); } if (database != 0) { res = redisCommand(config->master, "SELECT %d", database); if (res->type != REDIS_REPLY_STATUS) { log_warnx("warn: database selection on master failed"); goto end; } freeReplyObject(res); } } if (slave) { if (!strncmp("unix:", slave, 5)) { log_debug("debug: connect to slave via unix socket %s", slave + 5); config->slave = redisConnectUnix(slave + 5); } else { log_debug("debug: connect to slave via tcp at %s:%d", slave, slave_port); config->slave = redisConnect(slave, slave_port); } if (config->slave == NULL) { log_warnx("warn: can't create redis context for slave"); goto end; } if (!config->slave->err) { if (password) { res = redisCommand(config->slave, "AUTH %s", password); if (res->type == REDIS_REPLY_ERROR) { log_warnx("warn: authentication on slave failed"); goto end; } freeReplyObject(res); } if (database != 0) { res = redisCommand(config->slave, "SELECT %d", database); if (res->type != REDIS_REPLY_STATUS) { log_warnx("warn: database selection on slave failed"); goto end; } freeReplyObject(res); } } } for (i = 0; i < REDIS_MAX; i++) { q = dict_get(&config->conf, qspec[i].name); if (q) config->queries[i] = strdup(q); else config->queries[i] = strdup(qspec[i].default_query); if (config->queries[i] == NULL) { log_warn("warn: strdup"); goto end; } } if (config->master->err && config->slave->err) { log_warnx("warn: redisConnect for master and slave failed"); goto end; } log_debug("debug: connected"); return 1; end: if (res) freeReplyObject(res); config_reset(config); return 0; } static void config_free(struct config *config) { void *value; config_reset(config); while (dict_poproot(&config->conf, &value)) free(value); free(config); } static int table_redis_update(void) { struct config *c; if ((c = config_load(conffile)) == NULL) return 0; if (config_connect(c) == 0) { config_free(c); return 0; } config_free(config); config = c; return 1; } static redisReply * table_redis_query(const char *key, int service) { redisReply *res; char *query = NULL; int i; int retry_times; retry_times = 3; retry: --retry_times; if (retry_times < 0) { log_warnx("warn: giving up: too many retries"); return NULL; } for(i = 0; i < REDIS_MAX; i++) if (service == 1 << i) { query = config->queries[i]; break; } if (query == NULL) return NULL; if (!config->master->err) { log_debug("debug: running query \"%s\" on master", query); res = redisCommand(config->master, query, key); } else if (!config->slave->err) { log_debug("debug: running query \"%s\" on slave", query); res = redisCommand(config->slave, query, key); } else return NULL; if (res == NULL) { log_warnx("warn: redisCommand: %s", config->master->errstr); if (config_connect(config)) goto retry; return NULL; } return res; } static int table_redis_check(int service, struct dict *params, const char *key) { int r; redisReply *reply; if (config->master == NULL && config_connect(config) == 0) return -1; reply = table_redis_query(key, service); if (reply == NULL) return -1; r = 0; switch (reply->type) { case REDIS_REPLY_INTEGER: r = reply->integer; break; case REDIS_REPLY_STRING: case REDIS_REPLY_ARRAY: r = 1; break; case REDIS_REPLY_NIL: r = 0; break; case REDIS_REPLY_STATUS: case REDIS_REPLY_ERROR: default: r = -1; break; } freeReplyObject(reply); return r; } static int table_redis_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { redisReply *reply, *elmt; unsigned int i; int r; if (config->master == NULL && config_connect(config) == 0) return -1; reply = table_redis_query(key, service); if (reply == NULL) return -1; r = 1; switch(service) { case K_ALIAS: case K_CREDENTIALS: case K_USERINFO: memset(dst, 0, sz); if (reply->type == REDIS_REPLY_STRING) { if (strlcat(dst, reply->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } else if (reply->type == REDIS_REPLY_ARRAY) { if (reply->elements == 0) r = 0; for (i = 0; i < reply->elements; i++) { elmt = reply->element[i]; if (elmt == NULL || elmt->type != REDIS_REPLY_STRING) { r = -1; break; } if (dst[0] && strlcat(dst, service == K_ALIAS ? ", " : ":", sz) >= sz) { log_warnx("warn: result too large"); r = -1; } if (strlcat(dst, elmt->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } } else r = -1; break; case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: if (reply->type == REDIS_REPLY_STRING) { if (strlcpy(dst, reply->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } else r = -1; break; default: log_warnx("warn: unknown service %d", service); r = -1; } log_debug("debug: table_redis_lookup return %d (result = \"%s\")", r, dst); freeReplyObject(reply); return r; } static int table_redis_fetch(int service, struct dict *params, char *dst, size_t sz) { return -1; } int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); conffile = argv[0]; if ((config = config_load(conffile)) == NULL) fatalx("error parsing config file"); if (config_connect(config) == 0) fatalx("could not connect"); table_api_on_update(table_redis_update); table_api_on_check(table_redis_check); table_api_on_lookup(table_redis_lookup); table_api_on_fetch(table_redis_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-socketmap/000077500000000000000000000000001355240505200223745ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-socketmap/Makefile.am000066400000000000000000000003351355240505200244310ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-socketmap table_socketmap_SOURCES = $(SRCS) table_socketmap_SOURCES += table_socketmap.c man_MANS = table-socketmap.5 OpenSMTPD-extras-6.6.0/extras/tables/table-socketmap/table-socketmap.5000066400000000000000000000027331355240505200255420ustar00rootroot00000000000000.\" .\" Copyright (c) 2014 Gilles Chehade .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" .Dd $Mdocdate: February 4 2014 $ .Dt TABLE_SOCKETMAP 5 .Os .Sh NAME .Nm table_socketmap .Nd format description for smtpd socketmap tables .Sh DESCRIPTION This manual page documents the file format of "socketmap" tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh SOCKETMAP TABLE A "socketmap" table uses a simple protocol. The client sends a single-line request and the server sends a single-line reply. .Pp The table may be used for any kind of key-based lookup and replies are expected to follow the formats described in .Xr table 5 . .Sh SEE ALSO .Xr table 5 , .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 OpenSMTPD-extras-6.6.0/extras/tables/table-socketmap/table_socketmap.c000066400000000000000000000117521355240505200257030ustar00rootroot00000000000000/* * Copyright (c) 2014 Gilles Chehade * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include static char *config; static int sock = -1; static FILE *sockstream; #define REPLYBUFFERSIZE 100000 static char repbuffer[REPLYBUFFERSIZE+1]; enum socketmap_reply{ SM_OK = 0, SM_NOTFOUND, SM_TEMP, SM_TIMEOUT, SM_PERM, }; static int table_socketmap_connect(const char *s) { struct sockaddr_un sun; if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { log_warn("warn: socket"); goto err; } memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; if (strlcpy(sun.sun_path, s, sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { log_warnx("warn: socket path too long"); goto err; } if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { log_warn("warn: connect"); goto err; } if ((sockstream = fdopen(sock, "w+")) == NULL) { log_warn("warn: fdopen"); goto err; } return 1; err: if (sock == -1) { close(sock); sock = -1; } return 0; } static enum socketmap_reply table_socketmap_query(const char *name, const char *key) { char *buf = NULL; size_t sz = 0; ssize_t len; int ret = SM_PERM; memset(repbuffer, 0, sizeof repbuffer); fprintf(sockstream, "%s %s\n", name, key); fflush(sockstream); if ((len = getline(&buf, &sz, sockstream)) == -1) { log_warnx("warn: socketmap has lost its socket"); (void)strlcpy(repbuffer, "lost connection to socket", sizeof repbuffer); ret = SM_PERM; goto err; } if (buf[len - 1] == '\n') buf[len - 1] = '\0'; if (strlcpy(repbuffer, buf, sizeof repbuffer) >= sizeof repbuffer) { log_warnx("warn: socketmap reply too large (>%zu bytes)", sizeof repbuffer); (void)strlcpy(repbuffer, "socketmap reply too large", sizeof repbuffer); ret = SM_PERM; goto err; } if (strncasecmp(repbuffer, "OK ", 3) == 0) { ret = SM_OK; memmove(repbuffer, repbuffer+3, strlen(repbuffer)-2); } else if (strncasecmp(repbuffer, "NOTFOUND ", 9) == 0) { ret = SM_NOTFOUND; memmove(repbuffer, repbuffer+9, strlen(repbuffer)-8); } else if (strncasecmp(repbuffer, "TEMP ", 5) == 0) { ret = SM_TEMP; memmove(repbuffer, repbuffer+5, strlen(repbuffer)-4); } else if (strncasecmp(repbuffer, "TIMEOUT ", 8) == 0) { ret = SM_TIMEOUT; memmove(repbuffer, repbuffer+8, strlen(repbuffer)-7); } else if (strncasecmp(repbuffer, "PERM ", 5) == 0) { ret = SM_PERM; memmove(repbuffer, repbuffer+5, strlen(repbuffer)-4); } else { ret = SM_PERM; (void)strlcpy(repbuffer, "unrecognized socketmap reply", sizeof repbuffer); } err: free(buf); return ret; } static int table_socketmap_update(void) { return 1; } static int table_socketmap_check(int service, struct dict *params, const char *key) { return -1; } static int table_socketmap_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { int r; enum socketmap_reply rep; rep = table_socketmap_query(table_api_get_name(), key); if (rep == SM_NOTFOUND) return 0; if (rep != SM_OK) { log_warnx("warn: %s", repbuffer); return -1; } if (strlcpy(dst, repbuffer, sz) >= sz) { log_warnx("warn: result too large"); return -1; } r = 1; switch(service) { case K_ALIAS: case K_CREDENTIALS: case K_USERINFO: case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: break; default: log_warnx("warn: unknown service %d", service); r = -1; } return r; } static int table_socketmap_fetch(int service, struct dict *params, char *key, size_t sz) { return -1; } int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); config = argv[0]; if (table_socketmap_connect(config) == 0) fatalx("error connecting to %s", config); table_api_on_update(table_socketmap_update); table_api_on_check(table_socketmap_check); table_api_on_lookup(table_socketmap_lookup); table_api_on_fetch(table_socketmap_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-sqlite/000077500000000000000000000000001355240505200217075ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-sqlite/Makefile.am000066400000000000000000000003421355240505200237420ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-sqlite table_sqlite_SOURCES = $(SRCS) table_sqlite_SOURCES += table_sqlite.c man_MANS = table-sqlite.5 LDADD += -lsqlite3 OpenSMTPD-extras-6.6.0/extras/tables/table-sqlite/sqlite.conf000066400000000000000000000016551355240505200240660ustar00rootroot00000000000000# # Sample sqlite configuration file # # sqlite example schema # #CREATE TABLE virtuals ( # id INTEGER PRIMARY KEY AUTOINCREMENT, # email VARCHAR(255) NOT NULL, # destination VARCHAR(255) NOT NULL #); #CREATE TABLE credentials ( # id INTEGER PRIMARY KEY AUTOINCREMENT, # email VARCHAR(255) NOT NULL, # password VARCHAR(255) NOT NULL #); #CREATE TABLE domains ( # id INTEGER PRIMARY KEY AUTOINCREMENT, # domain VARCHAR(255) NOT NULL #); # DB file location # dbpath /etc/mail/smtp.sqlite # Alias lookup query # # rows >= 0 # fields == 1 (email VARCHAR) # query_alias SELECT destination FROM virtuals WHERE email=? # Domain lookup query # # rows == 1 # fields == 1 (domain VARCHAR) # query_domain SELECT domain FROM domains WHERE domain=?; # Credentials lookup query # # rows == 1 # fields == 2 (email VARCHAR, password VARCHAR) # query_credentials SELECT email, password FROM credentials WHERE email=?; OpenSMTPD-extras-6.6.0/extras/tables/table-sqlite/table-sqlite.5000066400000000000000000000146231355240505200243710ustar00rootroot00000000000000.\" .\" Copyright (c) 2014 Jason Barbier .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" .Dd $Mdocdate: July 4 2016 $ .Dt TABLE_SQLITE 5 .Os .Sh NAME .Nm table_sqlite .Nd format description for smtpd sqlite tables .Sh DESCRIPTION This manual page documents the file format of sqlite tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh SQLITE TABLE A sqlite table allows the storing of usernames, passwords, aliases, and domains in a format that is shareable across various machines that support .Xr sqlite3 1 (SQLite version 3). .Pp The table is used by .Xr smtpd 8 when authenticating a user, when user information such as user-id and/or home directory is required for a delivery, when a domain lookup may be required, and/or when looking for an alias. .Pp A sqlite table consists of one or more .Xr sqlite3 1 databases with one or more tables. .Pp If the table is used for authentication, the password should be encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Sh SQLITE TABLE CONFIG FILE The following configuration options are available: .Pp .Bl -tag -width Ds .It Xo .Ic dbpath .Ar file .Xc This is the path to where the DB file is located. For example: .Bd -literal -offset indent dbpath /etc/mail/smtp.sqlite .Ed .Pp .It Xo .Ic query_alias .Ar SQL statement .Xc This is used to provide a query to look up aliases. The question mark is replaced with the appropriate data. For alias it is the left hand side of the SMTP address. This expects one VARCHAR to be returned with the user name the alias resolves to. .Pp .It Xo .Ic query_credentials .Ar SQL statement .Xc This is used to provide a query for looking up user credentials. The question mark is replaced with the appropriate data. For credentials it is the left hand side of the SMTP address. The query expects that there are two VARCHARS returned, one with a user name and one with a password in .Xr crypt 3 format. .Pp .It Xo .Ic query_domain .Ar SQL statement .Xc This is used to provide a query for looking up a domain. The question mark is replaced with the appropriate data. For the domain it would be the right hand side of the SMTP address. This expects one VARCHAR to be returned with a matching domain name. .Pp .It Xo .Ic query_mailaddrmap .Ar SQL statement .Xc This is used to provide a query for looking up a senders. The question mark is replaced with the appropriate data. This expects one VARCHAR to be returned with the address the sender is allowed to send mails from. .El A generic SQL statement would be something like: .Bd -literal -offset indent query_ SELECT value FROM table WHERE key=?; .Ed .Sh EXAMPLES Example based on the OpenSMTPD FAQ: Building a Mail Server The filtering part is excluded in this example. The configuration below is for a medium-size mail server which handles multiple domains with multiple virtual users and is based on several assumptions. One is that a single system user named vmail is used for all virtual users. This user needs to be created: .Bd -literal # useradd -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail # mkdir /var/vmail # chown vmail:vmail /var/vmail .Ed .Ic Pa sqlite schema .Bd -literal -compact CREATE TABLE virtuals ( id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR(255) NOT NULL, destination VARCHAR(255) NOT NULL ); CREATE TABLE credentials ( id INTEGER PRIMARY KEY AUTOINCREMENT, email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL ); CREATE TABLE domains ( id INTEGER PRIMARY KEY AUTOINCREMENT, domain VARCHAR(255) NOT NULL ); INSERT INTO domains VALUES (1, "example.com"); INSERT INTO domains VALUES (2, "example.net"); INSERT INTO domains VALUES (3, "example.org"); INSERT INTO virtuals VALUES (1, "abuse@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (2, "postmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (3, "webmaster@example.com", "bob@example.com"); INSERT INTO virtuals VALUES (4, "bob@example.com", "vmail"); INSERT INTO virtuals VALUES (5, "abuse@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (6, "postmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (7, "webmaster@example.net", "alice@example.net"); INSERT INTO virtuals VALUES (8, "alice@example.net", "vmail"); INSERT INTO credentials VALUES (1, "bob@example.com", "$2b$08$ANGFKBL.BnDLL0bUl7I6aumTCLRJSQluSQLuueWRG.xceworWrUIu"); INSERT INTO credentials VALUES (2, "alice@example.net", "$2b$08$AkHdB37kaj2NEoTcISHSYOCEBA5vyW1RcD8H1HG.XX0P/G1KIYwii"); .Ed .Ic Pa /etc/mail/sqlite.conf .Bd -literal -compact dbpath /etc/mail/smtp.sqlite query_alias SELECT destination FROM virtuals WHERE email=?; query_credentials SELECT email, password FROM credentials WHERE email=?; query_domain SELECT domain FROM domains WHERE domain=?; .Ed .Ic Pa /etc/mail/smtpd.conf .Bd -literal -compact table domains sqlite:/etc/mail/sqlite.conf table virtuals sqlite:/etc/mail/sqlite.conf table credentials sqlite:/etc/mail/sqlite.conf listen on egress port 25 tls pki mail.example.com listen on egress port 587 tls-require pki mail.example.com auth accept from any for domain virtual deliver to mbox .Ed .Sh FILES .Bl -tag -width "/etc/mail/sqlite.conf" -compact .It Pa /etc/mail/sqlite.conf Default .Xr table-sqlite 8 configuration file. .It Pa /etc/mail/smtp.sqlite Suggested .Xr sqlite3 1 database file. .El .Sh TODO Documenting the following query options: .Bd -literal -offset indent -compact .Ic query_netaddr .Ic query_userinfo .Ic query_source .Ic query_mailaddr .Ic query_addrname .Ed .Sh SEE ALSO .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 , .Xr encrypt 1 , .Xr crypt 3 OpenSMTPD-extras-6.6.0/extras/tables/table-sqlite/table_sqlite.c000066400000000000000000000237711355240505200245350ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include enum { SQL_ALIAS = 0, SQL_DOMAIN, SQL_CREDENTIALS, SQL_NETADDR, SQL_USERINFO, SQL_SOURCE, SQL_MAILADDR, SQL_ADDRNAME, SQL_MAILADDRMAP, SQL_MAX }; #define DEFAULT_EXPIRE 60 #define DEFAULT_REFRESH 1000 static char *config; static sqlite3 *db; static sqlite3_stmt *statements[SQL_MAX]; static sqlite3_stmt *stmt_fetch_source; static struct dict sources; static void *source_iter; static size_t source_refresh = 1000, source_ncall; static int source_expire = 60; static time_t source_update; static int table_sqlite_getconfstr(const char *key, const char *value, char **var) { if (*var) { log_warnx("warn: duplicate %s %s", key, value); free(*var); } if ((*var = strdup(value)) == NULL) { log_warn("warn: strdup"); return -1; } return 0; } static sqlite3_stmt * table_sqlite_prepare_stmt(sqlite3 *_db, const char *query, int ncols) { sqlite3_stmt *stmt; if (sqlite3_prepare_v2(_db, query, -1, &stmt, 0) != SQLITE_OK) { log_warnx("warn: sqlite3_prepare_v2: %s", sqlite3_errmsg(_db)); goto end; } if (sqlite3_column_count(stmt) != ncols) { log_warnx("warn: columns: invalid columns count for query: %s", query); goto end; } return stmt; end: sqlite3_finalize(stmt); return NULL; } static int table_sqlite_update(void) { static const struct { const char *name; int cols; } qspec[SQL_MAX] = { { "query_alias", 1 }, { "query_domain", 1 }, { "query_credentials", 2 }, { "query_netaddr", 1 }, { "query_userinfo", 3 }, { "query_source", 1 }, { "query_mailaddr", 1 }, { "query_addrname", 1 }, { "query_mailaddrmap", 1 }, }; sqlite3 *_db; sqlite3_stmt *_statements[SQL_MAX]; sqlite3_stmt *_stmt_fetch_source; char *_query_fetch_source; char *queries[SQL_MAX]; ssize_t flen; size_t sz = 0, _source_refresh; int _source_expire; FILE *fp; char *key, *value, *buf = NULL, *dbpath; const char *e; int i, ret; long long ll; dbpath = NULL; _db = NULL; memset(queries, 0, sizeof(queries)); memset(_statements, 0, sizeof(_statements)); _query_fetch_source = NULL; _stmt_fetch_source = NULL; _source_refresh = DEFAULT_REFRESH; _source_expire = DEFAULT_EXPIRE; ret = 0; /* parse configuration */ if ((fp = fopen(config, "r")) == NULL) { log_warn("warn: \"%s\"", config); return 0; } while ((flen = getline(&buf, &sz, fp)) != -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); continue; } if (!strcmp("dbpath", key)) { if (table_sqlite_getconfstr(key, value, &dbpath) == -1) goto end; continue; } if (!strcmp("fetch_source", key)) { if (table_sqlite_getconfstr(key, value, &_query_fetch_source) == -1) goto end; continue; } if (!strcmp("fetch_source_expire", key)) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for %s: %s", key, e); goto end; } _source_expire = ll; continue; } if (!strcmp("fetch_source_refresh", key)) { e = NULL; ll = strtonum(value, 0, INT_MAX, &e); if (e) { log_warnx("warn: bad value for %s: %s", key, e); goto end; } _source_refresh = ll; continue; } for (i = 0; i < SQL_MAX; i++) if (!strcmp(qspec[i].name, key)) break; if (i == SQL_MAX) { log_warnx("warn: bogus key %s", key); continue; } if (queries[i]) { log_warnx("warn: duplicate key %s", key); continue; } if ((queries[i] = strdup(value)) == NULL) { log_warnx("warn: strdup"); goto end; } } /* set up db */ log_debug("debug: opening %s", dbpath); if (sqlite3_open(dbpath, &_db) != SQLITE_OK) { log_warnx("warn: open: %s", sqlite3_errmsg(_db)); goto end; } for (i = 0; i < SQL_MAX; i++) { if (queries[i] == NULL) continue; if ((_statements[i] = table_sqlite_prepare_stmt(_db, queries[i], qspec[i].cols)) == NULL) goto end; } if (_query_fetch_source && (_stmt_fetch_source = table_sqlite_prepare_stmt(_db, _query_fetch_source, 1)) == NULL) goto end; /* replace previous setup */ for (i = 0; i < SQL_MAX; i++) { if (statements[i]) sqlite3_finalize(statements[i]); statements[i] = _statements[i]; _statements[i] = NULL; } if (stmt_fetch_source) sqlite3_finalize(stmt_fetch_source); stmt_fetch_source = _stmt_fetch_source; _stmt_fetch_source = NULL; if (db) sqlite3_close(_db); db = _db; _db = NULL; source_update = 0; /* force update */ source_expire = _source_expire; source_refresh = _source_refresh; log_debug("debug: config successfully updated"); ret = 1; end: /* Cleanup */ for (i = 0; i < SQL_MAX; i++) { if (_statements[i]) sqlite3_finalize(_statements[i]); free(queries[i]); } if (_db) sqlite3_close(_db); free(dbpath); free(_query_fetch_source); free(buf); fclose(fp); return ret; } static sqlite3_stmt * table_sqlite_query(const char *key, int service) { int i; sqlite3_stmt *stmt = NULL; for (i = 0; i < SQL_MAX; i++) { if (service == (1 << i)) { stmt = statements[i]; break; } } if (stmt == NULL) return NULL; if (sqlite3_bind_text(stmt, 1, key, strlen(key), NULL) != SQLITE_OK) { log_warnx("warn: sqlite3_bind_text: %s", sqlite3_errmsg(db)); return NULL; } return stmt; } static int table_sqlite_check(int service, struct dict *params, const char *key) { sqlite3_stmt *stmt; int r; stmt = table_sqlite_query(key, service); if (stmt == NULL) return -1; r = sqlite3_step(stmt); sqlite3_reset(stmt); if (r == SQLITE_ROW) return 1; if (r == SQLITE_DONE) return 0; return -1; } static int table_sqlite_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { sqlite3_stmt *stmt; const char *value; int r, s; stmt = table_sqlite_query(key, service); if (stmt == NULL) return -1; s = sqlite3_step(stmt); if (s == SQLITE_DONE) { sqlite3_reset(stmt); return 0; } if (s != SQLITE_ROW) { log_warnx("warn: sqlite3_step: %s", sqlite3_errmsg(db)); sqlite3_reset(stmt); return -1; } r = 1; switch(service) { case K_ALIAS: case K_MAILADDRMAP: memset(dst, 0, sz); do { value = sqlite3_column_text(stmt, 0); if (dst[0] && strlcat(dst, ", ", sz) >= sz) { log_warnx("warn: result too large"); r = -1; break; } if (strlcat(dst, value, sz) >= sz) { log_warnx("warn: result too large"); r = -1; break; } s = sqlite3_step(stmt); } while (s == SQLITE_ROW); if (s != SQLITE_ROW && s != SQLITE_DONE) { log_warnx("warn: sqlite3_step: %s", sqlite3_errmsg(db)); r = -1; } break; case K_CREDENTIALS: if (snprintf(dst, sz, "%s:%s", sqlite3_column_text(stmt, 0), sqlite3_column_text(stmt, 1)) >= (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_USERINFO: if (snprintf(dst, sz, "%d:%d:%s", sqlite3_column_int(stmt, 0), sqlite3_column_int(stmt, 1), sqlite3_column_text(stmt, 2)) >= (ssize_t)sz) { log_warnx("warn: result too large"); r = -1; } break; case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: if (strlcpy(dst, sqlite3_column_text(stmt, 0), sz) >= sz) { log_warnx("warn: result too large"); r = -1; } break; default: log_warnx("warn: unknown service %d", service); r = -1; } sqlite3_reset(stmt); return r; } static int table_sqlite_fetch(int service, struct dict *params, char *dst, size_t sz) { const char *k; int s; if (service != K_SOURCE) return -1; if (stmt_fetch_source == NULL) return -1; if (source_ncall < source_refresh && time(NULL) - source_update < source_expire) goto fetch; source_iter = NULL; while (dict_poproot(&sources, NULL)) ; while ((s = sqlite3_step(stmt_fetch_source)) == SQLITE_ROW) dict_set(&sources, sqlite3_column_text(stmt_fetch_source, 0), NULL); if (s != SQLITE_DONE) log_warnx("warn: sqlite3_step: %s", sqlite3_errmsg(db)); sqlite3_reset(stmt_fetch_source); source_update = time(NULL); source_ncall = 0; fetch: source_ncall += 1; if (!dict_iter(&sources, &source_iter, &k, (void **)NULL)) { source_iter = NULL; if (!dict_iter(&sources, &source_iter, &k, (void **)NULL)) return 0; } if (strlcpy(dst, k, sz) >= sz) return -1; return 1; } int main(int argc, char **argv) { int ch; log_init(1); log_verbose(~0); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc != 1) fatalx("bogus argument(s)"); config = argv[0]; dict_init(&sources); if (table_sqlite_update() == 0) fatalx("error parsing config file"); table_api_on_update(table_sqlite_update); table_api_on_check(table_sqlite_check); table_api_on_lookup(table_sqlite_lookup); table_api_on_fetch(table_sqlite_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/extras/tables/table-stub/000077500000000000000000000000001355240505200213635ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/extras/tables/table-stub/Makefile.am000066400000000000000000000002511355240505200234150ustar00rootroot00000000000000include $(top_srcdir)/mk/paths.mk include $(top_srcdir)/mk/table.mk pkglibexec_PROGRAMS = table-stub table_stub_SOURCES = $(SRCS) table_stub_SOURCES += table_stub.c OpenSMTPD-extras-6.6.0/extras/tables/table-stub/table_stub.c000066400000000000000000000032341355240505200236550ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include static int table_stub_update(void) { return -1; } static int table_stub_check(int service, struct dict *params, const char *key) { return -1; } static int table_stub_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { return -1; } static int table_stub_fetch(int service, struct dict *params, char *dst, size_t sz) { return -1; } int main(int argc, char **argv) { int ch; log_init(1); while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: fatalx("bad option"); /* NOTREACHED */ } } argc -= optind; argv += optind; if (argc) fatalx("bogus argument(s)"); table_api_on_update(table_stub_update); table_api_on_check(table_stub_check); table_api_on_lookup(table_stub_lookup); table_api_on_fetch(table_stub_fetch); table_api_dispatch(); return 0; } OpenSMTPD-extras-6.6.0/m4/000077500000000000000000000000001355240505200150615ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/m4/aclocal-openssh.m4000066400000000000000000000125471355240505200204070ustar00rootroot00000000000000dnl $Id: aclocal.m4,v 1.13 2014/01/22 10:30:12 djm Exp $ dnl dnl OpenSSH-specific autoconf macros dnl dnl OSSH_CHECK_CFLAG_COMPILE(check_flag[, define_flag]) dnl Check that $CC accepts a flag 'check_flag'. If it is supported append dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append dnl 'check_flag'. AC_DEFUN([OSSH_CHECK_CFLAG_COMPILE], [{ AC_MSG_CHECKING([if $CC supports compile flag $1]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR $1" _define_flag="$2" test "x$_define_flag" = "x" && _define_flag="$1" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #include #include int main(int argc, char **argv) { /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); exit(0); } ]])], [ if `grep -i "unrecognized option" conftest.err >/dev/null` then AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" else AC_MSG_RESULT([yes]) CFLAGS="$saved_CFLAGS $_define_flag" fi], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ] ) }]) dnl OSSH_CHECK_CFLAG_LINK(check_flag[, define_flag]) dnl Check that $CC accepts a flag 'check_flag'. If it is supported append dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append dnl 'check_flag'. AC_DEFUN([OSSH_CHECK_CFLAG_LINK], [{ AC_MSG_CHECKING([if $CC supports compile flag $1 and linking succeeds]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $WERROR $1" _define_flag="$2" test "x$_define_flag" = "x" && _define_flag="$1" AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include #include int main(int argc, char **argv) { /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); exit(0); } ]])], [ if `grep -i "unrecognized option" conftest.err >/dev/null` then AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" else AC_MSG_RESULT([yes]) CFLAGS="$saved_CFLAGS $_define_flag" fi], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ] ) }]) dnl OSSH_CHECK_LDFLAG_LINK(check_flag[, define_flag]) dnl Check that $LD accepts a flag 'check_flag'. If it is supported append dnl 'define_flag' to $LDFLAGS. If 'define_flag' is not specified, then append dnl 'check_flag'. AC_DEFUN([OSSH_CHECK_LDFLAG_LINK], [{ AC_MSG_CHECKING([if $LD supports link flag $1]) saved_LDFLAGS="$LDFLAGS" LDFLAGS="$LDFLAGS $WERROR $1" _define_flag="$2" test "x$_define_flag" = "x" && _define_flag="$1" AC_LINK_IFELSE([AC_LANG_SOURCE([[ #include #include int main(int argc, char **argv) { /* Some math to catch -ftrapv problems in the toolchain */ int i = 123 * argc, j = 456 + argc, k = 789 - argc; float l = i * 2.1; double m = l / 0.5; long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); exit(0); } ]])], [ AC_MSG_RESULT([yes]) LDFLAGS="$saved_LDFLAGS $_define_flag"], [ AC_MSG_RESULT([no]) LDFLAGS="$saved_LDFLAGS" ] ) }]) dnl OSSH_CHECK_HEADER_FOR_FIELD(field, header, symbol) dnl Does AC_EGREP_HEADER on 'header' for the string 'field' dnl If found, set 'symbol' to be defined. Cache the result. dnl TODO: This is not foolproof, better to compile and read from there AC_DEFUN([OSSH_CHECK_HEADER_FOR_FIELD], [ # look for field '$1' in header '$2' dnl This strips characters illegal to m4 from the header filename ossh_safe=`echo "$2" | sed 'y%./+-%__p_%'` dnl ossh_varname="ossh_cv_$ossh_safe""_has_"$1 AC_MSG_CHECKING(for $1 field in $2) AC_CACHE_VAL($ossh_varname, [ AC_EGREP_HEADER($1, $2, [ dnl eval "$ossh_varname=yes" dnl ], [ dnl eval "$ossh_varname=no" dnl ]) dnl ]) ossh_result=`eval 'echo $'"$ossh_varname"` if test -n "`echo $ossh_varname`"; then AC_MSG_RESULT($ossh_result) if test "x$ossh_result" = "xyes"; then AC_DEFINE($3, 1, [Define if you have $1 in $2]) fi else AC_MSG_RESULT(no) fi ]) dnl Check for socklen_t: historically on BSD it is an int, and in dnl POSIX 1g it is a type of its own, but some platforms use different dnl types for the argument to getsockopt, getpeername, etc. So we dnl have to test to find something that will work. AC_DEFUN([TYPE_SOCKLEN_T], [ AC_CHECK_TYPE([socklen_t], ,[ AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([curl_cv_socklen_t_equiv], [ # Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername curl_cv_socklen_t_equiv= for arg2 in "struct sockaddr" void; do for t in int size_t unsigned long "unsigned long"; do AC_TRY_COMPILE([ #include #include int getpeername (int, $arg2 *, $t *); ],[ $t len; getpeername(0,0,&len); ],[ curl_cv_socklen_t_equiv="$t" break ]) done done if test "x$curl_cv_socklen_t_equiv" = x; then AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) fi ]) AC_MSG_RESULT($curl_cv_socklen_t_equiv) AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, [type to use in place of socklen_t if not defined])], [#include #include ]) ]) OpenSMTPD-extras-6.6.0/mk/000077500000000000000000000000001355240505200151505ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/mk/experimental.mk000066400000000000000000000000361355240505200201750ustar00rootroot00000000000000AM_CPPFLAGS += -DEXPERIMENTAL OpenSMTPD-extras-6.6.0/mk/filter.mk000066400000000000000000000007361355240505200167740ustar00rootroot00000000000000AM_CPPFLAGS = -I$(api_srcdir) AM_CPPFLAGS += -I$(compat_srcdir) AM_CPPFLAGS += -I$(asr_srcdir) AM_CPPFLAGS += $(PATHS) SRCS = $(api_srcdir)/filter_api.c SRCS += $(api_srcdir)/mproc.c SRCS += $(api_srcdir)/log.c SRCS += $(api_srcdir)/tree.c SRCS += $(api_srcdir)/dict.c SRCS += $(api_srcdir)/util.c SRCS += $(api_srcdir)/iobuf.c SRCS += $(api_srcdir)/ioev.c SRCS += $(api_srcdir)/rfc2822.c LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd-compat.a LDADD = $(LIBCOMPAT) OpenSMTPD-extras-6.6.0/mk/paths.mk000066400000000000000000000010211355240505200166120ustar00rootroot00000000000000api_srcdir = $(top_srcdir)/api asr_srcdir = $(top_srcdir)/contrib/lib/libc/asr compat_srcdir = $(top_srcdir)/openbsd-compat regress_srcdir = $(top_srcdir)/regress/bin filters_srcdir = $(top_srcdir)/extras/filters queues_srcdir = $(top_srcdir)/extras/queues schedulers_srcdir = $(top_srcdir)/extras/schedulers tables_srcdir = $(top_srcdir)/extras/tables tools_srcdir = $(top_srcdir)/extras/tools PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \ -DPATH_CHROOT=\"$(PRIVSEP_PATH)\" \ -DPATH_LIBEXEC=\"$(pkglibexecdir)\" OpenSMTPD-extras-6.6.0/mk/queue.mk000066400000000000000000000005621355240505200166300ustar00rootroot00000000000000AM_CPPFLAGS = -I$(api_srcdir) AM_CPPFLAGS += -I$(compat_srcdir) LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd-compat.a LDADD = $(LIBCOMPAT) SRCS = $(api_srcdir)/log.c SRCS += $(api_srcdir)/queue_utils.c SRCS += $(api_srcdir)/queue_api.c SRCS += $(api_srcdir)/tree.c SRCS += $(api_srcdir)/dict.c SRCS += $(api_srcdir)/util.c SRCS += $(api_srcdir)/iobuf.c OpenSMTPD-extras-6.6.0/mk/scheduler.mk000066400000000000000000000004621355240505200174610ustar00rootroot00000000000000AM_CPPFLAGS = -I$(api_srcdir) AM_CPPFLAGS += -I$(compat_srcdir) LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd-compat.a LDADD = $(LIBCOMPAT) SRCS = $(api_srcdir)/log.c SRCS += $(api_srcdir)/scheduler_api.c SRCS += $(api_srcdir)/tree.c SRCS += $(api_srcdir)/util.c SRCS += $(api_srcdir)/iobuf.c OpenSMTPD-extras-6.6.0/mk/table.mk000066400000000000000000000005131355240505200165670ustar00rootroot00000000000000AM_CPPFLAGS = -I$(api_srcdir) AM_CPPFLAGS += -I$(compat_srcdir) LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd-compat.a LDADD = $(LIBCOMPAT) SRCS = $(api_srcdir)/log.c SRCS += $(api_srcdir)/table_api.c SRCS += $(api_srcdir)/tree.c SRCS += $(api_srcdir)/dict.c SRCS += $(api_srcdir)/util.c SRCS += $(api_srcdir)/iobuf.c OpenSMTPD-extras-6.6.0/mk/tool.mk000066400000000000000000000004511355240505200164560ustar00rootroot00000000000000AM_CPPFLAGS = -I$(api_srcdir) AM_CPPFLAGS += -I$(compat_srcdir) LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd-compat.a LDADD = $(LIBCOMPAT) SRCS = $(api_srcdir)/log.c SRCS += $(api_srcdir)/tree.c SRCS += $(api_srcdir)/dict.c SRCS += $(api_srcdir)/util.c SRCS += $(api_srcdir)/iobuf.c OpenSMTPD-extras-6.6.0/openbsd-compat/000077500000000000000000000000001355240505200174545ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/openbsd-compat/Makefile.am000066400000000000000000000006741355240505200215170ustar00rootroot00000000000000noinst_LIBRARIES = libopenbsd-compat.a libopenbsd_compat_a_SOURCES = \ arc4random.c base64.c bsd-closefrom.c bsd-misc.c \ event_asr_run.c imsg.c \ imsg-buffer.c mktemp.c strlcat.c strlcpy.c strtonum.c strsep.c EXTRA_DIST = asr.h base64.h bsd-misc.h chacha_private.h defines.h \ imsg.h includes.h openbsd-compat.h sys/queue.h sys/tree.h AM_CPPFLAGS = -I$(top_srcdir)/smtpd -I$(top_srcdir)/openbsd-compat \ -I${top_srcdir}/api OpenSMTPD-extras-6.6.0/openbsd-compat/NOTES000066400000000000000000000011541355240505200202700ustar00rootroot00000000000000List of files and where they come from asr.h /usr/src/include/asr.h arc4random.c portable openssh base64.{c,h} portable openssh bsd-closefrom.c portable openssh defines.h portable openssh event_asr_run.c end of /usr/src/lib/libevent/event.c imsg-buffer.c part of /usr/src/libutil/imsg-buffer.c imsg.{c,h} part of /usr/src/libutil/imsg.c includes.h portable openssh mktemp.c portable openssh openbsd-compat.h portable openssh strlcat.c portable openssh strlcpy.c portable openssh strsep.c portable openssh strtonum.c portable openssh sys/queue.h portable openssh sys/tree.h portable openssh OpenSMTPD-extras-6.6.0/openbsd-compat/arc4random.c000066400000000000000000000131061355240505200216530ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */ /* $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $ */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * Copyright (c) 2013, Markus Friedl * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * ChaCha based random number generator for OpenBSD. */ #include "includes.h" #include #include #include #include #ifndef HAVE_ARC4RANDOM #include #include #include "log.h" #define KEYSTREAM_ONLY #include "chacha_private.h" #ifdef __GNUC__ #define inline __inline #else /* !__GNUC__ */ #define inline #endif /* !__GNUC__ */ /* OpenSMTPD isn't multithreaded */ #define _ARC4_LOCK() #define _ARC4_UNLOCK() #define KEYSZ 32 #define IVSZ 8 #define BLOCKSZ 64 #define RSBUFSZ (16*BLOCKSZ) static int rs_initialized; static pid_t rs_stir_pid; static chacha_ctx rs; /* chacha context for random keystream */ static unsigned char rs_buf[RSBUFSZ]; /* keystream blocks */ static size_t rs_have; /* valid bytes at end of rs_buf */ static size_t rs_count; /* bytes till reseed */ static inline void _rs_rekey(unsigned char *dat, size_t datlen); static inline void _rs_init(unsigned char *buf, size_t n) { if (n < KEYSZ + IVSZ) return; chacha_keysetup(&rs, buf, KEYSZ * 8, 0); chacha_ivsetup(&rs, buf + KEYSZ); } static void _rs_stir(void) { unsigned char rnd[KEYSZ + IVSZ]; if (RAND_bytes(rnd, sizeof(rnd)) <= 0) fatal("Couldn't obtain random bytes (error %ld)", ERR_get_error()); if (!rs_initialized) { rs_initialized = 1; _rs_init(rnd, sizeof(rnd)); } else _rs_rekey(rnd, sizeof(rnd)); memset(rnd, 0, sizeof(rnd)); /* invalidate rs_buf */ rs_have = 0; memset(rs_buf, 0, RSBUFSZ); rs_count = 1600000; } static inline void _rs_stir_if_needed(size_t len) { pid_t pid = getpid(); if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) { rs_stir_pid = pid; _rs_stir(); } else rs_count -= len; } static inline void _rs_rekey(unsigned char *dat, size_t datlen) { #ifndef KEYSTREAM_ONLY memset(rs_buf, 0,RSBUFSZ); #endif /* fill rs_buf with the keystream */ chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ); /* mix in optional user provided data */ if (dat) { size_t i, m; m = MIN(datlen, KEYSZ + IVSZ); for (i = 0; i < m; i++) rs_buf[i] ^= dat[i]; } /* immediately reinit for backtracking resistance */ _rs_init(rs_buf, KEYSZ + IVSZ); memset(rs_buf, 0, KEYSZ + IVSZ); rs_have = RSBUFSZ - KEYSZ - IVSZ; } static inline void _rs_random_buf(void *_buf, size_t n) { unsigned char *buf = (unsigned char *)_buf; size_t m; _rs_stir_if_needed(n); while (n > 0) { if (rs_have > 0) { m = MIN(n, rs_have); memcpy(buf, rs_buf + RSBUFSZ - rs_have, m); memset(rs_buf + RSBUFSZ - rs_have, 0, m); buf += m; n -= m; rs_have -= m; } if (rs_have == 0) _rs_rekey(NULL, 0); } } static inline void _rs_random_u32(uint32_t *val) { _rs_stir_if_needed(sizeof(*val)); if (rs_have < sizeof(*val)) _rs_rekey(NULL, 0); memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val)); memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val)); rs_have -= sizeof(*val); return; } void arc4random_stir(void) { _ARC4_LOCK(); _rs_stir(); _ARC4_UNLOCK(); } void arc4random_addrandom(unsigned char *dat, int datlen) { int m; _ARC4_LOCK(); if (!rs_initialized) _rs_stir(); while (datlen > 0) { m = MIN(datlen, KEYSZ + IVSZ); _rs_rekey(dat, m); dat += m; datlen -= m; } _ARC4_UNLOCK(); } uint32_t arc4random(void) { uint32_t val; _ARC4_LOCK(); _rs_random_u32(&val); _ARC4_UNLOCK(); return val; } #endif /* !HAVE_ARC4RANDOM */ #ifndef HAVE_ARC4RANDOM_UNIFORM /* * Calculate a uniformly distributed random number less than upper_bound * avoiding "modulo bias". * * Uniformity is achieved by generating new random numbers until the one * returned is outside the range [0, 2**32 % upper_bound). This * guarantees the selected random number will be inside * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) * after reduction modulo upper_bound. */ uint32_t arc4random_uniform(uint32_t upper_bound) { uint32_t r, min; if (upper_bound < 2) return 0; /* 2**32 % x == (2**32 - x) % x */ min = -upper_bound % upper_bound; /* * This could theoretically loop forever but each retry has * p > 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = arc4random(); if (r >= min) break; } return r % upper_bound; } #endif /* !HAVE_ARC4RANDOM_UNIFORM */ #if 0 /*-------- Test code for i386 --------*/ #include #include int main(int argc, char **argv) { const int iter = 1000000; int i; pctrval v; v = rdtsc(); for (i = 0; i < iter; i++) arc4random(); v = rdtsc() - v; v /= iter; printf("%qd cycles\n", v); exit(0); } #endif OpenSMTPD-extras-6.6.0/openbsd-compat/asr.h000066400000000000000000000067221355240505200204210ustar00rootroot00000000000000/* $OpenBSD: asr.h,v 1.1 2014/03/26 18:13:15 eric Exp $ */ /* * Copyright (c) 2012-2014 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" /* * Expected fd conditions */ #define ASR_WANT_READ 1 #define ASR_WANT_WRITE 2 /* * Structure through which asynchronous query results are returned when * calling asr_run(). */ struct asr_result { /* Fields set if the query is not done yet (asr_run returns 0) */ int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */ int ar_fd; /* the fd waiting for io condition */ int ar_timeout; /* time to wait for in milliseconds */ /* Error fields. Depends on the query type. */ int ar_errno; int ar_h_errno; int ar_gai_errno; int ar_rrset_errno; /* Result for res_*_async() calls */ int ar_count; /* number of answers in the dns reply */ int ar_rcode; /* response code in the dns reply */ void *ar_data; /* raw reply packet (must be freed) */ int ar_datalen; /* reply packet length */ struct sockaddr_storage ar_ns; /* nameserver that responded */ /* Result for other calls. Must be freed properly. */ struct addrinfo *ar_addrinfo; struct rrsetinfo *ar_rrsetinfo; struct hostent *ar_hostent; struct netent *ar_netent; }; /* * Asynchronous query management. */ /* Forward declaration. The API uses opaque pointers as query handles. */ struct asr_query; int asr_run(struct asr_query *, struct asr_result *); int asr_run_sync(struct asr_query *, struct asr_result *); void asr_abort(struct asr_query *); /* * Asynchronous version of the resolver functions. Similar prototypes, with * an extra context parameter at the end which must currently be set to NULL. * All functions return a handle suitable for use with the management functions * above. */ struct asr_query *res_send_async(const unsigned char *, int, void *); struct asr_query *res_query_async(const char *, int, int, void *); struct asr_query *res_search_async(const char *, int, int, void *); struct asr_query *getrrsetbyname_async(const char *, unsigned int, unsigned int, unsigned int, void *); struct asr_query *gethostbyname_async(const char *, void *); struct asr_query *gethostbyname2_async(const char *, int, void *); struct asr_query *gethostbyaddr_async(const void *, socklen_t, int, void *); struct asr_query *getnetbyname_async(const char *, void *); struct asr_query *getnetbyaddr_async(in_addr_t, int, void *); struct asr_query *getaddrinfo_async(const char *, const char *, const struct addrinfo *, void *); struct asr_query *getnameinfo_async(const struct sockaddr *, socklen_t, char *, size_t, char *, size_t, int, void *); /* only there for -portable */ void asr_freeaddrinfo(struct addrinfo *); /* from in event.h */ struct event_asr * event_asr_run(struct asr_query *, void (*)(struct asr_result *, void *), void *); OpenSMTPD-extras-6.6.0/openbsd-compat/base64.c000066400000000000000000000244501355240505200207110ustar00rootroot00000000000000/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ /* OPENBSD ORIGINAL: lib/libc/net/base64.c */ #include "includes.h" #if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) #include #include #include #include #include #include #include #include #include #include "base64.h" static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char Pad64 = '='; /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) The following encoding technique is taken from RFC 1521 by Borenstein and Freed. It is reproduced here in a slightly edited form for convenience. A 65-character subset of US-ASCII is used, enabling 6 bits to be represented per printable character. (The extra 65th character, "=", is used to signify a special processing function.) The encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters. Proceeding from left to right, a 24-bit input group is formed by concatenating 3 8-bit input groups. These 24 bits are then treated as 4 concatenated 6-bit groups, each of which is translated into a single digit in the base64 alphabet. Each 6-bit group is used as an index into an array of 64 printable characters. The character referenced by the index is placed in the output string. Table 1: The Base64 Alphabet Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y Special processing is performed if fewer than 24 bits are available at the end of the data being encoded. A full encoding quantum is always completed at the end of a quantity. When fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. Padding at the end of the data is performed using the '=' character. Since all base64 input is an integral number of octets, only the ------------------------------------------------- following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character. */ #if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) int b64_ntop(unsigned char const *src, size_t srclength, char *target, size_t targsize) { size_t datalength = 0; unsigned char input[3]; unsigned char output[4]; unsigned int i; while (2 < srclength) { input[0] = *src++; input[1] = *src++; input[2] = *src++; srclength -= 3; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); output[3] = input[2] & 0x3f; if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; target[datalength++] = Base64[output[2]]; target[datalength++] = Base64[output[3]]; } /* Now we worry about padding. */ if (0 != srclength) { /* Get what's left. */ input[0] = input[1] = input[2] = '\0'; for (i = 0; i < srclength; i++) input[i] = *src++; output[0] = input[0] >> 2; output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); if (datalength + 4 > targsize) return (-1); target[datalength++] = Base64[output[0]]; target[datalength++] = Base64[output[1]]; if (srclength == 1) target[datalength++] = Pad64; else target[datalength++] = Base64[output[2]]; target[datalength++] = Pad64; } if (datalength >= targsize) return (-1); target[datalength] = '\0'; /* Returned value doesn't count \0. */ return (datalength); } #endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ #if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) /* skips all whitespace anywhere. converts characters, four at a time, starting at (or after) src from base - 64 numbers into three 8 bit bytes in the target area. it returns the number of data bytes stored at the target, or -1 on error. */ int b64_pton(char const *src, unsigned char *target, size_t targsize) { unsigned int tarindex, state; int ch; char *pos; state = 0; tarindex = 0; while ((ch = *src++) != '\0') { if (isspace(ch)) /* Skip whitespace anywhere. */ continue; if (ch == Pad64) break; pos = strchr(Base64, ch); if (pos == 0) /* A non-base64 character. */ return (-1); switch (state) { case 0: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] = (pos - Base64) << 2; } state = 1; break; case 1: if (target) { if (tarindex + 1 >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 4; target[tarindex+1] = ((pos - Base64) & 0x0f) << 4 ; } tarindex++; state = 2; break; case 2: if (target) { if (tarindex + 1 >= targsize) return (-1); target[tarindex] |= (pos - Base64) >> 2; target[tarindex+1] = ((pos - Base64) & 0x03) << 6; } tarindex++; state = 3; break; case 3: if (target) { if (tarindex >= targsize) return (-1); target[tarindex] |= (pos - Base64); } tarindex++; state = 0; break; } } /* * We are done decoding Base-64 chars. Let's see if we ended * on a byte boundary, and/or with erroneous trailing characters. */ if (ch == Pad64) { /* We got a pad char. */ ch = *src++; /* Skip it, get next. */ switch (state) { case 0: /* Invalid = in first position */ case 1: /* Invalid = in second position */ return (-1); case 2: /* Valid, means one byte of info */ /* Skip any number of spaces. */ for (; ch != '\0'; ch = *src++) if (!isspace(ch)) break; /* Make sure there is another trailing = sign. */ if (ch != Pad64) return (-1); ch = *src++; /* Skip the = */ /* Fall through to "single trailing =" case. */ /* FALLTHROUGH */ case 3: /* Valid, means two bytes of info */ /* * We know this char is an =. Is there anything but * whitespace after it? */ for (; ch != '\0'; ch = *src++) if (!isspace(ch)) return (-1); /* * Now make sure for cases 2 and 3 that the "extra" * bits that slopped past the last full byte were * zeros. If we don't check them, they become a * subliminal channel. */ if (target && target[tarindex] != 0) return (-1); } } else { /* * We ended by seeing the end of the string. Make sure we * have no partial bytes lying around. */ if (state != 0) return (-1); } return (tarindex); } #endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */ #endif OpenSMTPD-extras-6.6.0/openbsd-compat/base64.h000066400000000000000000000054231355240505200207150ustar00rootroot00000000000000/* $Id: base64.h,v 1.6 2003/08/29 16:59:52 mouring Exp $ */ /* * Copyright (c) 1996 by Internet Software Consortium. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* * Portions Copyright (c) 1995 by International Business Machines, Inc. * * International Business Machines, Inc. (hereinafter called IBM) grants * permission under its copyrights to use, copy, modify, and distribute this * Software with or without fee, provided that the above copyright notice and * all paragraphs of this notice appear in all copies, and that the name of IBM * not be used in connection with the marketing of any product incorporating * the Software or modifications thereof, without specific, written prior * permission. * * To the extent it has a right to do so, IBM grants an immunity from suit * under its patents, if any, for the use, sale or manufacture of products to * the extent that such products are used for performing Domain Name System * dynamic updates in TCP/IP networks by means of the Software. No immunity is * granted for any product per se or for any other function of any product. * * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef _BSD_BASE64_H #define _BSD_BASE64_H #include "includes.h" #ifndef HAVE___B64_NTOP # ifndef HAVE_B64_NTOP int b64_ntop(unsigned char const *src, size_t srclength, char *target, size_t targsize); # endif /* !HAVE_B64_NTOP */ # define __b64_ntop(a,b,c,d) b64_ntop(a,b,c,d) #endif /* HAVE___B64_NTOP */ #ifndef HAVE___B64_PTON # ifndef HAVE_B64_PTON int b64_pton(char const *src, unsigned char *target, size_t targsize); # endif /* !HAVE_B64_PTON */ # define __b64_pton(a,b,c) b64_pton(a,b,c) #endif /* HAVE___B64_PTON */ #endif /* _BSD_BASE64_H */ OpenSMTPD-extras-6.6.0/openbsd-compat/bsd-closefrom.c000066400000000000000000000055321355240505200223640ustar00rootroot00000000000000/* * Copyright (c) 2004-2005 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #ifndef HAVE_CLOSEFROM #include #include #include #include #ifdef HAVE_FCNTL_H # include #endif #include #include #include #include #include #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # endif #endif #ifndef OPEN_MAX # define OPEN_MAX 256 #endif #if 0 __unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; #endif /* lint */ /* * Close all file descriptors greater than or equal to lowfd. */ #ifdef HAVE_FCNTL_CLOSEM void closefrom(int lowfd) { (void) fcntl(lowfd, F_CLOSEM, 0); } #else void closefrom(int lowfd) { long fd, maxfd; #if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) char fdpath[PATH_MAX], *endp; struct dirent *dent; DIR *dirp; int len; /* Check for a /proc/$$/fd directory. */ len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { while ((dent = readdir(dirp)) != NULL) { fd = strtol(dent->d_name, &endp, 10); if (dent->d_name != endp && *endp == '\0' && fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) (void) close((int) fd); } (void) closedir(dirp); } else #endif { /* * Fall back on sysconf() or getdtablesize(). We avoid checking * resource limits since it is possible to open a file descriptor * and then drop the rlimit such that it is below the open fd. */ #ifdef HAVE_SYSCONF maxfd = sysconf(_SC_OPEN_MAX); #else maxfd = getdtablesize(); #endif /* HAVE_SYSCONF */ if (maxfd < 0) maxfd = OPEN_MAX; for (fd = lowfd; fd < maxfd; fd++) (void) close((int) fd); } } #endif /* !HAVE_FCNTL_CLOSEM */ #endif /* HAVE_CLOSEFROM */ OpenSMTPD-extras-6.6.0/openbsd-compat/bsd-misc.c000066400000000000000000000025331355240505200213240ustar00rootroot00000000000000/* * Copyright (c) 1999-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #ifndef HAVE___PROGNAME char *__progname; #endif #if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) const char *strerror(int e) { extern int sys_nerr; extern char *sys_errlist[]; if ((e >= 0) && (e < sys_nerr)) return (sys_errlist[e]); return ("unlisted error"); } #endif #ifndef HAVE_STRDUP char * strdup(const char *str) { size_t len; char *cp; len = strlen(str) + 1; cp = malloc(len); if (cp != NULL) return(memcpy(cp, str, len)); return NULL; } #endif OpenSMTPD-extras-6.6.0/openbsd-compat/bsd-misc.h000066400000000000000000000020541355240505200213270ustar00rootroot00000000000000/* $Id: bsd-misc.h,v 1.25 2013/08/04 11:48:41 dtucker Exp $ */ /* * Copyright (c) 1999-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BSD_MISC_H #define _BSD_MISC_H #include "includes.h" #if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) const char *strerror(int); #endif #endif /* _BSD_MISC_H */ OpenSMTPD-extras-6.6.0/openbsd-compat/chacha_private.h000066400000000000000000000124241355240505200225710ustar00rootroot00000000000000/* chacha-merged.c version 20080118 D. J. Bernstein Public domain. */ /* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */ typedef unsigned char u8; typedef unsigned int u32; typedef struct { u32 input[16]; /* could be compressed */ } chacha_ctx; #define U8C(v) (v##U) #define U32C(v) (v##U) #define U8V(v) ((u8)(v) & U8C(0xFF)) #define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) #define ROTL32(v, n) \ (U32V((v) << (n)) | ((v) >> (32 - (n)))) #define U8TO32_LITTLE(p) \ (((u32)((p)[0]) ) | \ ((u32)((p)[1]) << 8) | \ ((u32)((p)[2]) << 16) | \ ((u32)((p)[3]) << 24)) #define U32TO8_LITTLE(p, v) \ do { \ (p)[0] = U8V((v) ); \ (p)[1] = U8V((v) >> 8); \ (p)[2] = U8V((v) >> 16); \ (p)[3] = U8V((v) >> 24); \ } while (0) #define ROTATE(v,c) (ROTL32(v,c)) #define XOR(v,w) ((v) ^ (w)) #define PLUS(v,w) (U32V((v) + (w))) #define PLUSONE(v) (PLUS((v),1)) #define QUARTERROUND(a,b,c,d) \ a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); static const char sigma[16] = "expand 32-byte k"; static const char tau[16] = "expand 16-byte k"; static void chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits) { const char *constants; x->input[4] = U8TO32_LITTLE(k + 0); x->input[5] = U8TO32_LITTLE(k + 4); x->input[6] = U8TO32_LITTLE(k + 8); x->input[7] = U8TO32_LITTLE(k + 12); if (kbits == 256) { /* recommended */ k += 16; constants = sigma; } else { /* kbits == 128 */ constants = tau; } x->input[8] = U8TO32_LITTLE(k + 0); x->input[9] = U8TO32_LITTLE(k + 4); x->input[10] = U8TO32_LITTLE(k + 8); x->input[11] = U8TO32_LITTLE(k + 12); x->input[0] = U8TO32_LITTLE(constants + 0); x->input[1] = U8TO32_LITTLE(constants + 4); x->input[2] = U8TO32_LITTLE(constants + 8); x->input[3] = U8TO32_LITTLE(constants + 12); } static void chacha_ivsetup(chacha_ctx *x,const u8 *iv) { x->input[12] = 0; x->input[13] = 0; x->input[14] = U8TO32_LITTLE(iv + 0); x->input[15] = U8TO32_LITTLE(iv + 4); } static void chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) { u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; u8 *ctarget = NULL; u8 tmp[64]; unsigned int i; if (!bytes) return; j0 = x->input[0]; j1 = x->input[1]; j2 = x->input[2]; j3 = x->input[3]; j4 = x->input[4]; j5 = x->input[5]; j6 = x->input[6]; j7 = x->input[7]; j8 = x->input[8]; j9 = x->input[9]; j10 = x->input[10]; j11 = x->input[11]; j12 = x->input[12]; j13 = x->input[13]; j14 = x->input[14]; j15 = x->input[15]; for (;;) { if (bytes < 64) { for (i = 0;i < bytes;++i) tmp[i] = m[i]; m = tmp; ctarget = c; c = tmp; } x0 = j0; x1 = j1; x2 = j2; x3 = j3; x4 = j4; x5 = j5; x6 = j6; x7 = j7; x8 = j8; x9 = j9; x10 = j10; x11 = j11; x12 = j12; x13 = j13; x14 = j14; x15 = j15; for (i = 20;i > 0;i -= 2) { QUARTERROUND( x0, x4, x8,x12) QUARTERROUND( x1, x5, x9,x13) QUARTERROUND( x2, x6,x10,x14) QUARTERROUND( x3, x7,x11,x15) QUARTERROUND( x0, x5,x10,x15) QUARTERROUND( x1, x6,x11,x12) QUARTERROUND( x2, x7, x8,x13) QUARTERROUND( x3, x4, x9,x14) } x0 = PLUS(x0,j0); x1 = PLUS(x1,j1); x2 = PLUS(x2,j2); x3 = PLUS(x3,j3); x4 = PLUS(x4,j4); x5 = PLUS(x5,j5); x6 = PLUS(x6,j6); x7 = PLUS(x7,j7); x8 = PLUS(x8,j8); x9 = PLUS(x9,j9); x10 = PLUS(x10,j10); x11 = PLUS(x11,j11); x12 = PLUS(x12,j12); x13 = PLUS(x13,j13); x14 = PLUS(x14,j14); x15 = PLUS(x15,j15); #ifndef KEYSTREAM_ONLY x0 = XOR(x0,U8TO32_LITTLE(m + 0)); x1 = XOR(x1,U8TO32_LITTLE(m + 4)); x2 = XOR(x2,U8TO32_LITTLE(m + 8)); x3 = XOR(x3,U8TO32_LITTLE(m + 12)); x4 = XOR(x4,U8TO32_LITTLE(m + 16)); x5 = XOR(x5,U8TO32_LITTLE(m + 20)); x6 = XOR(x6,U8TO32_LITTLE(m + 24)); x7 = XOR(x7,U8TO32_LITTLE(m + 28)); x8 = XOR(x8,U8TO32_LITTLE(m + 32)); x9 = XOR(x9,U8TO32_LITTLE(m + 36)); x10 = XOR(x10,U8TO32_LITTLE(m + 40)); x11 = XOR(x11,U8TO32_LITTLE(m + 44)); x12 = XOR(x12,U8TO32_LITTLE(m + 48)); x13 = XOR(x13,U8TO32_LITTLE(m + 52)); x14 = XOR(x14,U8TO32_LITTLE(m + 56)); x15 = XOR(x15,U8TO32_LITTLE(m + 60)); #endif j12 = PLUSONE(j12); if (!j12) { j13 = PLUSONE(j13); /* stopping at 2^70 bytes per nonce is user's responsibility */ } U32TO8_LITTLE(c + 0,x0); U32TO8_LITTLE(c + 4,x1); U32TO8_LITTLE(c + 8,x2); U32TO8_LITTLE(c + 12,x3); U32TO8_LITTLE(c + 16,x4); U32TO8_LITTLE(c + 20,x5); U32TO8_LITTLE(c + 24,x6); U32TO8_LITTLE(c + 28,x7); U32TO8_LITTLE(c + 32,x8); U32TO8_LITTLE(c + 36,x9); U32TO8_LITTLE(c + 40,x10); U32TO8_LITTLE(c + 44,x11); U32TO8_LITTLE(c + 48,x12); U32TO8_LITTLE(c + 52,x13); U32TO8_LITTLE(c + 56,x14); U32TO8_LITTLE(c + 60,x15); if (bytes <= 64) { if (bytes < 64) { for (i = 0;i < bytes;++i) ctarget[i] = c[i]; } x->input[12] = j12; x->input[13] = j13; return; } bytes -= 64; c += 64; #ifndef KEYSTREAM_ONLY m += 64; #endif } } OpenSMTPD-extras-6.6.0/openbsd-compat/defines.h000066400000000000000000000135671355240505200212560ustar00rootroot00000000000000/* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _DEFINES_H #define _DEFINES_H /* $Id: defines.h,v 1.181 2014/06/11 19:22:50 dtucker Exp $ */ /* Constants */ #ifndef PATH_MAX # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX # endif #endif /* * Looks like ugly, but MAX_IMSGSIZE equals 16384, * and if we don't care it will overflow for some struct */ #if PATH_MAX > 1024 # undef PATH_MAX # define PATH_MAX 1024 #endif #ifndef UID_MAX #define UID_MAX UINT_MAX #endif #ifndef GID_MAX #define GID_MAX UINT_MAX #endif #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #if defined(HAVE_DECL_O_NONBLOCK) && HAVE_DECL_O_NONBLOCK == 0 # define O_NONBLOCK 00004 /* Non Blocking Open */ #endif #ifndef S_IXUSR # define S_IXUSR 0000100 /* execute/search permission, */ # define S_IXGRP 0000010 /* execute/search permission, */ # define S_IXOTH 0000001 /* execute/search permission, */ # define _S_IWUSR 0000200 /* write permission, */ # define S_IWUSR _S_IWUSR /* write permission, owner */ # define S_IWGRP 0000020 /* write permission, group */ # define S_IWOTH 0000002 /* write permission, other */ # define S_IRUSR 0000400 /* read permission, owner */ # define S_IRGRP 0000040 /* read permission, group */ # define S_IROTH 0000004 /* read permission, other */ # define S_IRWXU 0000700 /* read, write, execute */ # define S_IRWXG 0000070 /* read, write, execute */ # define S_IRWXO 0000007 /* read, write, execute */ #endif /* S_IXUSR */ /* Types */ #ifndef ULLONG_MAX # define ULLONG_MAX ((unsigned long long)-1) #endif #ifndef SIZE_MAX #define SIZE_MAX ((size_t)-1) #endif #if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS) # define ss_family __ss_family #endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */ #ifndef HAVE_SYS_UN_H struct sockaddr_un { short sun_family; /* AF_UNIX */ char sun_path[108]; /* path name (gag) */ }; #endif /* HAVE_SYS_UN_H */ #ifndef HAVE_IN_ADDR_T typedef uint32_t in_addr_t; #endif #ifndef HAVE_IN_PORT_T typedef uint16_t in_port_t; #endif /* Macros */ #ifndef MAX # define MAX(a,b) (((a)>(b))?(a):(b)) # define MIN(a,b) (((a)<(b))?(a):(b)) #endif #if !defined(__GNUC__) || (__GNUC__ < 2) # define __attribute__(x) #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ #ifndef OSSH_ALIGNBYTES #define OSSH_ALIGNBYTES (sizeof(int) - 1) #endif #ifndef __CMSG_ALIGN #define __CMSG_ALIGN(p) (((unsigned int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES) #endif /* Length of the contents of a control message of length len */ #ifndef CMSG_LEN #define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) #endif /* Length of the space taken up by a padded control message of length len */ #ifndef CMSG_SPACE #define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) #endif /* given pointer to struct cmsghdr, return pointer to data */ #ifndef CMSG_DATA #define CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + __CMSG_ALIGN(sizeof(struct cmsghdr))) #endif /* CMSG_DATA */ /* * RFC 2292 requires to check msg_controllen, in case that the kernel returns * an empty list for some reasons. */ #ifndef CMSG_FIRSTHDR #define CMSG_FIRSTHDR(mhdr) \ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ (struct cmsghdr *)(mhdr)->msg_control : \ (struct cmsghdr *)NULL) #endif /* CMSG_FIRSTHDR */ /* Set up BSD-style BYTE_ORDER definition if it isn't there already */ /* XXX: doesn't try to cope with strange byte orders (PDP_ENDIAN) */ #ifndef BYTE_ORDER # ifndef LITTLE_ENDIAN # define LITTLE_ENDIAN 1234 # endif /* LITTLE_ENDIAN */ # ifndef BIG_ENDIAN # define BIG_ENDIAN 4321 # endif /* BIG_ENDIAN */ # ifdef WORDS_BIGENDIAN # define BYTE_ORDER BIG_ENDIAN # else /* WORDS_BIGENDIAN */ # define BYTE_ORDER LITTLE_ENDIAN # endif /* WORDS_BIGENDIAN */ #endif /* BYTE_ORDER */ #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) && \ defined(SYSLOG_R_SAFE_IN_SIGHAND) # define DO_LOG_SAFE_IN_SIGHAND #endif #ifndef IOV_MAX # if defined(_XOPEN_IOV_MAX) # define IOV_MAX _XOPEN_IOV_MAX # elif defined(DEF_IOV_MAX) # define IOV_MAX DEF_IOV_MAX # else # define IOV_MAX 16 # endif #endif /* OpenSMTPD-portable specific entries */ /* From OpenNTPD portable */ #if !defined(SA_LEN) # if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) # define SA_LEN(x) ((x)->sa_len) # else # define SA_LEN(x) ((x)->sa_family == AF_INET6 ? \ sizeof(struct sockaddr_in6) : \ sizeof(struct sockaddr_in)) # endif #endif /* EAI_NODATA is obsolete and may not be defined */ #ifndef EAI_NODATA #define EAI_NODATA EAI_NONAME #endif #endif /* _DEFINES_H */ OpenSMTPD-extras-6.6.0/openbsd-compat/event_asr_run.c000066400000000000000000000043501355240505200224740ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Eric Faurot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include struct event_asr { struct event ev; struct asr_query *async; void (*cb)(struct asr_result *, void *); void *arg; }; struct event_asr * event_asr_run(struct asr_query *, void (*)(struct asr_result *, void *), void *); void event_asr_abort(struct event_asr *); static void event_asr_dispatch(int fd __attribute__((__unused__)), short ev __attribute__((__unused__)), void *arg) { struct event_asr *eva = arg; struct asr_result ar; struct timeval tv; event_del(&eva->ev); if (asr_run(eva->async, &ar)) { eva->cb(&ar, eva->arg); free(eva); } else { event_set(&eva->ev, ar.ar_fd, ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, event_asr_dispatch, eva); tv.tv_sec = ar.ar_timeout / 1000; tv.tv_usec = (ar.ar_timeout % 1000) * 1000; event_add(&eva->ev, &tv); } } struct event_asr * event_asr_run(struct asr_query *async, void (*cb)(struct asr_result *, void *), void *arg) { struct event_asr *eva; struct timeval tv; eva = calloc(1, sizeof *eva); if (eva == NULL) return (NULL); eva->async = async; eva->cb = cb; eva->arg = arg; tv.tv_sec = 0; tv.tv_usec = 0; evtimer_set(&eva->ev, event_asr_dispatch, eva); evtimer_add(&eva->ev, &tv); return (eva); } void event_asr_abort(struct event_asr *eva) { asr_abort(eva->async); event_del(&eva->ev); free(eva); } OpenSMTPD-extras-6.6.0/openbsd-compat/imsg-buffer.c000066400000000000000000000132341355240505200220310ustar00rootroot00000000000000/* $OpenBSD: imsg-buffer.c,v 1.3 2013/11/13 20:40:24 benno Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include "imsg.h" int ibuf_realloc(struct ibuf *, size_t); void ibuf_enqueue(struct msgbuf *, struct ibuf *); void ibuf_dequeue(struct msgbuf *, struct ibuf *); struct ibuf * ibuf_open(size_t len) { struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if ((buf->buf = malloc(len)) == NULL) { free(buf); return (NULL); } buf->size = buf->max = len; buf->fd = -1; return (buf); } struct ibuf * ibuf_dynamic(size_t len, size_t max) { struct ibuf *buf; if (max < len) return (NULL); if ((buf = ibuf_open(len)) == NULL) return (NULL); if (max > 0) buf->max = max; return (buf); } int ibuf_realloc(struct ibuf *buf, size_t len) { unsigned char *b; /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { errno = ENOMEM; return (-1); } b = realloc(buf->buf, buf->wpos + len); if (b == NULL) return (-1); buf->buf = b; buf->size = buf->wpos + len; return (0); } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (-1); memcpy(buf->buf + buf->wpos, data, len); buf->wpos += len; return (0); } void * ibuf_reserve(struct ibuf *buf, size_t len) { void *b; if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (NULL); b = buf->buf + buf->wpos; buf->wpos += len; return (b); } void * ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allowed to seek in already written parts */ if (pos + len > buf->wpos) return (NULL); return (buf->buf + pos); } size_t ibuf_size(struct ibuf *buf) { return (buf->wpos); } size_t ibuf_left(struct ibuf *buf) { return (buf->max - buf->wpos); } void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) { ibuf_enqueue(msgbuf, buf); } int ibuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; } again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } msgbuf_drain(msgbuf, n); return (1); } void ibuf_free(struct ibuf *buf) { free(buf->buf); free(buf); } void msgbuf_init(struct msgbuf *msgbuf) { msgbuf->queued = 0; msgbuf->fd = -1; TAILQ_INIT(&msgbuf->bufs); } void msgbuf_drain(struct msgbuf *msgbuf, size_t n) { struct ibuf *buf, *next; for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; buf = next) { next = TAILQ_NEXT(buf, entry); if (buf->rpos + n >= buf->wpos) { n -= buf->wpos - buf->rpos; ibuf_dequeue(msgbuf, buf); } else { buf->rpos += n; n = 0; } } } void msgbuf_clear(struct msgbuf *msgbuf) { struct ibuf *buf; while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) ibuf_dequeue(msgbuf, buf); } int msgbuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; if (buf->fd != -1) break; } msg.msg_iov = iov; msg.msg_iovlen = i; if (buf != NULL && buf->fd != -1) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = buf->fd; } again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf != NULL && buf->fd != -1) { close(buf->fd); buf->fd = -1; } msgbuf_drain(msgbuf, n); return (1); } void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_REMOVE(&msgbuf->bufs, buf, entry); if (buf->fd != -1) close(buf->fd); msgbuf->queued--; ibuf_free(buf); } OpenSMTPD-extras-6.6.0/openbsd-compat/imsg.c000066400000000000000000000146601355240505200205660ustar00rootroot00000000000000/* $OpenBSD: imsg.c,v 1.5 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include "imsg.h" int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); int available_fds(unsigned int n) { unsigned int i; int ret, fds[256]; if (n > (sizeof(fds)/sizeof(fds[0]))) return (1); ret = 0; for (i = 0; i < n; i++) { fds[i] = -1; if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { ret = 1; break; } } for (i = 0; i < n && fds[i] >= 0; i++) close(fds[i]); return (ret); } void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); memset(&ibuf->r, 0, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); TAILQ_INIT(&ibuf->fds); } ssize_t imsg_read(struct imsgbuf *ibuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n = -1; int fd; struct imsg_fd *ifd; memset(&msg, 0, sizeof(msg)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) return (-1); again: if (available_fds(imsg_fd_overhead + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { errno = EAGAIN; free(ifd); return (-1); } if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EMSGSIZE) goto fail; if (errno != EINTR && errno != EAGAIN) goto fail; goto again; } ibuf->r.wpos += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int i; int j; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { fd = ((int *)CMSG_DATA(cmsg))[i]; if (ifd != NULL) { ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); ifd = NULL; } else close(fd); } } /* we do not handle other ctl data level */ } fail: if (ifd) free(ifd); return (n); } ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) { size_t av, left, datalen; av = ibuf->r.wpos; if (IMSG_HEADER_SIZE > av) return (0); memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { errno = ERANGE; return (-1); } if (imsg->hdr.len > av) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) imsg->fd = imsg_get_fd(ibuf); else imsg->fd = -1; memcpy(imsg->data, ibuf->r.rptr, datalen); if (imsg->hdr.len < av) { left = av - imsg->hdr.len; memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); ibuf->r.wpos = left; } else ibuf->r.wpos = 0; return (datalen + IMSG_HEADER_SIZE); } int imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, int fd, const void *data, uint16_t datalen) { struct ibuf *wbuf; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); if (imsg_add(wbuf, data, datalen) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } int imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { struct ibuf *wbuf; int i, datalen = 0; for (i = 0; i < iovcnt; i++) datalen += iov[i].iov_len; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); for (i = 0; i < iovcnt; i++) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } /* ARGSUSED */ struct ibuf * imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid, uint16_t datalen) { struct ibuf *wbuf; struct imsg_hdr hdr; datalen += IMSG_HEADER_SIZE; if (datalen > MAX_IMSGSIZE) { errno = ERANGE; return (NULL); } hdr.type = type; hdr.flags = 0; hdr.peerid = peerid; if ((hdr.pid = pid) == 0) hdr.pid = ibuf->pid; if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { return (NULL); } if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) return (NULL); return (wbuf); } int imsg_add(struct ibuf *msg, const void *data, uint16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); return (-1); } return (datalen); } void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) { struct imsg_hdr *hdr; hdr = (struct imsg_hdr *)msg->buf; hdr->flags &= ~IMSGF_HASFD; if (msg->fd != -1) hdr->flags |= IMSGF_HASFD; hdr->len = (uint16_t)msg->wpos; ibuf_close(&ibuf->w, msg); } void imsg_free(struct imsg *imsg) { free(imsg->data); } int imsg_get_fd(struct imsgbuf *ibuf) { int fd; struct imsg_fd *ifd; if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) return (-1); fd = ifd->fd; TAILQ_REMOVE(&ibuf->fds, ifd, entry); free(ifd); return (fd); } int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) < 0) return (-1); return (0); } void imsg_clear(struct imsgbuf *ibuf) { int fd; msgbuf_clear(&ibuf->w); while ((fd = imsg_get_fd(ibuf)) != -1) close(fd); } OpenSMTPD-extras-6.6.0/openbsd-compat/imsg.h000066400000000000000000000060441355240505200205700ustar00rootroot00000000000000/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard * Copyright (c) 2006, 2007, 2008 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _IMSG_H_ #define _IMSG_H_ #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; unsigned char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct msgbuf { TAILQ_HEAD(, ibuf) bufs; uint32_t queued; int fd; }; struct ibuf_read { unsigned char buf[IBUF_READ_SIZE]; unsigned char *rptr; size_t wpos; }; struct imsg_fd { TAILQ_ENTRY(imsg_fd) entry; int fd; }; struct imsgbuf { TAILQ_HEAD(, imsg_fd) fds; struct ibuf_read r; struct msgbuf w; int fd; pid_t pid; }; #define IMSGF_HASFD 1 struct imsg_hdr { uint32_t type; uint16_t len; uint16_t flags; uint32_t peerid; uint32_t pid; }; struct imsg { struct imsg_hdr hdr; int fd; void *data; }; /* buffer.c */ struct ibuf *ibuf_open(size_t); struct ibuf *ibuf_dynamic(size_t, size_t); int ibuf_add(struct ibuf *, const void *, size_t); void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t); size_t ibuf_size(struct ibuf *); size_t ibuf_left(struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *); int ibuf_write(struct msgbuf *); void ibuf_free(struct ibuf *); void msgbuf_init(struct msgbuf *); void msgbuf_clear(struct msgbuf *); int msgbuf_write(struct msgbuf *); void msgbuf_drain(struct msgbuf *, size_t); /* imsg.c */ int available_fds(unsigned int); void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const void *, uint16_t); int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t); int imsg_add(struct ibuf *, const void *, uint16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); #endif OpenSMTPD-extras-6.6.0/openbsd-compat/includes.h000066400000000000000000000030251355240505200214330ustar00rootroot00000000000000/* $OpenBSD: includes.h,v 1.54 2006/07/22 20:48:23 stevesk Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file includes most of the needed system headers. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef INCLUDES_H #define INCLUDES_H #include "config.h" #ifndef _GNU_SOURCE #define _GNU_SOURCE /* activate extra prototypes for glibc */ #endif #include #include /* For CMSG_* */ #ifdef HAVE_LIMITS_H # include /* For PATH_MAX */ #endif #ifdef HAVE_BSTRING_H # include #endif #ifdef HAVE_ENDIAN_H # include #endif #ifdef HAVE_PATHS_H # include #endif /* *-*-nto-qnx needs this header for strcasecmp */ #ifdef HAVE_STRINGS_H # include #endif #ifdef USE_PAM #if defined(HAVE_SECURITY_PAM_APPL_H) # include #elif defined (HAVE_PAM_PAM_APPL_H) # include #endif #endif #include /* chl */ #ifdef HAVE_NETDB_H # include #endif /* end of chl*/ #include /* For OPENSSL_VERSION_NUMBER */ #include "defines.h" #include "openbsd-compat.h" #endif /* INCLUDES_H */ OpenSMTPD-extras-6.6.0/openbsd-compat/mktemp.c000066400000000000000000000043121355240505200211150ustar00rootroot00000000000000/* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */ /* Changes: Removed mktemp */ /* $OpenBSD: mktemp.c,v 1.30 2010/03/21 23:09:30 schwarze Exp $ */ /* * Copyright (c) 1996-1998, 2008 Theo de Raadt * Copyright (c) 1997, 2008-2009 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/stdio/mktemp.c */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_STRICT_MKSTEMP) #define TEMPCHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" #define NUM_CHARS (sizeof(TEMPCHARS) - 1) static int mktemp_internal(char *path, int slen) { char *start, *cp, *ep; const char *tempchars = TEMPCHARS; unsigned int r, tries; size_t len; int fd; len = strlen(path); if (len == 0 || slen < 0 || (size_t)slen >= len) { errno = EINVAL; return(-1); } ep = path + len - slen; tries = 1; for (start = ep; start > path && start[-1] == 'X'; start--) { if (tries < INT_MAX / NUM_CHARS) tries *= NUM_CHARS; } tries *= 2; do { for (cp = start; cp != ep; cp++) { r = arc4random_uniform(NUM_CHARS); *cp = tempchars[r]; } fd = open(path, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR); if (fd != -1 || errno != EEXIST) return(fd); } while (--tries); errno = EEXIST; return(-1); } int mkstemp(char *path) { return(mktemp_internal(path, 0)); } #endif /* defined(HAVE_STRICT_MKSTEMP) */ OpenSMTPD-extras-6.6.0/openbsd-compat/openbsd-compat.h000066400000000000000000000061421355240505200225430ustar00rootroot00000000000000/* $Id: openbsd-compat.h,v 1.51 2010/10/07 10:25:29 djm Exp $ */ /* * Copyright (c) 1999-2003 Damien Miller. All rights reserved. * Copyright (c) 2003 Ben Lindstrom. All rights reserved. * Copyright (c) 2002 Tim Rice. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _OPENBSD_COMPAT_H #define _OPENBSD_COMPAT_H #include "includes.h" #include #include #include /* OpenBSD function replacements */ #include "base64.h" #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifndef HAVE_CLOSEFROM void closefrom(int); #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif #ifndef HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t size); #endif #if defined(HAVE_STRICT_MKSTEMP) int mkstemp(char *path); #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim); #endif /* Home grown routines */ #include "bsd-misc.h" #ifndef HAVE_ARC4RANDOM unsigned int arc4random(void); #endif /* !HAVE_ARC4RANDOM */ #ifndef HAVE_ARC4RANDOM_UNIFORM uint32_t arc4random_uniform(uint32_t); #endif #ifndef HAVE_ASPRINTF int asprintf(char **, const char *, ...); #endif /* #include XXX needed? For size_t */ #ifndef HAVE_SNPRINTF int snprintf(char *, size_t, SNPRINTF_CONST char *, ...); #endif #ifndef HAVE_STRTOLL long long strtoll(const char *, char **, int); #endif #ifndef HAVE_STRTOULL unsigned long long strtoull(const char *, char **, int); #endif #ifndef HAVE_STRTONUM long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr); #endif #if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF) # include #endif #ifndef HAVE_VASPRINTF int vasprintf(char **, const char *, va_list); #endif #ifndef HAVE_VSNPRINTF int vsnprintf(char *, size_t, const char *, va_list); #endif #endif /* _OPENBSD_COMPAT_H */ OpenSMTPD-extras-6.6.0/openbsd-compat/strlcat.c000066400000000000000000000034161355240505200213000ustar00rootroot00000000000000/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ #include "includes.h" #ifndef HAVE_STRLCAT #include #include /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif /* !HAVE_STRLCAT */ OpenSMTPD-extras-6.6.0/openbsd-compat/strlcpy.c000066400000000000000000000032241355240505200213210ustar00rootroot00000000000000/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ #include "includes.h" #ifndef HAVE_STRLCPY #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif /* !HAVE_STRLCPY */ OpenSMTPD-extras-6.6.0/openbsd-compat/strsep.c000066400000000000000000000051351355240505200211440ustar00rootroot00000000000000/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* OPENBSD ORIGINAL: lib/libc/string/strsep.c */ #include "includes.h" #if !defined(HAVE_STRSEP) #include #include /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ char * strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } #endif /* !defined(HAVE_STRSEP) */ OpenSMTPD-extras-6.6.0/openbsd-compat/strtonum.c000066400000000000000000000035461355240505200215230ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ #include "includes.h" #ifndef HAVE_STRTONUM #include #include #include #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; char *ep; int error = 0; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) error = INVALID; else { ll = strtoll(numstr, &ep, 10); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #endif /* HAVE_STRTONUM */ OpenSMTPD-extras-6.6.0/openbsd-compat/sys/000077500000000000000000000000001355240505200202725ustar00rootroot00000000000000OpenSMTPD-extras-6.6.0/openbsd-compat/sys/queue.h000066400000000000000000000516361355240505200216020ustar00rootroot00000000000000/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ /* OPENBSD ORIGINAL: sys/sys/queue.h */ #ifndef _FAKE_QUEUE_H_ #define _FAKE_QUEUE_H_ /* * Require for OS/X and other platforms that have old/broken/incomplete * . */ #undef SLIST_HEAD #undef SLIST_HEAD_INITIALIZER #undef SLIST_ENTRY #undef SLIST_FOREACH_PREVPTR #undef SLIST_FIRST #undef SLIST_END #undef SLIST_EMPTY #undef SLIST_NEXT #undef SLIST_FOREACH #undef SLIST_INIT #undef SLIST_INSERT_AFTER #undef SLIST_INSERT_HEAD #undef SLIST_REMOVE_HEAD #undef SLIST_REMOVE #undef SLIST_REMOVE_NEXT #undef LIST_HEAD #undef LIST_HEAD_INITIALIZER #undef LIST_ENTRY #undef LIST_FIRST #undef LIST_END #undef LIST_EMPTY #undef LIST_NEXT #undef LIST_FOREACH #undef LIST_INIT #undef LIST_INSERT_AFTER #undef LIST_INSERT_BEFORE #undef LIST_INSERT_HEAD #undef LIST_REMOVE #undef LIST_REPLACE #undef SIMPLEQ_HEAD #undef SIMPLEQ_HEAD_INITIALIZER #undef SIMPLEQ_ENTRY #undef SIMPLEQ_FIRST #undef SIMPLEQ_END #undef SIMPLEQ_EMPTY #undef SIMPLEQ_NEXT #undef SIMPLEQ_FOREACH #undef SIMPLEQ_INIT #undef SIMPLEQ_INSERT_HEAD #undef SIMPLEQ_INSERT_TAIL #undef SIMPLEQ_INSERT_AFTER #undef SIMPLEQ_REMOVE_HEAD #undef TAILQ_HEAD #undef TAILQ_HEAD_INITIALIZER #undef TAILQ_ENTRY #undef TAILQ_FIRST #undef TAILQ_END #undef TAILQ_NEXT #undef TAILQ_LAST #undef TAILQ_PREV #undef TAILQ_EMPTY #undef TAILQ_FOREACH #undef TAILQ_FOREACH_REVERSE #undef TAILQ_INIT #undef TAILQ_INSERT_HEAD #undef TAILQ_INSERT_TAIL #undef TAILQ_INSERT_AFTER #undef TAILQ_INSERT_BEFORE #undef TAILQ_REMOVE #undef TAILQ_REPLACE #undef CIRCLEQ_HEAD #undef CIRCLEQ_HEAD_INITIALIZER #undef CIRCLEQ_ENTRY #undef CIRCLEQ_FIRST #undef CIRCLEQ_LAST #undef CIRCLEQ_END #undef CIRCLEQ_NEXT #undef CIRCLEQ_PREV #undef CIRCLEQ_EMPTY #undef CIRCLEQ_FOREACH #undef CIRCLEQ_FOREACH_REVERSE #undef CIRCLEQ_INIT #undef CIRCLEQ_INSERT_AFTER #undef CIRCLEQ_INSERT_BEFORE #undef CIRCLEQ_INSERT_HEAD #undef CIRCLEQ_INSERT_TAIL #undef CIRCLEQ_REMOVE #undef CIRCLEQ_REPLACE /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * * A singly-linked list is headed by a single forward pointer. The elements * are singly linked for minimum space and pointer manipulation overhead at * the expense of O(n) removal for arbitrary elements. New elements can be * added to the list after an existing element or at the head of the list. * Elements being removed from the head of the list should use the explicit * macro for this purpose for optimum efficiency. A singly-linked list may * only be traversed in the forward direction. Singly-linked lists are ideal * for applications with large datasets and few or no removals or for * implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ #if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) #define _Q_INVALIDATE(a) (a) = ((void *)-1) #else #define _Q_INVALIDATE(a) #endif /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List access methods. */ #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_END(head) NULL #define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) #define SLIST_FOREACH(var, head, field) \ for((var) = SLIST_FIRST(head); \ (var) != SLIST_END(head); \ (var) = SLIST_NEXT(var, field)) #define SLIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SLIST_FIRST(head); \ (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * Singly-linked List functions. */ #define SLIST_INIT(head) { \ SLIST_FIRST(head) = SLIST_END(head); \ } #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (0) #define SLIST_REMOVE_AFTER(elm, field) do { \ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ } while (0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->slh_first; \ \ while (curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ _Q_INVALIDATE((elm)->field.sle_next); \ } \ } while (0) /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List access methods */ #define LIST_FIRST(head) ((head)->lh_first) #define LIST_END(head) NULL #define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) #define LIST_NEXT(elm, field) ((elm)->field.le_next) #define LIST_FOREACH(var, head, field) \ for((var) = LIST_FIRST(head); \ (var)!= LIST_END(head); \ (var) = LIST_NEXT(var, field)) #define LIST_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = LIST_FIRST(head); \ (var) && ((tvar) = LIST_NEXT(var, field), 1); \ (var) = (tvar)) /* * List functions. */ #define LIST_INIT(head) do { \ LIST_FIRST(head) = LIST_END(head); \ } while (0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) #define LIST_REPLACE(elm, elm2, field) do { \ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ (elm2)->field.le_next->field.le_prev = \ &(elm2)->field.le_next; \ (elm2)->field.le_prev = (elm)->field.le_prev; \ *(elm2)->field.le_prev = (elm2); \ _Q_INVALIDATE((elm)->field.le_prev); \ _Q_INVALIDATE((elm)->field.le_next); \ } while (0) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue access methods. */ #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_END(head) NULL #define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) #define SIMPLEQ_FOREACH(var, head, field) \ for((var) = SIMPLEQ_FIRST(head); \ (var) != SIMPLEQ_END(head); \ (var) = SIMPLEQ_NEXT(var, field)) #define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = SIMPLEQ_FIRST(head); \ (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ (var) = (tvar)) /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (0) #define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (0) /* * Tail queue definitions. */ #define TAILQ_HEAD(name, type) \ struct name { \ struct type *tqh_first; /* first element */ \ struct type **tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define TAILQ_ENTRY(type) \ struct { \ struct type *tqe_next; /* next element */ \ struct type **tqe_prev; /* address of previous next element */ \ } /* * tail queue access methods */ #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_END(head) NULL #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) /* XXX */ #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) #define TAILQ_EMPTY(head) \ (TAILQ_FIRST(head) == TAILQ_END(head)) #define TAILQ_FOREACH(var, head, field) \ for((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head); \ (var) = TAILQ_NEXT(var, field)) #define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = TAILQ_FIRST(head); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_NEXT(var, field), 1); \ (var) = (tvar)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head); \ (var) = TAILQ_PREV(var, headname, field)) #define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = TAILQ_LAST(head, headname); \ (var) != TAILQ_END(head) && \ ((tvar) = TAILQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) #define TAILQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ (elm2)->field.tqe_next->field.tqe_prev = \ &(elm2)->field.tqe_next; \ else \ (head)->tqh_last = &(elm2)->field.tqe_next; \ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ *(elm2)->field.tqe_prev = (elm2); \ _Q_INVALIDATE((elm)->field.tqe_prev); \ _Q_INVALIDATE((elm)->field.tqe_next); \ } while (0) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue access methods */ #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_END(head) ((void *)(head)) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_EMPTY(head) \ (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) #define CIRCLEQ_FOREACH(var, head, field) \ for((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_NEXT(var, field)) #define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ for ((var) = CIRCLEQ_FIRST(head); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ (var) = (tvar)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for((var) = CIRCLEQ_LAST(head); \ (var) != CIRCLEQ_END(head); \ (var) = CIRCLEQ_PREV(var, field)) #define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ for ((var) = CIRCLEQ_LAST(head, headname); \ (var) != CIRCLEQ_END(head) && \ ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ (var) = (tvar)) /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = CIRCLEQ_END(head); \ (head)->cqh_last = CIRCLEQ_END(head); \ } while (0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = CIRCLEQ_END(head); \ if ((head)->cqh_last == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = CIRCLEQ_END(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ CIRCLEQ_END(head)) \ (head).cqh_last = (elm2); \ else \ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ CIRCLEQ_END(head)) \ (head).cqh_first = (elm2); \ else \ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ _Q_INVALIDATE((elm)->field.cqe_prev); \ _Q_INVALIDATE((elm)->field.cqe_next); \ } while (0) #endif /* !_FAKE_QUEUE_H_ */ OpenSMTPD-extras-6.6.0/openbsd-compat/sys/tree.h000066400000000000000000000612651355240505200214140ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ /* * Copyright 2002 Niels Provos * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* OPENBSD ORIGINAL: sys/sys/tree.h */ #include "config.h" #ifdef NO_ATTRIBUTE_ON_RETURN_TYPE # define __attribute__(x) #endif #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ /* * This file defines data structures for different types of trees: * splay trees and red-black trees. * * A splay tree is a self-organizing data structure. Every operation * on the tree causes a splay to happen. The splay moves the requested * node to the root of the tree and partly rebalances it. * * This has the benefit that request locality causes faster lookups as * the requested nodes move to the top of the tree. On the other hand, * every lookup causes memory writes. * * The Balance Theorem bounds the total access time for m operations * and n inserts on an initially empty tree as O((m + n)lg n). The * amortized cost for a sequence of m accesses to a splay tree is O(lg n); * * A red-black tree is a binary search tree with the node color as an * extra attribute. It fulfills a set of conditions: * - every search path from the root to a leaf consists of the * same number of black nodes, * - each red node (except for the root) has a black parent, * - each leaf node is black. * * Every operation on a red-black tree is bounded as O(lg n). * The maximum height of a red-black tree is 2lg (n+1). */ #define SPLAY_HEAD(name, type) \ struct name { \ struct type *sph_root; /* root of the tree */ \ } #define SPLAY_INITIALIZER(root) \ { NULL } #define SPLAY_INIT(root) do { \ (root)->sph_root = NULL; \ } while (0) #define SPLAY_ENTRY(type) \ struct { \ struct type *spe_left; /* left element */ \ struct type *spe_right; /* right element */ \ } #define SPLAY_LEFT(elm, field) (elm)->field.spe_left #define SPLAY_RIGHT(elm, field) (elm)->field.spe_right #define SPLAY_ROOT(head) (head)->sph_root #define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) /* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ #define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ (head)->sph_root = tmp; \ } while (0) #define SPLAY_LINKLEFT(head, tmp, field) do { \ SPLAY_LEFT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ } while (0) #define SPLAY_LINKRIGHT(head, tmp, field) do { \ SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ tmp = (head)->sph_root; \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ } while (0) #define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ } while (0) /* Generates prototypes and inline functions */ #define SPLAY_PROTOTYPE(name, type, field, cmp) \ void name##_SPLAY(struct name *, struct type *); \ void name##_SPLAY_MINMAX(struct name *, int); \ struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ \ /* Finds the node with the same key as elm */ \ static __inline struct type * \ name##_SPLAY_FIND(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) \ return(NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) \ return (head->sph_root); \ return (NULL); \ } \ \ static __inline struct type * \ name##_SPLAY_NEXT(struct name *head, struct type *elm) \ { \ name##_SPLAY(head, elm); \ if (SPLAY_RIGHT(elm, field) != NULL) { \ elm = SPLAY_RIGHT(elm, field); \ while (SPLAY_LEFT(elm, field) != NULL) { \ elm = SPLAY_LEFT(elm, field); \ } \ } else \ elm = NULL; \ return (elm); \ } \ \ static __inline struct type * \ name##_SPLAY_MIN_MAX(struct name *head, int val) \ { \ name##_SPLAY_MINMAX(head, val); \ return (SPLAY_ROOT(head)); \ } /* Main splay operation. * Moves node close to the key of elm to top */ #define SPLAY_GENERATE(name, type, field, cmp) \ struct type * \ name##_SPLAY_INSERT(struct name *head, struct type *elm) \ { \ if (SPLAY_EMPTY(head)) { \ SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ } else { \ int __comp; \ name##_SPLAY(head, elm); \ __comp = (cmp)(elm, (head)->sph_root); \ if(__comp < 0) { \ SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ SPLAY_RIGHT(elm, field) = (head)->sph_root; \ SPLAY_LEFT((head)->sph_root, field) = NULL; \ } else if (__comp > 0) { \ SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ SPLAY_LEFT(elm, field) = (head)->sph_root; \ SPLAY_RIGHT((head)->sph_root, field) = NULL; \ } else \ return ((head)->sph_root); \ } \ (head)->sph_root = (elm); \ return (NULL); \ } \ \ struct type * \ name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ { \ struct type *__tmp; \ if (SPLAY_EMPTY(head)) \ return (NULL); \ name##_SPLAY(head, elm); \ if ((cmp)(elm, (head)->sph_root) == 0) { \ if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ } else { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ name##_SPLAY(head, elm); \ SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ } \ return (elm); \ } \ return (NULL); \ } \ \ void \ name##_SPLAY(struct name *head, struct type *elm) \ { \ struct type __node, *__left, *__right, *__tmp; \ int __comp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while ((__comp = (cmp)(elm, (head)->sph_root))) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if ((cmp)(elm, __tmp) > 0){ \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } \ \ /* Splay with either the minimum or the maximum element \ * Used to find minimum or maximum element in tree. \ */ \ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ { \ struct type __node, *__left, *__right, *__tmp; \ \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ __left = __right = &__node; \ \ while (1) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp < 0){ \ SPLAY_ROTATE_RIGHT(head, __tmp, field); \ if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKLEFT(head, __right, field); \ } else if (__comp > 0) { \ __tmp = SPLAY_RIGHT((head)->sph_root, field); \ if (__tmp == NULL) \ break; \ if (__comp > 0) { \ SPLAY_ROTATE_LEFT(head, __tmp, field); \ if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ break; \ } \ SPLAY_LINKRIGHT(head, __left, field); \ } \ } \ SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ } #define SPLAY_NEGINF -1 #define SPLAY_INF 1 #define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) #define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) #define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) #define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) #define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) #define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) #define SPLAY_FOREACH(x, name, head) \ for ((x) = SPLAY_MIN(name, head); \ (x) != NULL; \ (x) = SPLAY_NEXT(name, head, x)) /* Macros that define a red-black tree */ #define RB_HEAD(name, type) \ struct name { \ struct type *rbh_root; /* root of the tree */ \ } #define RB_INITIALIZER(root) \ { NULL } #define RB_INIT(root) do { \ (root)->rbh_root = NULL; \ } while (0) #define RB_BLACK 0 #define RB_RED 1 #define RB_ENTRY(type) \ struct { \ struct type *rbe_left; /* left element */ \ struct type *rbe_right; /* right element */ \ struct type *rbe_parent; /* parent element */ \ int rbe_color; /* node color */ \ } #define RB_LEFT(elm, field) (elm)->field.rbe_left #define RB_RIGHT(elm, field) (elm)->field.rbe_right #define RB_PARENT(elm, field) (elm)->field.rbe_parent #define RB_COLOR(elm, field) (elm)->field.rbe_color #define RB_ROOT(head) (head)->rbh_root #define RB_EMPTY(head) (RB_ROOT(head) == NULL) #define RB_SET(elm, parent, field) do { \ RB_PARENT(elm, field) = parent; \ RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ RB_COLOR(elm, field) = RB_RED; \ } while (0) #define RB_SET_BLACKRED(black, red, field) do { \ RB_COLOR(black, field) = RB_BLACK; \ RB_COLOR(red, field) = RB_RED; \ } while (0) #ifndef RB_AUGMENT #define RB_AUGMENT(x) do {} while (0) #endif #define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ (tmp) = RB_RIGHT(elm, field); \ if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_LEFT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) #define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ (tmp) = RB_LEFT(elm, field); \ if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ } \ RB_AUGMENT(elm); \ if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ else \ RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ } else \ (head)->rbh_root = (tmp); \ RB_RIGHT(tmp, field) = (elm); \ RB_PARENT(elm, field) = (tmp); \ RB_AUGMENT(tmp); \ if ((RB_PARENT(tmp, field))) \ RB_AUGMENT(RB_PARENT(tmp, field)); \ } while (0) /* Generates prototypes and inline functions */ #define RB_PROTOTYPE(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) #define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ attr struct type *name##_RB_INSERT(struct name *, struct type *); \ attr struct type *name##_RB_FIND(struct name *, struct type *); \ attr struct type *name##_RB_NFIND(struct name *, struct type *); \ attr struct type *name##_RB_NEXT(struct type *); \ attr struct type *name##_RB_PREV(struct type *); \ attr struct type *name##_RB_MINMAX(struct name *, int); \ \ /* Main rb operation. * Moves node close to the key of elm to top */ #define RB_GENERATE(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp,) #define RB_GENERATE_STATIC(name, type, field, cmp) \ RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) #define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ attr void \ name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ { \ struct type *parent, *gparent, *tmp; \ while ((parent = RB_PARENT(elm, field)) && \ RB_COLOR(parent, field) == RB_RED) { \ gparent = RB_PARENT(parent, field); \ if (parent == RB_LEFT(gparent, field)) { \ tmp = RB_RIGHT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_RIGHT(parent, field) == elm) { \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_RIGHT(head, gparent, tmp, field); \ } else { \ tmp = RB_LEFT(gparent, field); \ if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ RB_COLOR(tmp, field) = RB_BLACK; \ RB_SET_BLACKRED(parent, gparent, field);\ elm = gparent; \ continue; \ } \ if (RB_LEFT(parent, field) == elm) { \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = parent; \ parent = elm; \ elm = tmp; \ } \ RB_SET_BLACKRED(parent, gparent, field); \ RB_ROTATE_LEFT(head, gparent, tmp, field); \ } \ } \ RB_COLOR(head->rbh_root, field) = RB_BLACK; \ } \ \ attr void \ name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ { \ struct type *tmp; \ while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ elm != RB_ROOT(head)) { \ if (RB_LEFT(parent, field) == elm) { \ tmp = RB_RIGHT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_LEFT(head, parent, tmp, field);\ tmp = RB_RIGHT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ struct type *oleft; \ if ((oleft = RB_LEFT(tmp, field)))\ RB_COLOR(oleft, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_RIGHT(head, tmp, oleft, field);\ tmp = RB_RIGHT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_RIGHT(tmp, field)) \ RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_LEFT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } else { \ tmp = RB_LEFT(parent, field); \ if (RB_COLOR(tmp, field) == RB_RED) { \ RB_SET_BLACKRED(tmp, parent, field); \ RB_ROTATE_RIGHT(head, parent, tmp, field);\ tmp = RB_LEFT(parent, field); \ } \ if ((RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ (RB_RIGHT(tmp, field) == NULL || \ RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ RB_COLOR(tmp, field) = RB_RED; \ elm = parent; \ parent = RB_PARENT(elm, field); \ } else { \ if (RB_LEFT(tmp, field) == NULL || \ RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ struct type *oright; \ if ((oright = RB_RIGHT(tmp, field)))\ RB_COLOR(oright, field) = RB_BLACK;\ RB_COLOR(tmp, field) = RB_RED; \ RB_ROTATE_LEFT(head, tmp, oright, field);\ tmp = RB_LEFT(parent, field); \ } \ RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ RB_COLOR(parent, field) = RB_BLACK; \ if (RB_LEFT(tmp, field)) \ RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ RB_ROTATE_RIGHT(head, parent, tmp, field);\ elm = RB_ROOT(head); \ break; \ } \ } \ } \ if (elm) \ RB_COLOR(elm, field) = RB_BLACK; \ } \ \ attr struct type * \ name##_RB_REMOVE(struct name *head, struct type *elm) \ { \ struct type *child, *parent, *old = elm; \ int color; \ if (RB_LEFT(elm, field) == NULL) \ child = RB_RIGHT(elm, field); \ else if (RB_RIGHT(elm, field) == NULL) \ child = RB_LEFT(elm, field); \ else { \ struct type *left; \ elm = RB_RIGHT(elm, field); \ while ((left = RB_LEFT(elm, field))) \ elm = left; \ child = RB_RIGHT(elm, field); \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ if (RB_PARENT(elm, field) == old) \ parent = elm; \ (elm)->field = (old)->field; \ if (RB_PARENT(old, field)) { \ if (RB_LEFT(RB_PARENT(old, field), field) == old)\ RB_LEFT(RB_PARENT(old, field), field) = elm;\ else \ RB_RIGHT(RB_PARENT(old, field), field) = elm;\ RB_AUGMENT(RB_PARENT(old, field)); \ } else \ RB_ROOT(head) = elm; \ RB_PARENT(RB_LEFT(old, field), field) = elm; \ if (RB_RIGHT(old, field)) \ RB_PARENT(RB_RIGHT(old, field), field) = elm; \ if (parent) { \ left = parent; \ do { \ RB_AUGMENT(left); \ } while ((left = RB_PARENT(left, field))); \ } \ goto color; \ } \ parent = RB_PARENT(elm, field); \ color = RB_COLOR(elm, field); \ if (child) \ RB_PARENT(child, field) = parent; \ if (parent) { \ if (RB_LEFT(parent, field) == elm) \ RB_LEFT(parent, field) = child; \ else \ RB_RIGHT(parent, field) = child; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = child; \ color: \ if (color == RB_BLACK) \ name##_RB_REMOVE_COLOR(head, parent, child); \ return (old); \ } \ \ /* Inserts a node into the RB tree */ \ attr struct type * \ name##_RB_INSERT(struct name *head, struct type *elm) \ { \ struct type *tmp; \ struct type *parent = NULL; \ int comp = 0; \ tmp = RB_ROOT(head); \ while (tmp) { \ parent = tmp; \ comp = (cmp)(elm, parent); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ RB_SET(elm, parent, field); \ if (parent != NULL) { \ if (comp < 0) \ RB_LEFT(parent, field) = elm; \ else \ RB_RIGHT(parent, field) = elm; \ RB_AUGMENT(parent); \ } else \ RB_ROOT(head) = elm; \ name##_RB_INSERT_COLOR(head, elm); \ return (NULL); \ } \ \ /* Finds the node with the same key as elm */ \ attr struct type * \ name##_RB_FIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) \ tmp = RB_LEFT(tmp, field); \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (NULL); \ } \ \ /* Finds the first node greater than or equal to the search key */ \ attr struct type * \ name##_RB_NFIND(struct name *head, struct type *elm) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *res = NULL; \ int comp; \ while (tmp) { \ comp = cmp(elm, tmp); \ if (comp < 0) { \ res = tmp; \ tmp = RB_LEFT(tmp, field); \ } \ else if (comp > 0) \ tmp = RB_RIGHT(tmp, field); \ else \ return (tmp); \ } \ return (res); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_NEXT(struct type *elm) \ { \ if (RB_RIGHT(elm, field)) { \ elm = RB_RIGHT(elm, field); \ while (RB_LEFT(elm, field)) \ elm = RB_LEFT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ /* ARGSUSED */ \ attr struct type * \ name##_RB_PREV(struct type *elm) \ { \ if (RB_LEFT(elm, field)) { \ elm = RB_LEFT(elm, field); \ while (RB_RIGHT(elm, field)) \ elm = RB_RIGHT(elm, field); \ } else { \ if (RB_PARENT(elm, field) && \ (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ elm = RB_PARENT(elm, field); \ else { \ while (RB_PARENT(elm, field) && \ (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ elm = RB_PARENT(elm, field); \ elm = RB_PARENT(elm, field); \ } \ } \ return (elm); \ } \ \ attr struct type * \ name##_RB_MINMAX(struct name *head, int val) \ { \ struct type *tmp = RB_ROOT(head); \ struct type *parent = NULL; \ while (tmp) { \ parent = tmp; \ if (val < 0) \ tmp = RB_LEFT(tmp, field); \ else \ tmp = RB_RIGHT(tmp, field); \ } \ return (parent); \ } #define RB_NEGINF -1 #define RB_INF 1 #define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) #define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) #define RB_FIND(name, x, y) name##_RB_FIND(x, y) #define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) #define RB_NEXT(name, x, y) name##_RB_NEXT(y) #define RB_PREV(name, x, y) name##_RB_PREV(y) #define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) #define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) #define RB_FOREACH(x, name, head) \ for ((x) = RB_MIN(name, head); \ (x) != NULL; \ (x) = name##_RB_NEXT(x)) #define RB_FOREACH_SAFE(x, name, head, y) \ for ((x) = RB_MIN(name, head); \ ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ (x) = (y)) #define RB_FOREACH_REVERSE(x, name, head) \ for ((x) = RB_MAX(name, head); \ (x) != NULL; \ (x) = name##_RB_PREV(x)) #define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ for ((x) = RB_MAX(name, head); \ ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ (x) = (y)) #endif /* _SYS_TREE_H_ */