1 /* $OpenBSD: table_db.c,v 1.25 2021/09/22 17:09:07 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/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 "db", 45 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 table_db_config, 49 NULL, 50 NULL, 51 table_db_open, 52 table_db_update, 53 table_db_close, 54 table_db_lookup, 55 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 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 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 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 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 * 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 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 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 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 * 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 * 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