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