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 */ 30static int table_db_config(struct table *); 31static int table_db_update(struct table *); 32static int table_db_open(struct table *); 33static void *table_db_open2(struct table *); 34static int table_db_lookup(struct table *, enum table_service, const char *, char **); 35static int table_db_fetch(struct table *, enum table_service, char **); 36static void table_db_close(struct table *); 37static void table_db_close2(void *); 38 39static char *table_db_get_entry(void *, const char *, size_t *); 40static char *table_db_get_entry_match(void *, const char *, size_t *, 41 int(*)(const char *, const char *)); 42 43struct 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 58static 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 68struct dbhandle { 69 DB *db; 70 char pathname[PATH_MAX]; 71 time_t mtime; 72 int iter; 73}; 74 75static int 76table_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 88static int 89table_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 102static int 103table_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 111static void 112table_db_close(struct table *table) 113{ 114 table_db_close2(table->t_handle); 115 table->t_handle = NULL; 116} 117 118static void * 119table_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 139error: 140 if (handle->db) 141 handle->db->close(handle->db); 142 free(handle); 143 return NULL; 144} 145 146static void 147table_db_close2(void *hdl) 148{ 149 struct dbhandle *handle = hdl; 150 handle->db->close(handle->db); 151 free(handle); 152} 153 154static int 155table_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 195static int 196table_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 222static char * 223table_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 244static char * 245table_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