1*a4c00f8aSeric /* $OpenBSD: table_static.c,v 1.26 2018/12/27 08:57:03 eric Exp $ */ 265c4fdfbSgilles 365c4fdfbSgilles /* 4299c4efeSeric * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> 565c4fdfbSgilles * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 665c4fdfbSgilles * 765c4fdfbSgilles * Permission to use, copy, modify, and distribute this software for any 865c4fdfbSgilles * purpose with or without fee is hereby granted, provided that the above 965c4fdfbSgilles * copyright notice and this permission notice appear in all copies. 1065c4fdfbSgilles * 1165c4fdfbSgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1265c4fdfbSgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1365c4fdfbSgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1465c4fdfbSgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1565c4fdfbSgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1665c4fdfbSgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1765c4fdfbSgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1865c4fdfbSgilles */ 1965c4fdfbSgilles 2065c4fdfbSgilles #include <sys/types.h> 2165c4fdfbSgilles #include <sys/queue.h> 2265c4fdfbSgilles #include <sys/tree.h> 2365c4fdfbSgilles #include <sys/socket.h> 2465c4fdfbSgilles 2565c4fdfbSgilles #include <netinet/in.h> 2665c4fdfbSgilles #include <arpa/inet.h> 2765c4fdfbSgilles 2865c4fdfbSgilles #include <ctype.h> 2965c4fdfbSgilles #include <err.h> 3065c4fdfbSgilles #include <event.h> 3165c4fdfbSgilles #include <fcntl.h> 3265c4fdfbSgilles #include <imsg.h> 3365c4fdfbSgilles #include <stdio.h> 3465c4fdfbSgilles #include <stdlib.h> 35953aae25Sderaadt #include <limits.h> 3665c4fdfbSgilles #include <string.h> 3765c4fdfbSgilles 3865c4fdfbSgilles #include "smtpd.h" 3965c4fdfbSgilles #include "log.h" 4065c4fdfbSgilles 4165c4fdfbSgilles /* static backend */ 42299c4efeSeric static int table_static_config(struct table *); 4365c4fdfbSgilles static int table_static_update(struct table *); 44b700b1d8Seric static int table_static_open(struct table *); 4593cc0b04Seric static int table_static_lookup(void *, enum table_service, const char *, 46aa0d26b5Seric char **); 47699c3f98Seric static int table_static_fetch(void *, enum table_service, char **); 48*a4c00f8aSeric static void table_static_close(struct table *); 4965c4fdfbSgilles 5065c4fdfbSgilles struct table_backend table_backend_static = { 514f15e081Seric "static", 5293262a01Ssunil K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO| 53a8e22235Sgilles K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST| 5481c7fb0aSgilles K_STRING|K_REGEX, 5565c4fdfbSgilles table_static_config, 5665c4fdfbSgilles table_static_open, 5765c4fdfbSgilles table_static_update, 5865c4fdfbSgilles table_static_close, 5965c4fdfbSgilles table_static_lookup, 6065c4fdfbSgilles table_static_fetch 6165c4fdfbSgilles }; 6265c4fdfbSgilles 6365c4fdfbSgilles static struct keycmp { 6465c4fdfbSgilles enum table_service service; 6565c4fdfbSgilles int (*func)(const char *, const char *); 6665c4fdfbSgilles } keycmp[] = { 6765c4fdfbSgilles { K_DOMAIN, table_domain_match }, 6865c4fdfbSgilles { K_NETADDR, table_netaddr_match }, 6981c7fb0aSgilles { K_MAILADDR, table_mailaddr_match }, 7081c7fb0aSgilles { K_REGEX, table_regex_match }, 7165c4fdfbSgilles }; 7265c4fdfbSgilles 7365c4fdfbSgilles 7465c4fdfbSgilles static int 753cedc4a5Seric table_static_config(struct table *t) 76299c4efeSeric { 77299c4efeSeric FILE *fp; 78b7b75d53Seric char *buf = NULL, *p; 79b7b75d53Seric int lineno = 0; 80c17c2b51Ssunil size_t sz = 0; 81c17c2b51Ssunil ssize_t flen; 82299c4efeSeric char *keyp; 83299c4efeSeric char *valp; 84299c4efeSeric size_t ret = 0; 85299c4efeSeric 863cedc4a5Seric /* no config ? ok */ 873cedc4a5Seric if (*t->t_config == '\0') 883cedc4a5Seric return 1; 893cedc4a5Seric 903cedc4a5Seric if ((fp = fopen(t->t_config, "r")) == NULL) { 913cedc4a5Seric log_warn("warn: Table \"%s\"", t->t_config); 92299c4efeSeric return 0; 9327c7fbfbSgilles } 94299c4efeSeric 95c17c2b51Ssunil while ((flen = getline(&buf, &sz, fp)) != -1) { 96b7b75d53Seric lineno++; 97299c4efeSeric if (buf[flen - 1] == '\n') 98b7b75d53Seric buf[--flen] = '\0'; 99299c4efeSeric 100299c4efeSeric keyp = buf; 101b7b75d53Seric while (isspace((unsigned char)*keyp)) { 102b7b75d53Seric ++keyp; 103b7b75d53Seric --flen; 104b7b75d53Seric } 105b7b75d53Seric if (*keyp == '\0') 106b7b75d53Seric continue; 107b7b75d53Seric while (isspace((unsigned char)keyp[flen - 1])) 108b7b75d53Seric keyp[--flen] = '\0'; 109b7b75d53Seric if (*keyp == '#') { 110b7b75d53Seric if (t->t_type == T_NONE) { 111b7b75d53Seric keyp++; 112fc3a8311Seric while (isspace((unsigned char)*keyp)) 113299c4efeSeric ++keyp; 114b7b75d53Seric if (!strcmp(keyp, "@list")) 115b7b75d53Seric t->t_type = T_LIST; 116b7b75d53Seric } 117299c4efeSeric continue; 118b7b75d53Seric } 119b7b75d53Seric 120b7b75d53Seric if (t->t_type == T_NONE) { 121b7b75d53Seric for (p = keyp; *p; p++) { 122b7b75d53Seric if (*p == ' ' || *p == '\t' || *p == ':') { 123b7b75d53Seric t->t_type = T_HASH; 124b7b75d53Seric break; 125b7b75d53Seric } 126b7b75d53Seric } 127b7b75d53Seric if (t->t_type == T_NONE) 128b7b75d53Seric t->t_type = T_LIST; 129b7b75d53Seric } 130b7b75d53Seric 131b7b75d53Seric if (t->t_type == T_LIST) { 132b7b75d53Seric table_add(t, keyp, NULL); 133b7b75d53Seric continue; 134b7b75d53Seric } 135b7b75d53Seric 136b7b75d53Seric /* T_HASH */ 137299c4efeSeric valp = keyp; 138299c4efeSeric strsep(&valp, " \t:"); 139299c4efeSeric if (valp) { 140299c4efeSeric while (*valp) { 141fc3a8311Seric if (!isspace((unsigned char)*valp) && 14293262a01Ssunil !(*valp == ':' && 14393262a01Ssunil isspace((unsigned char)*(valp + 1)))) 144299c4efeSeric break; 145299c4efeSeric ++valp; 146299c4efeSeric } 147299c4efeSeric if (*valp == '\0') 148299c4efeSeric valp = NULL; 149299c4efeSeric } 150b7b75d53Seric if (valp == NULL) { 151b7b75d53Seric log_warnx("%s: invalid map entry line %d", t->t_config, 152b7b75d53Seric lineno); 153299c4efeSeric goto end; 154299c4efeSeric } 155b7b75d53Seric 156b7b75d53Seric table_add(t, keyp, valp); 157b7b75d53Seric } 158b7b75d53Seric 159b7b75d53Seric if (ferror(fp)) { 160b7b75d53Seric log_warn("%s: getline", t->t_config); 161b7b75d53Seric goto end; 162b7b75d53Seric } 163b7b75d53Seric 1643f95a974Seric /* Accept empty alias files; treat them as hashes */ 1653f95a974Seric if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS) 1663f95a974Seric t->t_type = T_HASH; 1673f95a974Seric 168299c4efeSeric ret = 1; 169299c4efeSeric end: 170c17c2b51Ssunil free(buf); 171299c4efeSeric fclose(fp); 172299c4efeSeric return ret; 17365c4fdfbSgilles } 17465c4fdfbSgilles 17565c4fdfbSgilles static int 17665c4fdfbSgilles table_static_update(struct table *table) 17765c4fdfbSgilles { 17865c4fdfbSgilles struct table *t; 179299c4efeSeric void *p = NULL; 18065c4fdfbSgilles 18165c4fdfbSgilles /* no config ? ok */ 18265c4fdfbSgilles if (table->t_config[0] == '\0') 18365c4fdfbSgilles goto ok; 18465c4fdfbSgilles 185b80b41afSgilles t = table_create(env, "static", table->t_name, "update", table->t_config); 186299c4efeSeric if (!table_config(t)) 18765c4fdfbSgilles goto err; 18865c4fdfbSgilles 189dd047c3bSgilles /* replace former table, frees t */ 19019503c5aSeric while (dict_poproot(&table->t_dict, (void **)&p)) 191299c4efeSeric free(p); 192299c4efeSeric dict_merge(&table->t_dict, &t->t_dict); 193b80b41afSgilles table_destroy(env, t); 19465c4fdfbSgilles 19565c4fdfbSgilles ok: 196dd047c3bSgilles log_info("info: Table \"%s\" successfully updated", table->t_name); 19765c4fdfbSgilles return 1; 19865c4fdfbSgilles 19965c4fdfbSgilles err: 200b80b41afSgilles table_destroy(env, t); 201dd047c3bSgilles log_info("info: Failed to update table \"%s\"", table->t_name); 20265c4fdfbSgilles return 0; 20365c4fdfbSgilles } 20465c4fdfbSgilles 205b700b1d8Seric static int 20665c4fdfbSgilles table_static_open(struct table *table) 20765c4fdfbSgilles { 208b700b1d8Seric table->t_handle = table; 209b700b1d8Seric return 1; 21065c4fdfbSgilles } 21165c4fdfbSgilles 21265c4fdfbSgilles static void 213*a4c00f8aSeric table_static_close(struct table *table) 21465c4fdfbSgilles { 215*a4c00f8aSeric table->t_handle = NULL; 21665c4fdfbSgilles } 21765c4fdfbSgilles 21865c4fdfbSgilles static int 21993cc0b04Seric table_static_lookup(void *hdl, enum table_service service, const char *key, 22093cc0b04Seric char **dst) 22165c4fdfbSgilles { 22265c4fdfbSgilles struct table *m = hdl; 22365c4fdfbSgilles char *line; 22465c4fdfbSgilles int ret; 22565c4fdfbSgilles int (*match)(const char *, const char *) = NULL; 22665c4fdfbSgilles size_t i; 22765c4fdfbSgilles void *iter; 22865c4fdfbSgilles const char *k; 22965c4fdfbSgilles char *v; 23065c4fdfbSgilles 23165c4fdfbSgilles for (i = 0; i < nitems(keycmp); ++i) 23265c4fdfbSgilles if (keycmp[i].service == service) 23365c4fdfbSgilles match = keycmp[i].func; 23465c4fdfbSgilles 23565c4fdfbSgilles line = NULL; 23665c4fdfbSgilles iter = NULL; 23765c4fdfbSgilles ret = 0; 23865c4fdfbSgilles while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) { 23965c4fdfbSgilles if (match) { 24065c4fdfbSgilles if (match(key, k)) { 24165c4fdfbSgilles line = v; 24265c4fdfbSgilles ret = 1; 24365c4fdfbSgilles } 24465c4fdfbSgilles } 24565c4fdfbSgilles else { 24665c4fdfbSgilles if (strcmp(key, k) == 0) { 24765c4fdfbSgilles line = v; 24865c4fdfbSgilles ret = 1; 24965c4fdfbSgilles } 25065c4fdfbSgilles } 25165c4fdfbSgilles if (ret) 25265c4fdfbSgilles break; 25365c4fdfbSgilles } 25465c4fdfbSgilles 255aa0d26b5Seric if (dst == NULL) 25665c4fdfbSgilles return ret ? 1 : 0; 25765c4fdfbSgilles 258299c4efeSeric if (ret == 0) 25965c4fdfbSgilles return 0; 26065c4fdfbSgilles 261aa0d26b5Seric *dst = strdup(line); 262aa0d26b5Seric if (*dst == NULL) 263aa0d26b5Seric return -1; 264aa0d26b5Seric 265aa0d26b5Seric return 1; 26665c4fdfbSgilles } 26765c4fdfbSgilles 26865c4fdfbSgilles static int 269699c3f98Seric table_static_fetch(void *hdl, enum table_service service, char **dst) 27065c4fdfbSgilles { 27165c4fdfbSgilles struct table *t = hdl; 27265c4fdfbSgilles const char *k; 27365c4fdfbSgilles 27465c4fdfbSgilles if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) { 27565c4fdfbSgilles t->t_iter = NULL; 27665c4fdfbSgilles if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) 27765c4fdfbSgilles return 0; 27865c4fdfbSgilles } 27965c4fdfbSgilles 280aa0d26b5Seric if (dst == NULL) 28165c4fdfbSgilles return 1; 28265c4fdfbSgilles 283aa0d26b5Seric *dst = strdup(k); 284aa0d26b5Seric if (*dst == NULL) 285aa0d26b5Seric return -1; 286aa0d26b5Seric 287aa0d26b5Seric return 1; 28865c4fdfbSgilles } 289