pax_global_header00006660000000000000000000000064147116660320014520gustar00rootroot0000000000000052 comment=57e56d06c15e7729525eded25493cc38f8e3e180 OpenSMTPD-table-mysql-e976fa1/000077500000000000000000000000001471166603200161055ustar00rootroot00000000000000OpenSMTPD-table-mysql-e976fa1/.gitignore000066400000000000000000000003061471166603200200740ustar00rootroot00000000000000**/*~ **/*.d **/*.o **/.deps **/Makefile **/Makefile.in **/aclocal.m4 **/.dirstamp **/autom4te.cache **/config.h **/config.h.in **/config.log **/config.status **/configure **/stamp-h1 table-mysql OpenSMTPD-table-mysql-e976fa1/Makefile.am000066400000000000000000000011261471166603200201410ustar00rootroot00000000000000SUBDIRS = openbsd-compat noinst_PROGRAMS = table-mysql table_mysql_SOURCES = table_mysql.c dict.c log.c table_stdio.c util.c LDADD = $(LIBOBJS) dist_man5_MANS = table-mysql.5 EXTRA_DIST = README.md compat.h config.h.in dict.h log.h \ table_stdio.h util.h smtpdir = ${prefix}/libexec/smtpd install-exec-local: $(noinst_PROGRAMS) $(MKDIR_P) $(DESTDIR)$(smtpdir) $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $(noinst_PROGRAMS) $(DESTDIR)$(smtpdir) uninstall-local: rm $(DESTDIR)$(smtpdir)/$(noinst_PROGRAMS) regen-readme: mandoc -Tmarkdown -l ${top_srcdir}/table-mysql.5 > README.md OpenSMTPD-table-mysql-e976fa1/README.md000066400000000000000000000140601471166603200173650ustar00rootroot00000000000000TABLE\_MYSQL(5) - File Formats Manual # NAME **table\_mysql** - format description for smtpd MySQL or MariaDB tables # DESCRIPTION This manual page documents the file format of MySQL or MariaDB tables used by the smtpd(8) mail daemon. The format described here applies to tables as defined in smtpd.conf(5). # 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 mysql(1). The table is used by 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. A MySQL table consists of one or more mysql(1) databases with one or more tables. If the table is used for authentication, the password should be encrypted using the crypt(3) function. Such passwords can be generated using the encrypt(1) utility or smtpctl(8) encrypt command. # MYSQL TABLE CONFIG FILE The following configuration options are available: **host** *hostname* > This is the host running MySQL or MariaDB. > For example: > > host db.example.com **username** *username* > The username required to talk to the MySQL or MariaDB database. > For example: > > username maildba **password** *password* > The password required to talk to the MySQL or MariaDB database. > For example: > > password OpenSMTPDRules! **database** *databse* > The name of the MySQL or MariaDB database. > For example: > > databse opensmtpdb **query\_alias** *SQL statement* > 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. **query\_credentials** *SQL statement* > 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 > crypt(3) > format. **query\_domain** *SQL statement* > 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. **query\_mailaddrmap** *SQL statement* > 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. A generic SQL statement would be something like: query_ SELECT value FROM table WHERE key=?; # EXAMPLES ## 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: # useradd -g =uid -c "Virtual Mail" -d /var/vmail -s /sbin/nologin vmail # mkdir /var/vmail # chown vmail:vmail /var/vmail *MySQL schema* 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"); */etc/mail/mysql.conf* 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=?; */etc/mail/smtpd.conf* 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 ## MOVING FROM POSTFIX (& POSTFIXADMIN) */etc/mail/mysql.conf* 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=?; The rest of the config remains the same. # FILES */etc/mail/mysql.conf* > Default > table-mysql(8) > configuration file. # TODO Documenting the following query options: **query_netaddr** **query_userinfo** **query_source** **query_mailaddr** **query_addrname** # SEE ALSO smtpd.conf(5), smtpctl(8), smtpd(8), encrypt(1), crypt(3) OpenBSD 7.5 - July 4, 2016 OpenSMTPD-table-mysql-e976fa1/bootstrap000077500000000000000000000000341471166603200200450ustar00rootroot00000000000000#! /bin/sh autoreconf -vfi OpenSMTPD-table-mysql-e976fa1/compat.h000066400000000000000000000012521471166603200175410ustar00rootroot00000000000000#include "config.h" #include #include #ifndef UID_MAX #define UID_MAX UINT_MAX #endif #ifndef GID_MAX #define GID_MAX UINT_MAX #endif #ifndef __dead #define __dead __attribute__((noreturn)) #endif #ifndef HAVE_ASPRINTF int asprintf(char **, const char *, ...); #endif #ifndef HAVE_GETPROGNAME const char *getprogname(void); #endif #ifndef HAVE_STRLCAT size_t strlcat(char *, const char *, size_t); #endif #ifndef HAVE_STRLCPY size_t strlcpy(char *, const char *, size_t); #endif #ifndef HAVE_STRSEP char *strsep(char **, const char *); #endif #ifndef HAVE_STRTONUM long long strtonum(const char *, long long, long long, const char **); #endif OpenSMTPD-table-mysql-e976fa1/configure.ac000066400000000000000000000014671471166603200204030ustar00rootroot00000000000000AC_INIT([table-mysql], [1.2.1], [bugs@opensmtpd.org]) AC_CONFIG_AUX_DIR(etc) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AC_CONFIG_LIBOBJ_DIR(openbsd-compat) AC_PROG_CC AC_USE_SYSTEM_EXTENSIONS AC_REPLACE_FUNCS([ \ asprintf \ getprogname \ err \ strlcat \ strlcpy \ strsep \ strtonum \ ]) AC_SEARCH_LIBS([mysql_init], [mariadb], [], [ AS_IF([command -v mysql_config >/dev/null], [ AC_MSG_RESULT([autodetect with mysql_config]) CFLAGS="$CFLAGS $(mysql_config --cflags)" LDFLAGS="$LDFLAGS $(mysql_config --libs)" ], [ AC_MSG_ERROR([requires MariaDB client library]) ]) ]) CFLAGS="$CFLAGS -I$srcdir/openbsd-compat" AC_CHECK_HEADER([sys/tree.h], [], [ CFLAGS="$CFLAGS -I$srcdir/openbsd-compat/tree" ]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ Makefile openbsd-compat/Makefile ]) AC_OUTPUT OpenSMTPD-table-mysql-e976fa1/dict.c000066400000000000000000000123351471166603200172000ustar00rootroot00000000000000/* $OpenBSD: dict.c,v 1.8 2021/06/14 17:58:15 eric Exp $ */ /* * 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 "compat.h" #include #include #include #include #include "dict.h" #include "log.h" 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) fatal("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) fatal("dict_xset: malloc"); if (SPLAY_INSERT(_dict, &d->dict, entry)) fatalx("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) fatalx("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) fatalx("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)) fatalx("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-table-mysql-e976fa1/dict.h000066400000000000000000000034511471166603200172040ustar00rootroot00000000000000/* $OpenBSD: dict.h,v 1.1 2018/12/23 16:06:24 gilles Exp $ */ /* * 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 _DICT_H_ #define _DICT_H_ SPLAY_HEAD(_dict, dictentry); struct dict { struct _dict dict; size_t count; }; /* 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 *); #endif OpenSMTPD-table-mysql-e976fa1/log.c000066400000000000000000000072331471166603200170370ustar00rootroot00000000000000/* $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 "compat.h" #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) { foreground = n_foreground; if (!foreground) openlog(getprogname(), LOG_PID | LOG_NDELAY, LOG_MAIL); tzset(); } void log_setverbose(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) { char *nfmt; if (foreground) { /* best effort in out of mem situations */ if (asprintf(&nfmt, "%s[%u]: %s\n", getprogname(), getpid(), fmt) == -1) { fprintf(stderr, "%s[%u]: ", getprogname(), 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-table-mysql-e976fa1/log.h000066400000000000000000000032621471166603200170420ustar00rootroot00000000000000/* $OpenBSD: log.h,v 1.9 2021/12/13 18:28:40 deraadt 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. */ #ifndef LOG_H #define LOG_H #include void log_init(int); void log_procinit(const char *); void log_setverbose(int); int log_getverbose(void); 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 logit(int, const char *, ...) __attribute__((__format__ (printf, 2, 3))); void vlog(int, const char *, va_list) __attribute__((__format__ (printf, 2, 0))); __dead void fatal(const char *, ...) __attribute__((__format__ (printf, 1, 2))); __dead void fatalx(const char *, ...) __attribute__((__format__ (printf, 1, 2))); #endif /* LOG_H */ OpenSMTPD-table-mysql-e976fa1/openbsd-compat/000077500000000000000000000000001471166603200210205ustar00rootroot00000000000000OpenSMTPD-table-mysql-e976fa1/openbsd-compat/Makefile.am000066400000000000000000000000431471166603200230510ustar00rootroot00000000000000EXTRA_DIST = err.h tree/sys/tree.h OpenSMTPD-table-mysql-e976fa1/openbsd-compat/asprintf.c000066400000000000000000000027011471166603200230120ustar00rootroot00000000000000/* * Copyright (c) 2006 Nicholas Marriott * * 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 "../compat.h" #include #include #include #include #include static int vasprintf(char **ret, const char *fmt, va_list ap) { int n; va_list ap2; va_copy(ap2, ap); if ((n = vsnprintf(NULL, 0, fmt, ap)) < 0) goto error; if ((*ret = malloc(n + 1)) == NULL) goto error; if ((n = vsnprintf(*ret, n + 1, fmt, ap2)) < 0) { free(*ret); goto error; } va_end(ap2); return (n); error: va_end(ap2); *ret = NULL; return (-1); } int asprintf(char **ret, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = vasprintf(ret, fmt, ap); va_end(ap); return (n); } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/err.c000066400000000000000000000033261471166603200217600ustar00rootroot00000000000000/* * Copyright (c) 2021 Omar Polo * * 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 "compat.h" static void vwarn(const char*, va_list); static void vwarnx(const char*, va_list); static void vwarn(const char *fmt, va_list ap) { fprintf(stderr, "%s: ", getprogname()); vfprintf(stderr, fmt, ap); fprintf(stderr, ": %s\n", strerror(errno)); } static void vwarnx(const char *fmt, va_list ap) { fprintf(stderr, "%s: ", getprogname()); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } void err(int ret, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); exit(ret); } void errx(int ret, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); exit(ret); } void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/err.h000066400000000000000000000003421471166603200217600ustar00rootroot00000000000000#include "../compat.h" #ifdef HAVE_ERR # include_next #else __dead void err(int, const char *, ...); __dead void errx(int, const char *, ...); void warn(const char *, ...); void warnx(const char *, ...); #endif OpenSMTPD-table-mysql-e976fa1/openbsd-compat/getprogname.c000066400000000000000000000020511471166603200234720ustar00rootroot00000000000000/* * Copyright (c) 2021 Omar Polo * * 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 "../compat.h" #if HAVE_PROGRAM_INVOCATION_SHORT_NAME #include extern char *program_invocation_short_name; const char * getprogname(void) { return program_invocation_short_name; } #else const char * getprogname(void) { return "table-passwd"; } #endif OpenSMTPD-table-mysql-e976fa1/openbsd-compat/strlcat.c000066400000000000000000000032401471166603200226370ustar00rootroot00000000000000/* * Copyright (c) 1998, 2015 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 "../compat.h" #include #include /* * Appends src to string dst of size dsize (unlike strncat, dsize is the * full size of dst, not space left). At most dsize-1 characters * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). * Returns strlen(src) + MIN(dsize, strlen(initial dst)). * If retval >= dsize, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t dsize) { const char *odst = dst; const char *osrc = src; size_t n = dsize; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end. */ while (n-- != 0 && *dst != '\0') dst++; dlen = dst - odst; n = dsize - dlen; if (n-- == 0) return(dlen + strlen(src)); while (*src != '\0') { if (n != 0) { *dst++ = *src; n--; } src++; } *dst = '\0'; return(dlen + (src - osrc)); /* count does not include NUL */ } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/strlcpy.c000066400000000000000000000030151471166603200226630ustar00rootroot00000000000000/* * Copyright (c) 1998, 2015 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 "../compat.h" #include #include /* * Copy string src to buffer dst of size dsize. At most dsize-1 * chars will be copied. Always NUL terminates (unless dsize == 0). * Returns strlen(src); if retval >= dsize, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t dsize) { const char *osrc = src; size_t nleft = dsize; /* Copy as many bytes as will fit. */ if (nleft != 0) { while (--nleft != 0) { if ((*dst++ = *src++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src. */ if (nleft == 0) { if (dsize != 0) *dst = '\0'; /* NUL-terminate dst */ while (*src++) ; } return(src - osrc - 1); /* count does not include NUL */ } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/strsep.c000066400000000000000000000050371471166603200225110ustar00rootroot00000000000000/* $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 "../compat.h" #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 */ } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/strtonum.c000066400000000000000000000034041471166603200230600ustar00rootroot00000000000000/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther 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. */ #include "../compat.h" #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; int error = 0; char *ep; 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); } OpenSMTPD-table-mysql-e976fa1/openbsd-compat/tree/000077500000000000000000000000001471166603200217575ustar00rootroot00000000000000OpenSMTPD-table-mysql-e976fa1/openbsd-compat/tree/sys/000077500000000000000000000000001471166603200225755ustar00rootroot00000000000000OpenSMTPD-table-mysql-e976fa1/openbsd-compat/tree/sys/tree.h000066400000000000000000001017461471166603200237160ustar00rootroot00000000000000/* $OpenBSD: tree.h,v 1.30 2020/10/10 18:03:41 otto 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. */ #ifndef _SYS_TREE_H_ #define _SYS_TREE_H_ #include /* * Local modifications: * - dropped __unused * - __inline -> inline */ /* * 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)) /* * Copyright (c) 2016 David Gwynne * * 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 rb_type { int (*t_compare)(const void *, const void *); void (*t_augment)(void *); unsigned int t_offset; /* offset of rb_entry in type */ }; struct rb_tree { struct rb_entry *rbt_root; }; struct rb_entry { struct rb_entry *rbt_parent; struct rb_entry *rbt_left; struct rb_entry *rbt_right; unsigned int rbt_color; }; #define RBT_HEAD(_name, _type) \ struct _name { \ struct rb_tree rbh_root; \ } #define RBT_ENTRY(_type) struct rb_entry static inline void _rb_init(struct rb_tree *rbt) { rbt->rbt_root = NULL; } static inline int _rb_empty(struct rb_tree *rbt) { return (rbt->rbt_root == NULL); } void *_rb_insert(const struct rb_type *, struct rb_tree *, void *); void *_rb_remove(const struct rb_type *, struct rb_tree *, void *); void *_rb_find(const struct rb_type *, struct rb_tree *, const void *); void *_rb_nfind(const struct rb_type *, struct rb_tree *, const void *); void *_rb_root(const struct rb_type *, struct rb_tree *); void *_rb_min(const struct rb_type *, struct rb_tree *); void *_rb_max(const struct rb_type *, struct rb_tree *); void *_rb_next(const struct rb_type *, void *); void *_rb_prev(const struct rb_type *, void *); void *_rb_left(const struct rb_type *, void *); void *_rb_right(const struct rb_type *, void *); void *_rb_parent(const struct rb_type *, void *); void _rb_set_left(const struct rb_type *, void *, void *); void _rb_set_right(const struct rb_type *, void *, void *); void _rb_set_parent(const struct rb_type *, void *, void *); void _rb_poison(const struct rb_type *, void *, unsigned long); int _rb_check(const struct rb_type *, void *, unsigned long); #define RBT_INITIALIZER(_head) { { NULL } } #define RBT_PROTOTYPE(_name, _type, _field, _cmp) \ extern const struct rb_type *const _name##_RBT_TYPE; \ \ static inline void \ _name##_RBT_INIT(struct _name *head) \ { \ _rb_init(&head->rbh_root); \ } \ \ static inline struct _type * \ _name##_RBT_INSERT(struct _name *head, struct _type *elm) \ { \ return _rb_insert(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ static inline struct _type * \ _name##_RBT_REMOVE(struct _name *head, struct _type *elm) \ { \ return _rb_remove(_name##_RBT_TYPE, &head->rbh_root, elm); \ } \ \ static inline struct _type * \ _name##_RBT_FIND(struct _name *head, const struct _type *key) \ { \ return _rb_find(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ static inline struct _type * \ _name##_RBT_NFIND(struct _name *head, const struct _type *key) \ { \ return _rb_nfind(_name##_RBT_TYPE, &head->rbh_root, key); \ } \ \ static inline struct _type * \ _name##_RBT_ROOT(struct _name *head) \ { \ return _rb_root(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ static inline int \ _name##_RBT_EMPTY(struct _name *head) \ { \ return _rb_empty(&head->rbh_root); \ } \ \ static inline struct _type * \ _name##_RBT_MIN(struct _name *head) \ { \ return _rb_min(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ static inline struct _type * \ _name##_RBT_MAX(struct _name *head) \ { \ return _rb_max(_name##_RBT_TYPE, &head->rbh_root); \ } \ \ static inline struct _type * \ _name##_RBT_NEXT(struct _type *elm) \ { \ return _rb_next(_name##_RBT_TYPE, elm); \ } \ \ static inline struct _type * \ _name##_RBT_PREV(struct _type *elm) \ { \ return _rb_prev(_name##_RBT_TYPE, elm); \ } \ \ static inline struct _type * \ _name##_RBT_LEFT(struct _type *elm) \ { \ return _rb_left(_name##_RBT_TYPE, elm); \ } \ \ static inline struct _type * \ _name##_RBT_RIGHT(struct _type *elm) \ { \ return _rb_right(_name##_RBT_TYPE, elm); \ } \ \ static inline struct _type * \ _name##_RBT_PARENT(struct _type *elm) \ { \ return _rb_parent(_name##_RBT_TYPE, elm); \ } \ \ static inline void \ _name##_RBT_SET_LEFT(struct _type *elm, struct _type *left) \ { \ _rb_set_left(_name##_RBT_TYPE, elm, left); \ } \ \ static inline void \ _name##_RBT_SET_RIGHT(struct _type *elm, struct _type *right) \ { \ _rb_set_right(_name##_RBT_TYPE, elm, right); \ } \ \ static inline void \ _name##_RBT_SET_PARENT(struct _type *elm, struct _type *parent) \ { \ _rb_set_parent(_name##_RBT_TYPE, elm, parent); \ } \ \ static inline void \ _name##_RBT_POISON(struct _type *elm, unsigned long poison) \ { \ _rb_poison(_name##_RBT_TYPE, elm, poison); \ } \ \ static inline int \ _name##_RBT_CHECK(struct _type *elm, unsigned long poison) \ { \ return _rb_check(_name##_RBT_TYPE, elm, poison); \ } #define RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _aug) \ static int \ _name##_RBT_COMPARE(const void *lptr, const void *rptr) \ { \ const struct _type *l = lptr, *r = rptr; \ return _cmp(l, r); \ } \ static const struct rb_type _name##_RBT_INFO = { \ _name##_RBT_COMPARE, \ _aug, \ offsetof(struct _type, _field), \ }; \ const struct rb_type *const _name##_RBT_TYPE = &_name##_RBT_INFO #define RBT_GENERATE_AUGMENT(_name, _type, _field, _cmp, _aug) \ static void \ _name##_RBT_AUGMENT(void *ptr) \ { \ struct _type *p = ptr; \ return _aug(p); \ } \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, _name##_RBT_AUGMENT) #define RBT_GENERATE(_name, _type, _field, _cmp) \ RBT_GENERATE_INTERNAL(_name, _type, _field, _cmp, NULL) #define RBT_INIT(_name, _head) _name##_RBT_INIT(_head) #define RBT_INSERT(_name, _head, _elm) _name##_RBT_INSERT(_head, _elm) #define RBT_REMOVE(_name, _head, _elm) _name##_RBT_REMOVE(_head, _elm) #define RBT_FIND(_name, _head, _key) _name##_RBT_FIND(_head, _key) #define RBT_NFIND(_name, _head, _key) _name##_RBT_NFIND(_head, _key) #define RBT_ROOT(_name, _head) _name##_RBT_ROOT(_head) #define RBT_EMPTY(_name, _head) _name##_RBT_EMPTY(_head) #define RBT_MIN(_name, _head) _name##_RBT_MIN(_head) #define RBT_MAX(_name, _head) _name##_RBT_MAX(_head) #define RBT_NEXT(_name, _elm) _name##_RBT_NEXT(_elm) #define RBT_PREV(_name, _elm) _name##_RBT_PREV(_elm) #define RBT_LEFT(_name, _elm) _name##_RBT_LEFT(_elm) #define RBT_RIGHT(_name, _elm) _name##_RBT_RIGHT(_elm) #define RBT_PARENT(_name, _elm) _name##_RBT_PARENT(_elm) #define RBT_SET_LEFT(_name, _elm, _l) _name##_RBT_SET_LEFT(_elm, _l) #define RBT_SET_RIGHT(_name, _elm, _r) _name##_RBT_SET_RIGHT(_elm, _r) #define RBT_SET_PARENT(_name, _elm, _p) _name##_RBT_SET_PARENT(_elm, _p) #define RBT_POISON(_name, _elm, _p) _name##_RBT_POISON(_elm, _p) #define RBT_CHECK(_name, _elm, _p) _name##_RBT_CHECK(_elm, _p) #define RBT_FOREACH(_e, _name, _head) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_NEXT(_name, (_e))) #define RBT_FOREACH_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MIN(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_NEXT(_name, (_e)), 1); \ (_e) = (_n)) #define RBT_FOREACH_REVERSE(_e, _name, _head) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL; \ (_e) = RBT_PREV(_name, (_e))) #define RBT_FOREACH_REVERSE_SAFE(_e, _name, _head, _n) \ for ((_e) = RBT_MAX(_name, (_head)); \ (_e) != NULL && ((_n) = RBT_PREV(_name, (_e)), 1); \ (_e) = (_n)) #endif /* _SYS_TREE_H_ */ OpenSMTPD-table-mysql-e976fa1/shell.nix000066400000000000000000000002641471166603200177360ustar00rootroot00000000000000{ pkgs ? import {} }: pkgs.mkShell { nativeBuildInputs = with pkgs; [ autoreconfHook pkg-config gdb mandoc ]; buildInputs = with pkgs; [ libbsd libmysqlclient ]; } OpenSMTPD-table-mysql-e976fa1/table-mysql.5000066400000000000000000000164401471166603200204320ustar00rootroot00000000000000.\" .\" 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-table-mysql-e976fa1/table_mysql.c000066400000000000000000000315301471166603200205670ustar00rootroot00000000000000/* * 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 "compat.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "dict.h" #include "log.h" #include "table_stdio.h" #include "util.h" 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 }, }; #if !defined(MARIADB_BASE_VERSION) && MYSQL_VERSION_ID >= 80001 bool reconn; #else my_bool reconn; #endif 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, retries = 1; 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)) { log_warnx("warn: trying to reconnect after error: %s", mysql_stmt_error(stmt)); if (config_connect(config) && retries-- > 0) goto retry; if (retries <= 0) log_warnx("warn: to many retries"); 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, retries = 1; 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)) { log_warnx("warn: trying to reconnect after error: %s", mysql_stmt_error(stmt)); if (config_connect(config) && retries-- > 0) goto retry; if (retries <= 0) log_warnx("warn: to many retries"); 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_setverbose(~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-table-mysql-e976fa1/table_stdio.c000066400000000000000000000135371471166603200205530ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2024 Omar Polo * * 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 "config.h" #include #include #include #include #include #include "dict.h" #include "table_stdio.h" 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 char tablename[128]; /* Dummy; just kept for backward compatibility */ static struct dict params; static int service_id(const char *service) { if (!strcmp(service, "alias")) return (K_ALIAS); if (!strcmp(service, "domain")) return (K_DOMAIN); if (!strcmp(service, "credentials")) return (K_CREDENTIALS); if (!strcmp(service, "netaddr")) return (K_NETADDR); if (!strcmp(service, "userinfo")) return (K_USERINFO); if (!strcmp(service, "source")) return (K_SOURCE); if (!strcmp(service, "mailaddr")) return (K_MAILADDR); if (!strcmp(service, "addrname")) return (K_ADDRNAME); if (!strcmp(service, "mailaddrmap")) return (K_MAILADDRMAP); errx(1, "unknown service %s", service); } 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 tablename; } int table_api_dispatch(void) { char buf[LINE_MAX]; char *t, *vers, *tname, *type, *service, *id, *key; char *line = NULL; size_t linesize = 0; ssize_t linelen; int i, r, configured = 0; dict_init(¶ms); while ((linelen = getline(&line, &linesize, stdin)) != -1) { if (line[linelen - 1] == '\n') line[--linelen] = '\0'; t = line; if (!configured) { if (strncmp(t, "config|", 7) != 0) errx(1, "unexpected config line: %s", line); t += 7; if (!strcmp(t, "ready")) { configured = 1; /* * XXX register all the services since * we don't have a clue what the table * will do. */ puts("register|alias"); puts("register|domain"); puts("register|credentials"); puts("register|netaddr"); puts("register|userinfo"); puts("register|source");; puts("register|mailaddr"); puts("register|addrname"); puts("register|mailaddrmap"); puts("register|ready"); if (fflush(stdout) == EOF) err(1, "fflush"); continue; } continue; } if (strncmp(t, "table|", 6)) errx(1, "malformed line"); t += 6; vers = t; if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing version"); *t++ = '\0'; if (strcmp(vers, "0.1") != 0) errx(1, "unsupported protocol version: %s", vers); /* skip timestamp */ if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing timestamp"); *t++ = '\0'; tname = t; if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing table name"); *t++ = '\0'; strlcpy(tablename, tname, sizeof(tablename)); type = t; if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing type"); *t++ = '\0'; if (!strcmp(type, "update")) { if (handler_update == NULL) errx(1, "no update handler registered"); id = t; r = handler_update(); printf("update-result|%s|%s\n", id, r == -1 ? "error" : "ok"); if (fflush(stdout) == EOF) err(1, "fflush"); continue; } service = t; if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing service"); *t++ = '\0'; id = t; if (!strcmp(type, "fetch")) { if (handler_fetch == NULL) errx(1, "no fetch handler registered"); r = handler_fetch(service_id(service), ¶ms, buf, sizeof(buf)); if (r == 1) printf("fetch-result|%s|found|%s\n", id, buf); else if (r == 0) printf("fetch-result|%s|not-found\n", id); else printf("fetch-result|%s|error\n", id); if (fflush(stdout) == EOF) err(1, "fflush"); memset(buf, 0, sizeof(buf)); continue; } if ((t = strchr(t, '|')) == NULL) errx(1, "malformed line: missing key"); *t++ = '\0'; key = t; if (!strcmp(type, "check")) { if (handler_check == NULL) errx(1, "no check handler registered"); r = handler_check(service_id(service), ¶ms, key); if (r == 1) printf("check-result|%s|found\n", id); else if (r == 0) printf("check-result|%s|not-found\n", id); else printf("check-result|%s|error\n", id); } else if (!strcmp(type, "lookup")) { if (handler_lookup == NULL) errx(1, "no lookup handler registered"); r = handler_lookup(service_id(service), ¶ms, key, buf, sizeof(buf)); if (r == 1) printf("lookup-result|%s|found|%s\n", id, buf); else if (r == 0) printf("lookup-result|%s|not-found\n", id); else printf("lookup-result|%s|error\n", id); memset(buf, 0, sizeof(buf)); } else errx(1, "unknown action %s", type); if (fflush(stdout) == EOF) err(1, "fflush"); } if (ferror(stdin)) err(1, "getline"); return (0); } OpenSMTPD-table-mysql-e976fa1/table_stdio.h000066400000000000000000000032241471166603200205500ustar00rootroot00000000000000/* * 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. */ enum table_service { 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 */ K_ANY = 0xfff, }; 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); OpenSMTPD-table-mysql-e976fa1/util.c000066400000000000000000000045641471166603200172370ustar00rootroot00000000000000/* $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 "compat.h" #include #include #include #include #include #include #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); } 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; } OpenSMTPD-table-mysql-e976fa1/util.h000066400000000000000000000003631471166603200172350ustar00rootroot00000000000000void *xmalloc(size_t, const char *); void *xcalloc(size_t, size_t, const char *); char *xstrdup(const char *, const char *); void *xmemdup(const char *, size_t, const char *); char *strip(char *); int lowercase(char *, const char *, size_t);