pax_global_header00006660000000000000000000000064147116674100014521gustar00rootroot0000000000000052 comment=d92273f3bf210aed6cc308f5ac9bcdbea53682be OpenSMTPD-table-redis-f54b0fb/000077500000000000000000000000001471166741000161155ustar00rootroot00000000000000OpenSMTPD-table-redis-f54b0fb/.gitignore000066400000000000000000000003061471166741000201040ustar00rootroot00000000000000**/*~ **/*.d **/*.o **/.deps **/Makefile **/Makefile.in **/aclocal.m4 **/.dirstamp **/autom4te.cache **/config.h **/config.h.in **/config.log **/config.status **/configure **/stamp-h1 table-redis OpenSMTPD-table-redis-f54b0fb/Makefile.am000066400000000000000000000011101471166741000201420ustar00rootroot00000000000000SUBDIRS = openbsd-compat noinst_PROGRAMS = table-redis table_redis_SOURCES = table_redis.c dict.c log.c table_stdio.c util.c LDADD = $(LIBOBJS) dist_man5_MANS = table-redis.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 table-redis.5 > README.md OpenSMTPD-table-redis-f54b0fb/README.md000066400000000000000000000112741471166741000174010ustar00rootroot00000000000000TABLE\_REDIS(5) - File Formats Manual # NAME **table\_redis** - format description for smtpd redis tables # DESCRIPTION This manual page documents the file format of redis tables used by the smtpd(8) mail daemon. The format described here applies to tables as defined in smtpd.conf(5). # REDIS TABLE A Redis table allows the storing of usernames, passwords, aliases, and domains in a redis server. 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 Redis table consists of one Redis Databases with one or more keys. 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. # REDIS TABLE CONFIG FILE **master** > This is the IP of the master redis server. > To connect via an unix socket use unix:/path/to/sock > The default is 127.0.0.1 **master\_port** > This is the port used to connect to the master redis server. > The default is 6379 **slave** > This is the IP of the slave redis server, if any. > To connect via an unix socket use unix:/path/to/sock **slave\_port** > This is the port used to connect to the slave redis server if any. **database** > The database number to use. > The default is 0. **password** > The password to use to authenticate to the redis server if any. **query\_domain** > This is used to provide a query for a domain query call. > All the '%s' are replaced > with the appropriate data, in this case it would be the right hand side of > the SMTP address. > This expects one string to be returned with a matching domain name. **query\_userinfo** > This is used to provide a query for looking up user information. > All the '%s' are replaced with the appropriate data, in this case it > would be the left hand side of the SMTP address. > This expects three fields to be returned an int containing a UID, an int > containing a GID > and a string containing the home directory for the user. **query\_credentials** > This is used to provide a query for looking up credentials. > All the '%s' are replaced > with the appropriate data, in this case it would be the left hand side of > the SMTP address. > the query expects that there are two strings returned one with a > user name one with a password in encrypted format. **query\_alias** > This is used to provide a query to look up aliases. > All the '%s' are replaced with the appropriate data, in this case it would > be the left hand side of the SMTP address. > This expects one string to be returned with the user name the alias resolves to. > If the query returns an array, all the data will be concatenated into one > string with ',' as a separator **query\_mailaddr** > This is used to provide a query to check if a mail address exists. > All the '%s' are replaced with the appropriate data, in this case it would > be the SMTP address. > This expects an integer as a reply, 0 = false and 1 = true # EXAMPLES Due to the nature of redis, multiple schemas can be used. Those provided here a known to work. **domain** > Using a set for the domains: > > \# redis-cli sadd domains example.net > in the redis table configuration file: > > query\_domain SISMEMBER domains %s **userinfo** > Hash works well for users > > \# redis-cli HSET user:foo uid 1001 > > \# redis-cli HSET user:foo gid 1001 > > \# redis-cli HSET user:foo maildir /mail/foo > in the redis table configuration file : > > query\_userinfo HMGET user:%s uid gid maildir **credentials** > We can extend the hash for our user to put credential in it > > \# redis-cli HSET user:foo login foo > > \# redis-cli HSET user:foo passwd encrypted\_password > in the redis table configuration file: > > query\_credentials HMGET user:%s login passwd **alias** > Using redis sorted list: > > \# redis-cli LPUSH aliases:foo@example.net foo > > \# redis-cli LPUSH aliases:bar@example.net foo > in the redis table configuration file: > > query\_alias LRANGE aliases:%s 0 -1 **mailaddr** > Using a set for the addresses: > > \# redis-cli sadd mailaddr foo@example.net > in the redis table configuration file: > > query\_mailaddr SISMEMBER mailaddr %s # SEE ALSO encrypt(1), smtpd.conf(5), smtpctl(8), smtpd(8) # HISTORY The first version of **table\_redis** was written in 2015. It was converted to the stdio protocol in 2024. # AUTHORS **table\_redis** was initially written by Emmanuel Vadot <[elbarto@bocal.org](mailto:elbarto@bocal.org)>. The conversion to the stdio table protocol was done by Omar Polo <[op@openbsd.org](mailto:op@openbsd.org)>. OpenBSD 7.5 - April 21, 2024 OpenSMTPD-table-redis-f54b0fb/bootstrap000077500000000000000000000000341471166741000200550ustar00rootroot00000000000000#! /bin/sh autoreconf -vfi OpenSMTPD-table-redis-f54b0fb/compat.h000066400000000000000000000012521471166741000175510ustar00rootroot00000000000000#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-redis-f54b0fb/configure.ac000066400000000000000000000011601471166741000204010ustar00rootroot00000000000000AC_INIT([table-redis], [1.0.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([redisConnect], [hiredis], [], [ AC_MSG_ERROR([requires hiredis 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-redis-f54b0fb/dict.c000066400000000000000000000123351471166741000172100ustar00rootroot00000000000000/* $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-redis-f54b0fb/dict.h000066400000000000000000000034511471166741000172140ustar00rootroot00000000000000/* $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-redis-f54b0fb/log.c000066400000000000000000000072331471166741000170470ustar00rootroot00000000000000/* $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-redis-f54b0fb/log.h000066400000000000000000000032621471166741000170520ustar00rootroot00000000000000/* $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-redis-f54b0fb/openbsd-compat/000077500000000000000000000000001471166741000210305ustar00rootroot00000000000000OpenSMTPD-table-redis-f54b0fb/openbsd-compat/Makefile.am000066400000000000000000000000431471166741000230610ustar00rootroot00000000000000EXTRA_DIST = err.h tree/sys/tree.h OpenSMTPD-table-redis-f54b0fb/openbsd-compat/asprintf.c000066400000000000000000000027011471166741000230220ustar00rootroot00000000000000/* * 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-redis-f54b0fb/openbsd-compat/err.c000066400000000000000000000033261471166741000217700ustar00rootroot00000000000000/* * 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-redis-f54b0fb/openbsd-compat/err.h000066400000000000000000000003421471166741000217700ustar00rootroot00000000000000#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-redis-f54b0fb/openbsd-compat/getprogname.c000066400000000000000000000020511471166741000235020ustar00rootroot00000000000000/* * 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-redis-f54b0fb/openbsd-compat/strlcat.c000066400000000000000000000032401471166741000226470ustar00rootroot00000000000000/* * 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-redis-f54b0fb/openbsd-compat/strlcpy.c000066400000000000000000000030151471166741000226730ustar00rootroot00000000000000/* * 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-redis-f54b0fb/openbsd-compat/strsep.c000066400000000000000000000050371471166741000225210ustar00rootroot00000000000000/* $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-redis-f54b0fb/openbsd-compat/strtonum.c000066400000000000000000000034041471166741000230700ustar00rootroot00000000000000/* $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-redis-f54b0fb/openbsd-compat/tree/000077500000000000000000000000001471166741000217675ustar00rootroot00000000000000OpenSMTPD-table-redis-f54b0fb/openbsd-compat/tree/sys/000077500000000000000000000000001471166741000226055ustar00rootroot00000000000000OpenSMTPD-table-redis-f54b0fb/openbsd-compat/tree/sys/tree.h000066400000000000000000001017461471166741000237260ustar00rootroot00000000000000/* $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-redis-f54b0fb/shell.nix000066400000000000000000000002551471166741000177460ustar00rootroot00000000000000{ pkgs ? import {} }: pkgs.mkShell { nativeBuildInputs = with pkgs; [ autoreconfHook pkg-config gdb mandoc ]; buildInputs = with pkgs; [ libbsd hiredis ]; } OpenSMTPD-table-redis-f54b0fb/table-redis.5000066400000000000000000000126151471166741000204030ustar00rootroot00000000000000.\" Copyright (c) 2015 Emmanuel Vadot .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" .Dd $Mdocdate: April 21 2024 $ .Dt TABLE_REDIS 5 .Os .Sh NAME .Nm table_redis .Nd format description for smtpd redis tables .Sh DESCRIPTION This manual page documents the file format of redis tables used by the .Xr smtpd 8 mail daemon. .Pp The format described here applies to tables as defined in .Xr smtpd.conf 5 . .Sh REDIS TABLE A Redis table allows the storing of usernames, passwords, aliases, and domains in a redis server. .Pp The table is used by .Xr smtpd 8 when authenticating a user, when user information such as user-id and/or home directory is required for a delivery, when a domain lookup may be required, and/or when looking for an alias. .Pp A Redis table consists of one Redis Databases with one or more keys. .Pp If the table is used for authentication, the password should be encrypted using the .Xr crypt 3 function. Such passwords can be generated using the .Xr encrypt 1 utility or .Xr smtpctl 8 encrypt command. .Sh REDIS TABLE CONFIG FILE .Bl -tag -width query_credentials .It Cm master This is the IP of the master redis server. To connect via an unix socket use unix:/path/to/sock The default is 127.0.0.1 .It Cm master_port This is the port used to connect to the master redis server. The default is 6379 .It Cm slave This is the IP of the slave redis server, if any. To connect via an unix socket use unix:/path/to/sock .It Cm slave_port This is the port used to connect to the slave redis server if any. .It Cm database The database number to use. The default is 0. .It Cm password The password to use to authenticate to the redis server if any. .It Cm query_domain This is used to provide a query for a domain query call. All the '%s' are replaced with the appropriate data, in this case it would be the right hand side of the SMTP address. This expects one string to be returned with a matching domain name. .It Cm query_userinfo This is used to provide a query for looking up user information. All the '%s' are replaced with the appropriate data, in this case it would be the left hand side of the SMTP address. This expects three fields to be returned an int containing a UID, an int containing a GID and a string containing the home directory for the user. .It Cm query_credentials This is used to provide a query for looking up credentials. All the '%s' are replaced with the appropriate data, in this case it would be the left hand side of the SMTP address. the query expects that there are two strings returned one with a user name one with a password in encrypted format. .It Cm query_alias This is used to provide a query to look up aliases. All the '%s' are replaced with the appropriate data, in this case it would be the left hand side of the SMTP address. This expects one string to be returned with the user name the alias resolves to. If the query returns an array, all the data will be concatenated into one string with ',' as a separator .It Cm query_mailaddr This is used to provide a query to check if a mail address exists. All the '%s' are replaced with the appropriate data, in this case it would be the SMTP address. This expects an integer as a reply, 0 = false and 1 = true .El .Sh EXAMPLES Due to the nature of redis, multiple schemas can be used. Those provided here a known to work. .Bl -tag -width 1 .It Cm domain Using a set for the domains: .Dl # redis-cli sadd domains example.net in the redis table configuration file: .Dl query_domain SISMEMBER domains %s .It Cm userinfo Hash works well for users .Dl # redis-cli HSET user:foo uid 1001 .Dl # redis-cli HSET user:foo gid 1001 .Dl # redis-cli HSET user:foo maildir "/mail/foo" in the redis table configuration file : .Dl query_userinfo HMGET user:%s uid gid maildir .It Cm credentials We can extend the hash for our user to put credential in it .Dl # redis-cli HSET user:foo login foo .Dl # redis-cli HSET user:foo passwd encrypted_password in the redis table configuration file: .Dl query_credentials HMGET user:%s login passwd .It Cm alias Using redis sorted list: .Dl # redis-cli LPUSH aliases:foo@example.net foo .Dl # redis-cli LPUSH aliases:bar@example.net foo in the redis table configuration file: .Dl query_alias LRANGE aliases:%s 0 -1 .It Cm mailaddr Using a set for the addresses: .Dl # redis-cli sadd mailaddr foo@example.net in the redis table configuration file: .Dl query_mailaddr SISMEMBER mailaddr %s .El .Sh SEE ALSO .Xr encrypt 1 , .Xr smtpd.conf 5 , .Xr smtpctl 8 , .Xr smtpd 8 .Sh HISTORY The first version of .Nm was written in 2015. It was converted to the stdio protocol in 2024. .Sh AUTHORS .An -nosplit .Nm was initially written by .An Emmanuel Vadot Aq Mt elbarto@bocal.org . The conversion to the stdio table protocol was done by .An Omar Polo Aq Mt op@openbsd.org . OpenSMTPD-table-redis-f54b0fb/table_redis.c000066400000000000000000000261731471166741000205470ustar00rootroot00000000000000/* * Copyright (c) 2013 Eric Faurot * Copyright (c) 2014 Michael Neumann * Copyright (c) 2015 Emmanuel Vadot * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "compat.h" #include #include #include #include #include #include #include #include #include #include #include "dict.h" #include "log.h" #include "table_stdio.h" #include "util.h" enum { REDIS_ALIAS = 0, REDIS_DOMAIN, REDIS_CREDENTIALS, REDIS_NETADDR, REDIS_USERINFO, REDIS_SOURCE, REDIS_MAILADDR, REDIS_ADDRNAME, REDIS_MAX }; struct config { struct dict conf; redisContext *master; redisContext *slave; char *queries[REDIS_MAX]; }; static void config_free(struct config *); static char *conffile; static struct config *config; static struct config * config_load(const char *path) { struct config *config; FILE *fp; size_t sz = 0; ssize_t flen; char *key, *value, *buf = NULL; if ((config = calloc(1, sizeof(*config))) == NULL) { log_warn("warn: calloc"); return NULL; } dict_init(&config->conf); if ((fp = fopen(path, "r")) == NULL) { log_warn("warn: \"%s\"", path); goto end; } while ((flen = getline(&buf, &sz, fp)) != -1) { if (buf[flen - 1] == '\n') buf[flen - 1] = '\0'; key = strip(buf); if (*key == '\0' || *key == '#') continue; value = key; strsep(&value, " \t:"); if (value) { while (*value) { if (!isspace((unsigned char)*value) && !(*value == ':' && isspace((unsigned char)*(value + 1)))) break; ++value; } if (*value == '\0') value = NULL; } if (value == NULL) { log_warnx("warn: missing value for key %s", key); goto end; } if (dict_check(&config->conf, key)) { log_warnx("warn: duplicate key %s", key); goto end; } if ((value = strdup(value)) == NULL) { log_warn("warn: strdup"); goto end; } dict_set(&config->conf, key, value); } free(buf); fclose(fp); return config; end: free(buf); fclose(fp); config_free(config); return NULL; } static void config_reset(struct config *config) { size_t i; for (i = 0; i < REDIS_MAX; i++) if (config->queries[i]) { free(config->queries[i]); config->queries[i] = NULL; } if (config->master) { redisFree(config->master); config->master = NULL; } } static int config_connect(struct config *config) { static const struct { const char *name; const char *default_query; } qspec[REDIS_MAX] = { { "query_alias", "GET alias:%s" }, { "query_domain", "GET domain:%s" }, { "query_credentials", "GET credentials:%s" }, { "query_netaddr", "GET netaddr:%s" }, { "query_userinfo", "GET userinfo:%s" }, { "query_source", "GET source:%s" }, { "query_mailaddr", "GET mailaddr:%s" }, { "query_addrname", "GET addrname:%s" }, }; size_t i; char *master = "127.0.0.1"; int master_port = 6379; char *slave = "NULL"; int slave_port = 6380; char *password = NULL; int database = 0; char *q; char *value; const char *e; long long ll; redisReply *res = NULL; log_debug("debug: (re)connecting"); /* disconnect first, if needed */ config_reset(config); if ((value = dict_get(&config->conf, "master"))) master = value; if ((value = dict_get(&config->conf, "slave"))) slave = value; if ((value = dict_get(&config->conf, "master_port"))) { e = NULL; ll = strtonum(value, 0, 65535, &e); if (e) { log_warnx("warn: bad value for master_port: %s", e); goto end; } master_port = ll; } if ((value = dict_get(&config->conf, "slave_port"))) { e = NULL; ll = strtonum(value, 0, 65535, &e); if (e) { log_warnx("warn: bad value for slave_port: %s", e); goto end; } slave_port = ll; } if ((value = dict_get(&config->conf, "password"))) password = value; if ((value = dict_get(&config->conf, "database"))) { e = NULL; ll = strtonum(value, 0, 256, &e); if (e) { log_warnx("warn: bad value for database: %s", e); goto end; } database = ll; } if (!strncmp("unix:", master, 5)) { log_debug("debug: connect via unix socket %s", master + 5); config->master = redisConnectUnix(master + 5); } else { log_debug("debug: connect to master via tcp at %s:%d", master, master_port); config->master = redisConnect(master, master_port); } if (config->master == NULL) { log_warnx("warn: can't create redis context for master"); goto end; } if (!config->master->err) { log_debug("debug: connected to master"); if (password) { res = redisCommand(config->master, "AUTH %s", password); if (res->type == REDIS_REPLY_ERROR) { log_warnx("warn: authentication on master failed"); goto end; } freeReplyObject(res); } if (database != 0) { res = redisCommand(config->master, "SELECT %d", database); if (res->type != REDIS_REPLY_STATUS) { log_warnx("warn: database selection on master failed"); goto end; } freeReplyObject(res); } } if (slave) { if (!strncmp("unix:", slave, 5)) { log_debug("debug: connect to slave via unix socket %s", slave + 5); config->slave = redisConnectUnix(slave + 5); } else { log_debug("debug: connect to slave via tcp at %s:%d", slave, slave_port); config->slave = redisConnect(slave, slave_port); } if (config->slave == NULL) { log_warnx("warn: can't create redis context for slave"); goto end; } if (!config->slave->err) { if (password) { res = redisCommand(config->slave, "AUTH %s", password); if (res->type == REDIS_REPLY_ERROR) { log_warnx("warn: authentication on slave failed"); goto end; } freeReplyObject(res); } if (database != 0) { res = redisCommand(config->slave, "SELECT %d", database); if (res->type != REDIS_REPLY_STATUS) { log_warnx("warn: database selection on slave failed"); goto end; } freeReplyObject(res); } } } for (i = 0; i < REDIS_MAX; i++) { q = dict_get(&config->conf, qspec[i].name); if (q) config->queries[i] = strdup(q); else config->queries[i] = strdup(qspec[i].default_query); if (config->queries[i] == NULL) { log_warn("warn: strdup"); goto end; } } if (config->master->err && config->slave->err) { log_warnx("warn: redisConnect for master and slave failed"); goto end; } log_debug("debug: connected"); return 1; end: if (res) freeReplyObject(res); config_reset(config); return 0; } static void config_free(struct config *config) { void *value; config_reset(config); while (dict_poproot(&config->conf, &value)) free(value); free(config); } static int table_redis_update(void) { struct config *c; if ((c = config_load(conffile)) == NULL) return 0; if (config_connect(c) == 0) { config_free(c); return 0; } config_free(config); config = c; return 1; } static redisReply * table_redis_query(const char *key, int service) { redisReply *res; char *query = NULL; int i; int retry_times; retry_times = 3; retry: --retry_times; if (retry_times < 0) { log_warnx("warn: giving up: too many retries"); return NULL; } for(i = 0; i < REDIS_MAX; i++) if (service == 1 << i) { query = config->queries[i]; break; } if (query == NULL) return NULL; if (!config->master->err) { log_debug("debug: running query \"%s\" on master", query); res = redisCommand(config->master, query, key); } else if (!config->slave->err) { log_debug("debug: running query \"%s\" on slave", query); res = redisCommand(config->slave, query, key); } else return NULL; if (res == NULL) { log_warnx("warn: redisCommand: %s", config->master->errstr); if (config_connect(config)) goto retry; return NULL; } return res; } static int table_redis_check(int service, struct dict *params, const char *key) { int r; redisReply *reply; if (config->master == NULL && config_connect(config) == 0) return -1; reply = table_redis_query(key, service); if (reply == NULL) return -1; r = 0; switch (reply->type) { case REDIS_REPLY_INTEGER: r = reply->integer; break; case REDIS_REPLY_STRING: case REDIS_REPLY_ARRAY: r = 1; break; case REDIS_REPLY_NIL: r = 0; break; case REDIS_REPLY_STATUS: case REDIS_REPLY_ERROR: default: r = -1; break; } freeReplyObject(reply); return r; } static int table_redis_lookup(int service, struct dict *params, const char *key, char *dst, size_t sz) { redisReply *reply, *elmt; unsigned int i; int r; if (config->master == NULL && config_connect(config) == 0) return -1; reply = table_redis_query(key, service); if (reply == NULL) return -1; r = 1; switch(service) { case K_ALIAS: case K_CREDENTIALS: case K_USERINFO: memset(dst, 0, sz); if (reply->type == REDIS_REPLY_STRING) { if (strlcat(dst, reply->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } else if (reply->type == REDIS_REPLY_ARRAY) { if (reply->elements == 0) r = 0; for (i = 0; i < reply->elements; i++) { elmt = reply->element[i]; if (elmt == NULL || elmt->type != REDIS_REPLY_STRING) { r = -1; break; } if (dst[0] && strlcat(dst, service == K_ALIAS ? ", " : ":", sz) >= sz) { log_warnx("warn: result too large"); r = -1; } if (strlcat(dst, elmt->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } } else r = -1; break; case K_DOMAIN: case K_NETADDR: case K_SOURCE: case K_MAILADDR: case K_ADDRNAME: if (reply->type == REDIS_REPLY_STRING) { if (strlcpy(dst, reply->str, sz) >= sz) { log_warnx("warn: result too large"); r = -1; } } else r = -1; break; default: log_warnx("warn: unknown service %d", service); r = -1; } log_debug("debug: table_redis_lookup return %d (result = \"%s\")", r, dst); freeReplyObject(reply); return r; } static int table_redis_fetch(int service, struct dict *params, char *dst, size_t sz) { return -1; } int main(int argc, char **argv) { int ch; log_init(1); log_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]; if ((config = config_load(conffile)) == NULL) fatalx("error parsing config file"); if (config_connect(config) == 0) fatalx("could not connect"); table_api_on_update(table_redis_update); table_api_on_check(table_redis_check); table_api_on_lookup(table_redis_lookup); table_api_on_fetch(table_redis_fetch); table_api_dispatch(); return 0; } OpenSMTPD-table-redis-f54b0fb/table_stdio.c000066400000000000000000000135371471166741000205630ustar00rootroot00000000000000/* $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-redis-f54b0fb/table_stdio.h000066400000000000000000000032241471166741000205600ustar00rootroot00000000000000/* * 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-redis-f54b0fb/util.c000066400000000000000000000045641471166741000172470ustar00rootroot00000000000000/* $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-redis-f54b0fb/util.h000066400000000000000000000003631471166741000172450ustar00rootroot00000000000000void *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);