xref: /openbsd/usr.sbin/smtpd/table_db.c (revision a6445c1d)
1 /*	$OpenBSD: table_db.c,v 1.7 2014/07/08 13:49:09 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/stat.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 <db.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <event.h>
32 #include <fcntl.h>
33 #include <imsg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "smtpd.h"
39 #include "log.h"
40 
41 
42 /* db(3) backend */
43 static int table_db_config(struct table *);
44 static int table_db_update(struct table *);
45 static void *table_db_open(struct table *);
46 static int table_db_lookup(void *, struct dict *, const char *, enum table_service, union lookup *);
47 static int table_db_fetch(void *, struct dict *, enum table_service, union lookup *);
48 static void  table_db_close(void *);
49 
50 static char *table_db_get_entry(void *, const char *, size_t *);
51 static char *table_db_get_entry_match(void *, const char *, size_t *,
52     int(*)(const char *, const char *));
53 
54 struct table_backend table_backend_db = {
55 	K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|K_SOURCE|K_MAILADDR|K_ADDRNAME,
56 	table_db_config,
57 	table_db_open,
58 	table_db_update,
59 	table_db_close,
60 	table_db_lookup,
61 	table_db_fetch,
62 };
63 
64 static struct keycmp {
65 	enum table_service	service;
66 	int		       (*func)(const char *, const char *);
67 } keycmp[] = {
68 	{ K_DOMAIN, table_domain_match },
69 	{ K_NETADDR, table_netaddr_match },
70 	{ K_MAILADDR, table_mailaddr_match }
71 };
72 
73 struct dbhandle {
74 	DB		*db;
75 	char		 pathname[SMTPD_MAXPATHLEN];
76 	time_t		 mtime;
77 	struct table	*table;
78 };
79 
80 static int
81 table_db_config(struct table *table)
82 {
83 	struct dbhandle	       *handle;
84 
85 	handle = table_db_open(table);
86 	if (handle == NULL)
87 		return 0;
88 
89 	table_db_close(handle);
90 	return 1;
91 }
92 
93 static int
94 table_db_update(struct table *table)
95 {
96 	struct dbhandle	*handle;
97 
98 	handle = table_db_open(table);
99 	if (handle == NULL)
100 		return 0;
101 
102 	table_db_close(table->t_handle);
103 	table->t_handle = handle;
104 	return 1;
105 }
106 
107 static void *
108 table_db_open(struct table *table)
109 {
110 	struct dbhandle	       *handle;
111 	struct stat		sb;
112 
113 	handle = xcalloc(1, sizeof *handle, "table_db_open");
114 	if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
115 	    >= sizeof handle->pathname)
116 		goto error;
117 
118 	if (stat(handle->pathname, &sb) < 0)
119 		goto error;
120 
121 	handle->mtime = sb.st_mtime;
122 	handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL);
123 	if (handle->db == NULL)
124 		goto error;
125 	handle->table = table;
126 
127 	return handle;
128 
129 error:
130 	if (handle->db)
131 		handle->db->close(handle->db);
132 	free(handle);
133 	return NULL;
134 }
135 
136 static void
137 table_db_close(void *hdl)
138 {
139 	struct dbhandle	*handle = hdl;
140 	handle->db->close(handle->db);
141 	free(handle);
142 }
143 
144 static int
145 table_db_lookup(void *hdl, struct dict *params, const char *key, enum table_service service,
146     union lookup *lk)
147 {
148 	struct dbhandle	*handle = hdl;
149 	struct table	*table = NULL;
150 	char	       *line;
151 	size_t		len = 0;
152 	int		ret;
153 	int	       (*match)(const char *, const char *) = NULL;
154 	size_t		i;
155 	struct stat	sb;
156 
157 	if (stat(handle->pathname, &sb) < 0)
158 		return -1;
159 
160 	/* DB has changed, close and reopen */
161 	if (sb.st_mtime != handle->mtime) {
162 		table = handle->table;
163 		table_db_update(handle->table);
164 		handle = table->t_handle;
165 	}
166 
167 	for (i = 0; i < nitems(keycmp); ++i)
168 		if (keycmp[i].service == service)
169 			match = keycmp[i].func;
170 
171 	if (match == NULL)
172 		line = table_db_get_entry(handle, key, &len);
173 	else
174 		line = table_db_get_entry_match(handle, key, &len, match);
175 	if (line == NULL)
176 		return 0;
177 
178 	ret = 1;
179 	if (lk)
180 		ret = table_parse_lookup(service, key, line, lk);
181 	free(line);
182 
183 	return ret;
184 }
185 
186 static int
187 table_db_fetch(void *hdl, struct dict *params, enum table_service service, union lookup *lk)
188 {
189 	struct dbhandle	*handle = hdl;
190 	struct table	*table  = handle->table;
191 	DBT dbk;
192 	DBT dbd;
193 	int r;
194 
195 	if (table->t_iter == NULL)
196 		r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
197 	else
198 		r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
199 	table->t_iter = handle->db;
200 	if (!r) {
201 		r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
202 		if (!r)
203 			return 0;
204 	}
205 
206 	return table_parse_lookup(service, NULL, dbk.data, lk);
207 }
208 
209 
210 static char *
211 table_db_get_entry_match(void *hdl, const char *key, size_t *len,
212     int(*func)(const char *, const char *))
213 {
214 	struct dbhandle	*handle = hdl;
215 	DBT dbk;
216 	DBT dbd;
217 	int r;
218 	char *buf = NULL;
219 
220 	for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r;
221 	     r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) {
222 		buf = xmemdup(dbk.data, dbk.size, "table_db_get_entry_cmp");
223 		if (func(key, buf)) {
224 			*len = dbk.size;
225 			return buf;
226 		}
227 		free(buf);
228 	}
229 	return NULL;
230 }
231 
232 static char *
233 table_db_get_entry(void *hdl, const char *key, size_t *len)
234 {
235 	struct dbhandle	*handle = hdl;
236 	int ret;
237 	DBT dbk;
238 	DBT dbv;
239 	char pkey[SMTPD_MAXLINESIZE];
240 
241 	/* workaround the stupidity of the DB interface */
242 	if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey)
243 		errx(1, "table_db_get_entry: key too long");
244 	dbk.data = pkey;
245 	dbk.size = strlen(pkey) + 1;
246 
247 	if ((ret = handle->db->get(handle->db, &dbk, &dbv, 0)) != 0)
248 		return NULL;
249 
250 	*len = dbv.size;
251 
252 	return xmemdup(dbv.data, dbv.size, "table_db_get_entry");
253 }
254