xref: /openbsd/usr.sbin/smtpd/table_static.c (revision 93cc0b04)
1 /*	$OpenBSD: table_static.c,v 1.24 2018/12/26 20:13:43 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
5  * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/queue.h>
22 #include <sys/tree.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 
28 #include <ctype.h>
29 #include <err.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <imsg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <limits.h>
36 #include <string.h>
37 
38 #include "smtpd.h"
39 #include "log.h"
40 
41 /* static backend */
42 static int table_static_config(struct table *);
43 static int table_static_update(struct table *);
44 static void *table_static_open(struct table *);
45 static int table_static_lookup(void *, enum table_service, const char *,
46     char **);
47 static int table_static_fetch(void *, enum table_service, char **);
48 static void  table_static_close(void *);
49 
50 struct table_backend table_backend_static = {
51 	"static",
52 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
53 	K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
54 	K_STRING|K_REGEX,
55 	table_static_config,
56 	table_static_open,
57 	table_static_update,
58 	table_static_close,
59 	table_static_lookup,
60 	table_static_fetch
61 };
62 
63 static struct keycmp {
64 	enum table_service	service;
65 	int		       (*func)(const char *, const char *);
66 } keycmp[] = {
67 	{ K_DOMAIN, table_domain_match },
68 	{ K_NETADDR, table_netaddr_match },
69 	{ K_MAILADDR, table_mailaddr_match },
70 	{ K_REGEX, table_regex_match },
71 };
72 
73 
74 static int
75 table_static_config(struct table *t)
76 {
77 	FILE	*fp;
78 	char	*buf = NULL, *p;
79 	int	 lineno = 0;
80 	size_t	 sz = 0;
81 	ssize_t	 flen;
82 	char	*keyp;
83 	char	*valp;
84 	size_t	 ret = 0;
85 
86 	/* no config ? ok */
87 	if (*t->t_config == '\0')
88 		return 1;
89 
90 	if ((fp = fopen(t->t_config, "r")) == NULL) {
91 		log_warn("warn: Table \"%s\"", t->t_config);
92 		return 0;
93 	}
94 
95 	while ((flen = getline(&buf, &sz, fp)) != -1) {
96 		lineno++;
97 		if (buf[flen - 1] == '\n')
98 			buf[--flen] = '\0';
99 
100 		keyp = buf;
101 		while (isspace((unsigned char)*keyp)) {
102 			++keyp;
103 			--flen;
104 		}
105 		if (*keyp == '\0')
106 			continue;
107 		while (isspace((unsigned char)keyp[flen - 1]))
108 			keyp[--flen] = '\0';
109 		if (*keyp == '#') {
110 			if (t->t_type == T_NONE) {
111 				keyp++;
112 				while (isspace((unsigned char)*keyp))
113 					++keyp;
114 				if (!strcmp(keyp, "@list"))
115 					t->t_type = T_LIST;
116 			}
117 			continue;
118 		}
119 
120 		if (t->t_type == T_NONE) {
121 			for (p = keyp; *p; p++) {
122 				if (*p == ' ' || *p == '\t' || *p == ':') {
123 					t->t_type = T_HASH;
124 					break;
125 				}
126 			}
127 			if (t->t_type == T_NONE)
128 				t->t_type = T_LIST;
129 		}
130 
131 		if (t->t_type == T_LIST) {
132 			table_add(t, keyp, NULL);
133 			continue;
134 		}
135 
136 		/* T_HASH */
137 		valp = keyp;
138 		strsep(&valp, " \t:");
139 		if (valp) {
140 			while (*valp) {
141 				if (!isspace((unsigned char)*valp) &&
142 				    !(*valp == ':' &&
143 				    isspace((unsigned char)*(valp + 1))))
144 					break;
145 				++valp;
146 			}
147 			if (*valp == '\0')
148 				valp = NULL;
149 		}
150 		if (valp == NULL) {
151 			log_warnx("%s: invalid map entry line %d", t->t_config,
152 			    lineno);
153 			goto end;
154 		}
155 
156 		table_add(t, keyp, valp);
157 	}
158 
159 	if (ferror(fp)) {
160 		log_warn("%s: getline", t->t_config);
161 		goto end;
162 	}
163 
164 	/* Accept empty alias files; treat them as hashes */
165 	if (t->t_type == T_NONE && t->t_backend->services & K_ALIAS)
166 	    t->t_type = T_HASH;
167 
168 	ret = 1;
169 end:
170 	free(buf);
171 	fclose(fp);
172 	return ret;
173 }
174 
175 static int
176 table_static_update(struct table *table)
177 {
178 	struct table	*t;
179 	void		*p = NULL;
180 
181 	/* no config ? ok */
182 	if (table->t_config[0] == '\0')
183 		goto ok;
184 
185 	t = table_create(env, "static", table->t_name, "update", table->t_config);
186 	if (!table_config(t))
187 		goto err;
188 
189 	/* replace former table, frees t */
190 	while (dict_poproot(&table->t_dict, (void **)&p))
191 		free(p);
192 	dict_merge(&table->t_dict, &t->t_dict);
193 	table_destroy(env, t);
194 
195 ok:
196 	log_info("info: Table \"%s\" successfully updated", table->t_name);
197 	return 1;
198 
199 err:
200 	table_destroy(env, t);
201 	log_info("info: Failed to update table \"%s\"", table->t_name);
202 	return 0;
203 }
204 
205 static void *
206 table_static_open(struct table *table)
207 {
208 	return table;
209 }
210 
211 static void
212 table_static_close(void *hdl)
213 {
214 	return;
215 }
216 
217 static int
218 table_static_lookup(void *hdl, enum table_service service, const char *key,
219     char **dst)
220 {
221 	struct table   *m  = hdl;
222 	char	       *line;
223 	int		ret;
224 	int	       (*match)(const char *, const char *) = NULL;
225 	size_t		i;
226 	void	       *iter;
227 	const char     *k;
228 	char	       *v;
229 
230 	for (i = 0; i < nitems(keycmp); ++i)
231 		if (keycmp[i].service == service)
232 			match = keycmp[i].func;
233 
234 	line = NULL;
235 	iter = NULL;
236 	ret = 0;
237 	while (dict_iter(&m->t_dict, &iter, &k, (void **)&v)) {
238 		if (match) {
239 			if (match(key, k)) {
240 				line = v;
241 				ret = 1;
242 			}
243 		}
244 		else {
245 			if (strcmp(key, k) == 0) {
246 				line = v;
247 				ret = 1;
248 			}
249 		}
250 		if (ret)
251 			break;
252 	}
253 
254 	if (dst == NULL)
255 		return ret ? 1 : 0;
256 
257 	if (ret == 0)
258 		return 0;
259 
260 	*dst = strdup(line);
261 	if (*dst == NULL)
262 		return -1;
263 
264 	return 1;
265 }
266 
267 static int
268 table_static_fetch(void *hdl, enum table_service service, char **dst)
269 {
270 	struct table   *t = hdl;
271 	const char     *k;
272 
273 	if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL)) {
274 		t->t_iter = NULL;
275 		if (!dict_iter(&t->t_dict, &t->t_iter, &k, (void **)NULL))
276 			return 0;
277 	}
278 
279 	if (dst == NULL)
280 		return 1;
281 
282 	*dst = strdup(k);
283 	if (*dst == NULL)
284 		return -1;
285 
286 	return 1;
287 }
288