diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2013-01-26 09:37:25 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2013-01-26 09:37:25 +0000 |
commit | 52e93b0e61fd0a116dbb373054e2cd0ea3bfcf39 (patch) | |
tree | 41934d0fc43bfebf55ba5a199e0d699adf24aff1 /usr.sbin/smtpd/table.c | |
parent | 3b78bd2481525635417ca0fc75396ef754c09171 (diff) |
Sync with our smtpd repo:
* first bricks of ldap and sqlite support (not finished but both working)
* new table API to replace map API, all lookups are done through tables
* improved handling of temporary errors throughout the daemon
* improved scheduler and mta logic: connection reuse, optimizes batches
* improved queue: more tolerant to admin errors, new layout, less disk-IO
* improved memory usage under high load
* SSL certs/keys isolated to lookup process to avoid facing network
* VIRTUAL support improved, fully virtual setups possible now
* runtime tracing of processes through smtpctl trace
* ssl_privsep.c sync-ed with relayd
* ssl.c no longer contains smtpd specific interfaces
* smtpd-specific ssl bits moved to ssl_smtpd.c
* update mail address in copyright
FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE.
smtpd.conf(5) simplified, it will require adaptations
ok eric@
Diffstat (limited to 'usr.sbin/smtpd/table.c')
-rw-r--r-- | usr.sbin/smtpd/table.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/usr.sbin/smtpd/table.c b/usr.sbin/smtpd/table.c new file mode 100644 index 00000000000..3f8f14e346b --- /dev/null +++ b/usr.sbin/smtpd/table.c @@ -0,0 +1,461 @@ +/* $OpenBSD: table.c,v 1.1 2013/01/26 09:37:24 gilles Exp $ */ + +/* + * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> + * + * 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 <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/param.h> +#include <sys/socket.h> + +#include <netinet/in.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "smtpd.h" +#include "log.h" + +struct table_backend *table_backend_lookup(const char *); + +extern struct table_backend table_backend_static; +extern struct table_backend table_backend_db; +extern struct table_backend table_backend_getpwnam; +extern struct table_backend table_backend_sqlite; +extern struct table_backend table_backend_ldap; + +static objid_t last_table_id = 0; + +struct table_backend * +table_backend_lookup(const char *backend) +{ + if (!strcmp(backend, "static") || !strcmp(backend, "file")) + return &table_backend_static; + if (!strcmp(backend, "db")) + return &table_backend_db; + if (!strcmp(backend, "getpwnam")) + return &table_backend_getpwnam; + if (!strcmp(backend, "sqlite")) + return &table_backend_sqlite; + if (!strcmp(backend, "ldap")) + return &table_backend_ldap; + return NULL; +} + +struct table * +table_findbyname(const char *name) +{ + return dict_get(env->sc_tables_dict, name); +} + +struct table * +table_find(objid_t id) +{ + return tree_get(env->sc_tables_tree, id); +} + +int +table_lookup(struct table *table, const char *key, enum table_service kind, + void **retp) +{ + return table->t_backend->lookup(table->t_handle, key, kind, retp); +} + +int +table_fetch(struct table *table, enum table_service kind, char **retp) +{ + return table->t_backend->fetch(table->t_handle, kind, retp); +} + +struct table * +table_create(const char *backend, const char *name, const char *config) +{ + struct table *t; + struct table_backend *tb; + size_t n; + + if (name && table_findbyname(name)) + errx(1, "table_create: table \"%s\" already defined", name); + + if ((tb = table_backend_lookup(backend)) == NULL) + errx(1, "table_create: backend \"%s\" does not exist", backend); + + t = xcalloc(1, sizeof(*t), "table_create"); + t->t_backend = tb; + + /* XXX */ + /* + * until people forget about it, "file" really means "static" + */ + if (!strcmp(backend, "file")) + backend = "static"; + + if (strlcpy(t->t_src, backend, sizeof t->t_src) >= sizeof t->t_src) + errx(1, "table_create: table backend \"%s\" too large", + t->t_src); + + if (config && *config) { + if (strlcpy(t->t_config, config, sizeof t->t_config) + >= sizeof t->t_config) + errx(1, "table_create: table config \"%s\" too large", + t->t_config); + } + + if (strcmp(t->t_src, "static") != 0) + t->t_type = T_DYNAMIC; + + t->t_id = ++last_table_id; + if (t->t_id == INT_MAX) + errx(1, "table_create: too many tables defined"); + + if (name == NULL) + snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", t->t_id); + else { + n = strlcpy(t->t_name, name, sizeof(t->t_name)); + if (n >= sizeof(t->t_name)) + errx(1, "table_create: table name too long"); + } + + dict_init(&t->t_dict); + dict_set(env->sc_tables_dict, t->t_name, t); + tree_set(env->sc_tables_tree, t->t_id, t); + + return (t); +} + +void +table_destroy(struct table *t) +{ + void *p = NULL; + + while (dict_poproot(&t->t_dict, NULL, (void **)&p)) + free(p); + + dict_xpop(env->sc_tables_dict, t->t_name); + tree_xpop(env->sc_tables_tree, t->t_id); + free(t); +} + +void +table_set_configuration(struct table *t, struct table *config) +{ + strlcpy(t->t_cfgtable, config->t_name, sizeof t->t_cfgtable); +} + +struct table * +table_get_configuration(struct table *t) +{ + return table_findbyname(t->t_cfgtable); +} + +void +table_set_payload(struct table *t, void *payload) +{ + t->t_payload = payload; +} + +void * +table_get_payload(struct table *t) +{ + return t->t_payload; +} + +void +table_add(struct table *t, const char *key, const char *val) +{ + if (strcmp(t->t_src, "static") != 0) + errx(1, "table_add: cannot add to table"); + dict_set(&t->t_dict, key, val ? xstrdup(val, "table_add") : NULL); +} + +const void * +table_get(struct table *t, const char *key) +{ + if (strcmp(t->t_src, "static") != 0) + errx(1, "table_add: cannot get from table"); + return dict_get(&t->t_dict, key); +} + +void +table_delete(struct table *t, const char *key) +{ + if (strcmp(t->t_src, "static") != 0) + errx(1, "map_add: cannot delete from map"); + free(dict_pop(&t->t_dict, key)); +} + +int +table_check_type(struct table *t, uint32_t mask) +{ + return t->t_type & mask; +} + +int +table_check_service(struct table *t, uint32_t mask) +{ + return t->t_backend->services & mask; +} + +int +table_check_use(struct table *t, uint32_t tmask, uint32_t smask) +{ + return table_check_type(t, tmask) && table_check_service(t, smask); +} + +int +table_open(struct table *t) +{ + t->t_handle = t->t_backend->open(t); + if (t->t_handle == NULL) + return 0; + return 1; +} + +void +table_close(struct table *t) +{ + t->t_backend->close(t->t_handle); +} + + +void +table_update(struct table *t) +{ + t->t_backend->update(t); +} + +void * +table_config_create(void) +{ + return table_create("static", NULL, NULL); +} + +const char * +table_config_get(void *p, const char *key) +{ + return (const char *)table_get(p, key); +} + +void +table_config_destroy(void *p) +{ + table_destroy(p); +} + +int +table_config_parse(void *p, const char *config, enum table_type type) +{ + struct table *t = p; + FILE *fp; + char *buf, *lbuf; + size_t flen; + char *keyp; + char *valp; + size_t ret = 0; + + if (strcmp("static", t->t_src) != 0) { + log_warn("table_config_parser: config table must be static"); + return 0; + } + + fp = fopen(config, "r"); + if (fp == NULL) + return 0; + + lbuf = NULL; + while ((buf = fgetln(fp, &flen))) { + if (buf[flen - 1] == '\n') + buf[flen - 1] = '\0'; + else { + lbuf = xmalloc(flen + 1, "table_stdio_get_entry"); + memcpy(lbuf, buf, flen); + lbuf[flen] = '\0'; + buf = lbuf; + } + + keyp = buf; + while (isspace((int)*keyp)) + ++keyp; + if (*keyp == '\0' || *keyp == '#') + continue; + valp = keyp; + strsep(&valp, " \t:"); + if (valp) { + while (*valp && isspace(*valp)) + ++valp; + if (*valp == '\0') + valp = NULL; + } + + /**/ + if (t->t_type == 0) + t->t_type = (valp == keyp || valp == NULL) ? T_LIST : + T_HASH; + + if (!(t->t_type & type)) + goto end; + + if ((valp == keyp || valp == NULL) && t->t_type == T_LIST) + table_add(t, keyp, NULL); + else if ((valp != keyp && valp != NULL) && t->t_type == T_HASH) + table_add(t, keyp, valp); + else + goto end; + } + ret = 1; +end: + free(lbuf); + fclose(fp); + return ret; +} + +int +table_domain_match(const char *s1, const char *s2) +{ + return hostname_match(s1, s2); +} + +int +table_mailaddr_match(const char *s1, const char *s2) +{ + struct mailaddr m1; + struct mailaddr m2; + + if (! text_to_mailaddr(&m1, s1)) + return 0; + if (! text_to_mailaddr(&m2, s2)) + return 0; + + if (strcasecmp(m1.domain, m2.domain)) + return 0; + + if (m2.user[0]) + if (strcasecmp(m1.user, m2.user)) + return 0; + return 1; +} + +static int table_match_mask(struct sockaddr_storage *, struct netaddr *); +static int table_inet4_match(struct sockaddr_in *, struct netaddr *); +static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); + +int +table_netaddr_match(const char *s1, const char *s2) +{ + struct netaddr n1; + struct netaddr n2; + + if (strcmp(s1, s2) == 0) + return 1; + if (! text_to_netaddr(&n1, s1)) + return 0; + if (! text_to_netaddr(&n2, s2)) + return 0; + if (n1.ss.ss_family != n2.ss.ss_family) + return 0; + if (n1.ss.ss_len != n2.ss.ss_len) + return 0; + return table_match_mask(&n1.ss, &n2); +} + +static int +table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) +{ + if (ss->ss_family == AF_INET) + return table_inet4_match((struct sockaddr_in *)ss, ssmask); + + if (ss->ss_family == AF_INET6) + return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); + + return (0); +} + +static int +table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) +{ + in_addr_t mask; + int i; + + /* a.b.c.d/8 -> htonl(0xff000000) */ + mask = 0; + for (i = 0; i < ssmask->bits; ++i) + mask = (mask >> 1) | 0x80000000; + mask = htonl(mask); + + /* (addr & mask) == (net & mask) */ + if ((ss->sin_addr.s_addr & mask) == + (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) + return 1; + + return 0; +} + +static int +table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) +{ + struct in6_addr *in; + struct in6_addr *inmask; + struct in6_addr mask; + int i; + + bzero(&mask, sizeof(mask)); + for (i = 0; i < ssmask->bits / 8; i++) + mask.s6_addr[i] = 0xff; + i = ssmask->bits % 8; + if (i) + mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i; + + in = &ss->sin6_addr; + inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; + + for (i = 0; i < 16; i++) { + if ((in->s6_addr[i] & mask.s6_addr[i]) != + (inmask->s6_addr[i] & mask.s6_addr[i])) + return (0); + } + + return (1); +} + +void +table_open_all(void) +{ + struct table *t; + void *iter; + + iter = NULL; + while (tree_iter(env->sc_tables_tree, &iter, NULL, (void **)&t)) + if (! table_open(t)) + errx(1, "failed to open table %s", t->t_name); +} + +void +table_close_all(void) +{ + struct table *t; + void *iter; + + iter = NULL; + while (tree_iter(env->sc_tables_tree, &iter, NULL, (void **)&t)) + table_close(t); +} |