xref: /openbsd/usr.sbin/smtpd/table_static.c (revision 19503c5a)
1*19503c5aSeric /*	$OpenBSD: table_static.c,v 1.7 2013/11/18 11:47:16 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>
3565c4fdfbSgilles #include <string.h>
3665c4fdfbSgilles 
3765c4fdfbSgilles #include "smtpd.h"
3865c4fdfbSgilles #include "log.h"
3965c4fdfbSgilles 
4065c4fdfbSgilles /* static backend */
41299c4efeSeric static int table_static_config(struct table *);
4265c4fdfbSgilles static int table_static_update(struct table *);
4365c4fdfbSgilles static void *table_static_open(struct table *);
4465c4fdfbSgilles static int table_static_lookup(void *, const char *, enum table_service,
45299c4efeSeric     union lookup *);
46299c4efeSeric static int table_static_fetch(void *, enum table_service, union lookup *);
4765c4fdfbSgilles static void  table_static_close(void *);
48299c4efeSeric static int table_static_parse(struct table *, const char *, enum table_type);
4965c4fdfbSgilles 
5065c4fdfbSgilles struct table_backend table_backend_static = {
5165c4fdfbSgilles 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME,
5265c4fdfbSgilles 	table_static_config,
5365c4fdfbSgilles 	table_static_open,
5465c4fdfbSgilles 	table_static_update,
5565c4fdfbSgilles 	table_static_close,
5665c4fdfbSgilles 	table_static_lookup,
5765c4fdfbSgilles 	table_static_fetch
5865c4fdfbSgilles };
5965c4fdfbSgilles 
6065c4fdfbSgilles static struct keycmp {
6165c4fdfbSgilles 	enum table_service	service;
6265c4fdfbSgilles 	int		       (*func)(const char *, const char *);
6365c4fdfbSgilles } keycmp[] = {
6465c4fdfbSgilles 	{ K_DOMAIN, table_domain_match },
6565c4fdfbSgilles 	{ K_NETADDR, table_netaddr_match },
6665c4fdfbSgilles 	{ K_MAILADDR, table_mailaddr_match }
6765c4fdfbSgilles };
6865c4fdfbSgilles 
6965c4fdfbSgilles 
7065c4fdfbSgilles static int
71299c4efeSeric table_static_config(struct table *table)
7265c4fdfbSgilles {
7365c4fdfbSgilles 	/* no config ? ok */
74299c4efeSeric 	if (*table->t_config == '\0')
7565c4fdfbSgilles 		return 1;
7665c4fdfbSgilles 
77299c4efeSeric 	return table_static_parse(table, table->t_config, T_LIST|T_HASH);
78299c4efeSeric }
79299c4efeSeric 
80299c4efeSeric static int
81299c4efeSeric table_static_parse(struct table *t, const char *config, enum table_type type)
82299c4efeSeric {
83299c4efeSeric 	FILE	*fp;
84299c4efeSeric 	char	*buf, *lbuf;
85299c4efeSeric 	size_t	 flen;
86299c4efeSeric 	char	*keyp;
87299c4efeSeric 	char	*valp;
88299c4efeSeric 	size_t	 ret = 0;
89299c4efeSeric 
90299c4efeSeric 	fp = fopen(config, "r");
91299c4efeSeric 	if (fp == NULL)
92299c4efeSeric 		return 0;
93299c4efeSeric 
94299c4efeSeric 	lbuf = NULL;
95299c4efeSeric 	while ((buf = fgetln(fp, &flen))) {
96299c4efeSeric 		if (buf[flen - 1] == '\n')
97299c4efeSeric 			buf[flen - 1] = '\0';
98299c4efeSeric 		else {
99299c4efeSeric 			lbuf = xmalloc(flen + 1, "table_config_parse");
100299c4efeSeric 			memcpy(lbuf, buf, flen);
101299c4efeSeric 			lbuf[flen] = '\0';
102299c4efeSeric 			buf = lbuf;
103299c4efeSeric 		}
104299c4efeSeric 
105299c4efeSeric 		keyp = buf;
106299c4efeSeric 		while (isspace((int)*keyp))
107299c4efeSeric 			++keyp;
108299c4efeSeric 		if (*keyp == '\0' || *keyp == '#')
109299c4efeSeric 			continue;
110299c4efeSeric 		valp = keyp;
111299c4efeSeric 		strsep(&valp, " \t:");
112299c4efeSeric 		if (valp) {
113299c4efeSeric 			while (*valp) {
114299c4efeSeric 				if (!isspace(*valp) &&
115299c4efeSeric 				    !(*valp == ':' && isspace(*(valp + 1))))
116299c4efeSeric 					break;
117299c4efeSeric 				++valp;
118299c4efeSeric 			}
119299c4efeSeric 			if (*valp == '\0')
120299c4efeSeric 				valp = NULL;
121299c4efeSeric 		}
122299c4efeSeric 
123299c4efeSeric 		/**/
124299c4efeSeric 		if (t->t_type == 0)
125299c4efeSeric 			t->t_type = (valp == keyp || valp == NULL) ? T_LIST :
126299c4efeSeric 			    T_HASH;
127299c4efeSeric 
128299c4efeSeric 		if (!(t->t_type & type))
129299c4efeSeric 			goto end;
130299c4efeSeric 
131299c4efeSeric 		if ((valp == keyp || valp == NULL) && t->t_type == T_LIST)
132299c4efeSeric 			table_add(t, keyp, NULL);
133299c4efeSeric 		else if ((valp != keyp && valp != NULL) && t->t_type == T_HASH)
134299c4efeSeric 			table_add(t, keyp, valp);
135299c4efeSeric 		else
136299c4efeSeric 			goto end;
137299c4efeSeric 	}
1383f95a974Seric 	/* Accept empty alias files; treat them as hashes */
1393f95a974Seric 	if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS)
1403f95a974Seric 	    t->t_type = T_HASH;
1413f95a974Seric 
142299c4efeSeric 	ret = 1;
143299c4efeSeric end:
144299c4efeSeric 	free(lbuf);
145299c4efeSeric 	fclose(fp);
146299c4efeSeric 	return ret;
14765c4fdfbSgilles }
14865c4fdfbSgilles 
14965c4fdfbSgilles static int
15065c4fdfbSgilles table_static_update(struct table *table)
15165c4fdfbSgilles {
15265c4fdfbSgilles 	struct table	*t;
153299c4efeSeric 	void		*p = NULL;
15465c4fdfbSgilles 
15565c4fdfbSgilles 	/* no config ? ok */
15665c4fdfbSgilles 	if (table->t_config[0] == '\0')
15765c4fdfbSgilles 		goto ok;
15865c4fdfbSgilles 
159299c4efeSeric 	t = table_create("static", table->t_name, "update", table->t_config);
160299c4efeSeric 	if (!table_config(t))
16165c4fdfbSgilles 		goto err;
16265c4fdfbSgilles 
163dd047c3bSgilles 	/* replace former table, frees t */
164*19503c5aSeric 	while (dict_poproot(&table->t_dict, (void **)&p))
165299c4efeSeric 		free(p);
166299c4efeSeric 	dict_merge(&table->t_dict, &t->t_dict);
167299c4efeSeric 	table_destroy(t);
16865c4fdfbSgilles 
16965c4fdfbSgilles ok:
170dd047c3bSgilles 	log_info("info: Table \"%s\" successfully updated", table->t_name);
17165c4fdfbSgilles 	return 1;
17265c4fdfbSgilles 
17365c4fdfbSgilles err:
17465c4fdfbSgilles 	table_destroy(t);
175dd047c3bSgilles 	log_info("info: Failed to update table \"%s\"", table->t_name);
17665c4fdfbSgilles 	return 0;
17765c4fdfbSgilles }
17865c4fdfbSgilles 
17965c4fdfbSgilles static void *
18065c4fdfbSgilles table_static_open(struct table *table)
18165c4fdfbSgilles {
18265c4fdfbSgilles 	return table;
18365c4fdfbSgilles }
18465c4fdfbSgilles 
18565c4fdfbSgilles static void
18665c4fdfbSgilles table_static_close(void *hdl)
18765c4fdfbSgilles {
18865c4fdfbSgilles 	return;
18965c4fdfbSgilles }
19065c4fdfbSgilles 
19165c4fdfbSgilles static int
19265c4fdfbSgilles table_static_lookup(void *hdl, const char *key, enum table_service service,
193299c4efeSeric     union lookup *lk)
19465c4fdfbSgilles {
19565c4fdfbSgilles 	struct table   *m  = hdl;
19665c4fdfbSgilles 	char	       *line;
19765c4fdfbSgilles 	int		ret;
19865c4fdfbSgilles 	int	       (*match)(const char *, const char *) = NULL;
19965c4fdfbSgilles 	size_t		i;
20065c4fdfbSgilles 	void	       *iter;
20165c4fdfbSgilles 	const char     *k;
20265c4fdfbSgilles 	char	       *v;
20365c4fdfbSgilles 
20465c4fdfbSgilles 	for (i = 0; i < nitems(keycmp); ++i)
20565c4fdfbSgilles 		if (keycmp[i].service == service)
20665c4fdfbSgilles 			match = keycmp[i].func;
20765c4fdfbSgilles 
20865c4fdfbSgilles 	line = NULL;
20965c4fdfbSgilles 	iter = NULL;
21065c4fdfbSgilles 	ret = 0;
21165c4fdfbSgilles 	while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) {
21265c4fdfbSgilles 		if (match) {
21365c4fdfbSgilles 			if (match(key, k)) {
21465c4fdfbSgilles 				line = v;
21565c4fdfbSgilles 				ret = 1;
21665c4fdfbSgilles 			}
21765c4fdfbSgilles 		}
21865c4fdfbSgilles 		else {
21965c4fdfbSgilles 			if (strcmp(key, k) == 0) {
22065c4fdfbSgilles 				line = v;
22165c4fdfbSgilles 				ret = 1;
22265c4fdfbSgilles 			}
22365c4fdfbSgilles 		}
22465c4fdfbSgilles 		if (ret)
22565c4fdfbSgilles 			break;
22665c4fdfbSgilles 	}
22765c4fdfbSgilles 
228299c4efeSeric 	if (lk == NULL)
22965c4fdfbSgilles 		return ret ? 1 : 0;
23065c4fdfbSgilles 
231299c4efeSeric 	if (ret == 0)
23265c4fdfbSgilles 		return 0;
23365c4fdfbSgilles 
234299c4efeSeric 	return table_parse_lookup(service, key, line, lk);
23565c4fdfbSgilles }
23665c4fdfbSgilles 
23765c4fdfbSgilles static int
238299c4efeSeric table_static_fetch(void *hdl, enum table_service service, union lookup *lk)
23965c4fdfbSgilles {
24065c4fdfbSgilles 	struct table   *t = hdl;
24165c4fdfbSgilles 	const char     *k;
24265c4fdfbSgilles 
24365c4fdfbSgilles 	if (! dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) {
24465c4fdfbSgilles 		t->t_iter = NULL;
24565c4fdfbSgilles 		if (! dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL))
24665c4fdfbSgilles 			return 0;
24765c4fdfbSgilles 	}
24865c4fdfbSgilles 
249299c4efeSeric 	if (lk == NULL)
25065c4fdfbSgilles 		return 1;
25165c4fdfbSgilles 
252299c4efeSeric 	return table_parse_lookup(service, NULL, k, lk);
25365c4fdfbSgilles }
254