1 /* $OpenBSD: table_db.c,v 1.26 2024/05/14 13:28:08 op 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/stat.h>
20
21 #include <db.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "smtpd.h"
27 #include "log.h"
28
29 /* db(3) backend */
30 static int table_db_config(struct table *);
31 static int table_db_update(struct table *);
32 static int table_db_open(struct table *);
33 static void *table_db_open2(struct table *);
34 static int table_db_lookup(struct table *, enum table_service, const char *, char **);
35 static int table_db_fetch(struct table *, enum table_service, char **);
36 static void table_db_close(struct table *);
37 static void table_db_close2(void *);
38
39 static char *table_db_get_entry(void *, const char *, size_t *);
40 static char *table_db_get_entry_match(void *, const char *, size_t *,
41 int(*)(const char *, const char *));
42
43 struct table_backend table_backend_db = {
44 .name = "db",
45 .services = K_ALIAS|K_CREDENTIALS|K_DOMAIN|K_NETADDR|K_USERINFO|
46 K_SOURCE|K_MAILADDR|K_ADDRNAME|K_MAILADDRMAP|K_RELAYHOST|
47 K_STRING|K_REGEX,
48 .config = table_db_config,
49 .add = NULL,
50 .dump = NULL,
51 .open = table_db_open,
52 .update = table_db_update,
53 .close = table_db_close,
54 .lookup = table_db_lookup,
55 .fetch = table_db_fetch,
56 };
57
58 static struct keycmp {
59 enum table_service service;
60 int (*func)(const char *, const char *);
61 } keycmp[] = {
62 { K_DOMAIN, table_domain_match },
63 { K_NETADDR, table_netaddr_match },
64 { K_MAILADDR, table_mailaddr_match },
65 { K_REGEX, table_regex_match },
66 };
67
68 struct dbhandle {
69 DB *db;
70 char pathname[PATH_MAX];
71 time_t mtime;
72 int iter;
73 };
74
75 static int
table_db_config(struct table * table)76 table_db_config(struct table *table)
77 {
78 struct dbhandle *handle;
79
80 handle = table_db_open2(table);
81 if (handle == NULL)
82 return 0;
83
84 table_db_close2(handle);
85 return 1;
86 }
87
88 static int
table_db_update(struct table * table)89 table_db_update(struct table *table)
90 {
91 struct dbhandle *handle;
92
93 handle = table_db_open2(table);
94 if (handle == NULL)
95 return 0;
96
97 table_db_close2(table->t_handle);
98 table->t_handle = handle;
99 return 1;
100 }
101
102 static int
table_db_open(struct table * table)103 table_db_open(struct table *table)
104 {
105 table->t_handle = table_db_open2(table);
106 if (table->t_handle == NULL)
107 return 0;
108 return 1;
109 }
110
111 static void
table_db_close(struct table * table)112 table_db_close(struct table *table)
113 {
114 table_db_close2(table->t_handle);
115 table->t_handle = NULL;
116 }
117
118 static void *
table_db_open2(struct table * table)119 table_db_open2(struct table *table)
120 {
121 struct dbhandle *handle;
122 struct stat sb;
123
124 handle = xcalloc(1, sizeof *handle);
125 if (strlcpy(handle->pathname, table->t_config, sizeof handle->pathname)
126 >= sizeof handle->pathname)
127 goto error;
128
129 if (stat(handle->pathname, &sb) == -1)
130 goto error;
131
132 handle->mtime = sb.st_mtime;
133 handle->db = dbopen(table->t_config, O_RDONLY, 0600, DB_HASH, NULL);
134 if (handle->db == NULL)
135 goto error;
136
137 return handle;
138
139 error:
140 if (handle->db)
141 handle->db->close(handle->db);
142 free(handle);
143 return NULL;
144 }
145
146 static void
table_db_close2(void * hdl)147 table_db_close2(void *hdl)
148 {
149 struct dbhandle *handle = hdl;
150 handle->db->close(handle->db);
151 free(handle);
152 }
153
154 static int
table_db_lookup(struct table * table,enum table_service service,const char * key,char ** dst)155 table_db_lookup(struct table *table, enum table_service service, const char *key,
156 char **dst)
157 {
158 struct dbhandle *handle = table->t_handle;
159 char *line;
160 size_t len = 0;
161 int ret;
162 int (*match)(const char *, const char *) = NULL;
163 size_t i;
164 struct stat sb;
165
166 if (stat(handle->pathname, &sb) == -1)
167 return -1;
168
169 /* DB has changed, close and reopen */
170 if (sb.st_mtime != handle->mtime) {
171 table_db_update(table);
172 handle = table->t_handle;
173 }
174
175 for (i = 0; i < nitems(keycmp); ++i)
176 if (keycmp[i].service == service)
177 match = keycmp[i].func;
178
179 if (match == NULL)
180 line = table_db_get_entry(handle, key, &len);
181 else
182 line = table_db_get_entry_match(handle, key, &len, match);
183 if (line == NULL)
184 return 0;
185
186 ret = 1;
187 if (dst)
188 *dst = line;
189 else
190 free(line);
191
192 return ret;
193 }
194
195 static int
table_db_fetch(struct table * table,enum table_service service,char ** dst)196 table_db_fetch(struct table *table, enum table_service service, char **dst)
197 {
198 struct dbhandle *handle = table->t_handle;
199 DBT dbk;
200 DBT dbd;
201 int r;
202
203 if (handle->iter == 0)
204 r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
205 else
206 r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT);
207 handle->iter = 1;
208 if (!r) {
209 r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST);
210 if (!r)
211 return 0;
212 }
213
214 *dst = strdup(dbk.data);
215 if (*dst == NULL)
216 return -1;
217
218 return 1;
219 }
220
221
222 static char *
table_db_get_entry_match(void * hdl,const char * key,size_t * len,int (* func)(const char *,const char *))223 table_db_get_entry_match(void *hdl, const char *key, size_t *len,
224 int(*func)(const char *, const char *))
225 {
226 struct dbhandle *handle = hdl;
227 DBT dbk;
228 DBT dbd;
229 int r;
230 char *buf = NULL;
231
232 for (r = handle->db->seq(handle->db, &dbk, &dbd, R_FIRST); !r;
233 r = handle->db->seq(handle->db, &dbk, &dbd, R_NEXT)) {
234 buf = xmemdup(dbk.data, dbk.size);
235 if (func(key, buf)) {
236 *len = dbk.size;
237 return buf;
238 }
239 free(buf);
240 }
241 return NULL;
242 }
243
244 static char *
table_db_get_entry(void * hdl,const char * key,size_t * len)245 table_db_get_entry(void *hdl, const char *key, size_t *len)
246 {
247 struct dbhandle *handle = hdl;
248 DBT dbk;
249 DBT dbv;
250 char pkey[LINE_MAX];
251
252 /* workaround the stupidity of the DB interface */
253 if (strlcpy(pkey, key, sizeof pkey) >= sizeof pkey)
254 fatalx("table_db_get_entry: key too long");
255 dbk.data = pkey;
256 dbk.size = strlen(pkey) + 1;
257
258 if (handle->db->get(handle->db, &dbk, &dbv, 0) != 0)
259 return NULL;
260
261 *len = dbv.size;
262
263 return xmemdup(dbv.data, dbv.size);
264 }
265