| 1 | /************************************************* |
| 2 | * Exim - an Internet mail transport agent * |
| 3 | *************************************************/ |
| 4 | |
| 5 | /* Copyright (c) University of Cambridge 1995 - 2018 */ |
| 6 | /* Copyright (c) The Exim Maintainers 2020 */ |
| 7 | /* See the file NOTICE for conditions of use and distribution. */ |
| 8 | |
| 9 | #include "../exim.h" |
| 10 | #include "lf_functions.h" |
| 11 | |
| 12 | |
| 13 | /************************************************* |
| 14 | * Open entry point * |
| 15 | *************************************************/ |
| 16 | |
| 17 | /* See local README for interface description */ |
| 18 | |
| 19 | static void * |
| 20 | dbmdb_open(const uschar * filename, uschar ** errmsg) |
| 21 | { |
| 22 | uschar * dirname = string_copy(filename); |
| 23 | uschar * s; |
| 24 | EXIM_DB *yield = NULL; |
| 25 | |
| 26 | if ((s = Ustrrchr(dirname, '/'))) *s = '\0'; |
| 27 | EXIM_DBOPEN(filename, dirname, O_RDONLY, 0, &yield); |
| 28 | if (!yield) |
| 29 | { |
| 30 | int save_errno = errno; |
| 31 | *errmsg = string_open_failed(errno, "%s as a %s file", filename, EXIM_DBTYPE); |
| 32 | errno = save_errno; |
| 33 | } |
| 34 | return yield; |
| 35 | } |
| 36 | |
| 37 | |
| 38 | |
| 39 | /************************************************* |
| 40 | * Check entry point * |
| 41 | *************************************************/ |
| 42 | |
| 43 | /* This needs to know more about the underlying files than is good for it! |
| 44 | We need to know what the real file names are in order to check the owners and |
| 45 | modes. If USE_DB is set, we know it is Berkeley DB, which uses an unmodified |
| 46 | file name. If USE_TDB or USE_GDBM is set, we know it is tdb or gdbm, which do |
| 47 | the same. Otherwise, for safety, we have to check for x.db or x.dir and x.pag. |
| 48 | */ |
| 49 | |
| 50 | static BOOL |
| 51 | dbmdb_check(void *handle, const uschar *filename, int modemask, uid_t *owners, |
| 52 | gid_t *owngroups, uschar **errmsg) |
| 53 | { |
| 54 | int rc; |
| 55 | handle = handle; /* Keep picky compilers happy */ |
| 56 | |
| 57 | #if defined(USE_DB) || defined(USE_TDB) || defined(USE_GDBM) |
| 58 | rc = lf_check_file(-1, filename, S_IFREG, modemask, owners, owngroups, |
| 59 | "dbm", errmsg); |
| 60 | #else |
| 61 | { |
| 62 | uschar filebuffer[256]; |
| 63 | (void)sprintf(CS filebuffer, "%.250s.db", filename); |
| 64 | rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups, |
| 65 | "dbm", errmsg); |
| 66 | if (rc < 0) /* stat() failed */ |
| 67 | { |
| 68 | (void)sprintf(CS filebuffer, "%.250s.dir", filename); |
| 69 | rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups, |
| 70 | "dbm", errmsg); |
| 71 | if (rc == 0) /* x.dir was OK */ |
| 72 | { |
| 73 | (void)sprintf(CS filebuffer, "%.250s.pag", filename); |
| 74 | rc = lf_check_file(-1, filebuffer, S_IFREG, modemask, owners, owngroups, |
| 75 | "dbm", errmsg); |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | #endif |
| 80 | |
| 81 | return rc == 0; |
| 82 | } |
| 83 | |
| 84 | |
| 85 | |
| 86 | /************************************************* |
| 87 | * Find entry point * |
| 88 | *************************************************/ |
| 89 | |
| 90 | /* See local README for interface description. This function adds 1 to |
| 91 | the keylength in order to include the terminating zero. */ |
| 92 | |
| 93 | static int |
| 94 | dbmdb_find(void * handle, const uschar * filename, const uschar * keystring, |
| 95 | int length, uschar ** result, uschar ** errmsg, uint * do_cache, |
| 96 | const uschar * opts) |
| 97 | { |
| 98 | EXIM_DB *d = (EXIM_DB *)handle; |
| 99 | EXIM_DATUM key, data; |
| 100 | |
| 101 | filename = filename; /* Keep picky compilers happy */ |
| 102 | errmsg = errmsg; |
| 103 | do_cache = do_cache; |
| 104 | |
| 105 | EXIM_DATUM_INIT(key); /* Some DBM libraries require datums to */ |
| 106 | EXIM_DATUM_INIT(data); /* be cleared before use. */ |
| 107 | EXIM_DATUM_DATA(key) = CS keystring; |
| 108 | EXIM_DATUM_SIZE(key) = length + 1; |
| 109 | |
| 110 | if (EXIM_DBGET(d, key, data)) |
| 111 | { |
| 112 | *result = string_copyn(US EXIM_DATUM_DATA(data), EXIM_DATUM_SIZE(data)); |
| 113 | EXIM_DATUM_FREE(data); /* Some DBM libraries need a free() call */ |
| 114 | return OK; |
| 115 | } |
| 116 | return FAIL; |
| 117 | } |
| 118 | |
| 119 | |
| 120 | |
| 121 | /************************************************* |
| 122 | * Find entry point - no zero on key * |
| 123 | *************************************************/ |
| 124 | |
| 125 | /* See local README for interface description */ |
| 126 | |
| 127 | static int |
| 128 | dbmnz_find(void * handle, const uschar * filename, const uschar * keystring, |
| 129 | int length, uschar ** result, uschar ** errmsg, uint * do_cache, |
| 130 | const uschar * opts) |
| 131 | { |
| 132 | return dbmdb_find(handle, filename, keystring, length-1, result, errmsg, |
| 133 | do_cache, opts); |
| 134 | } |
| 135 | |
| 136 | |
| 137 | |
| 138 | /************************************************* |
| 139 | * Find entry point - zero-joined list key * |
| 140 | *************************************************/ |
| 141 | |
| 142 | /* |
| 143 | * The parameter passed as a key is a list in normal Exim list syntax. |
| 144 | * The elements of that list are joined together on NUL, with no trailing |
| 145 | * NUL, to form the key. |
| 146 | */ |
| 147 | |
| 148 | static int |
| 149 | dbmjz_find(void * handle, const uschar * filename, const uschar * keystring, |
| 150 | int length, uschar ** result, uschar ** errmsg, uint * do_cache, |
| 151 | const uschar * opts) |
| 152 | { |
| 153 | uschar *key_item, *key_buffer, *key_p; |
| 154 | const uschar *key_elems = keystring; |
| 155 | int buflen, bufleft, key_item_len, sep = 0; |
| 156 | |
| 157 | /* To a first approximation, the size of the lookup key needs to be about, |
| 158 | or less than, the length of the delimited list passed in + 1. */ |
| 159 | |
| 160 | buflen = length + 3; |
| 161 | key_buffer = store_get(buflen, is_tainted(keystring)); |
| 162 | |
| 163 | key_buffer[0] = '\0'; |
| 164 | |
| 165 | key_p = key_buffer; |
| 166 | bufleft = buflen; |
| 167 | |
| 168 | /* In all cases of an empty list item, we can set 1 and advance by 1 and then |
| 169 | pick up the trailing NUL from the previous list item, EXCEPT when at the |
| 170 | beginning of the output string, in which case we need to supply that NUL |
| 171 | ourselves. */ |
| 172 | while ((key_item = string_nextinlist(&key_elems, &sep, key_p, bufleft)) != NULL) |
| 173 | { |
| 174 | key_item_len = Ustrlen(key_item) + 1; |
| 175 | if (key_item_len == 1) |
| 176 | { |
| 177 | key_p[0] = '\0'; |
| 178 | if (key_p == key_buffer) |
| 179 | { |
| 180 | key_p[1] = '\0'; |
| 181 | key_item_len += 1; |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | bufleft -= key_item_len; |
| 186 | if (bufleft <= 0) |
| 187 | { |
| 188 | /* The string_nextinlist() will stop at buffer size, but we should always |
| 189 | have at least 1 character extra, so some assumption has failed. */ |
| 190 | *errmsg = string_copy(US"Ran out of buffer space for joining elements"); |
| 191 | return DEFER; |
| 192 | } |
| 193 | key_p += key_item_len; |
| 194 | } |
| 195 | |
| 196 | if (key_p == key_buffer) |
| 197 | { |
| 198 | *errmsg = string_copy(US"empty list key"); |
| 199 | return FAIL; |
| 200 | } |
| 201 | |
| 202 | /* We do not pass in the final NULL; if needed, the list should include an |
| 203 | empty element to put one in. Boundary: key length 1, is a NULL */ |
| 204 | key_item_len = key_p - key_buffer - 1; |
| 205 | |
| 206 | DEBUG(D_lookup) debug_printf_indent("NUL-joined key length: %d\n", key_item_len); |
| 207 | |
| 208 | /* beware that dbmdb_find() adds 1 to length to get back terminating NUL, so |
| 209 | because we've calculated the real length, we need to subtract one more here */ |
| 210 | |
| 211 | return dbmdb_find(handle, filename, key_buffer, key_item_len - 1, |
| 212 | result, errmsg, do_cache, opts); |
| 213 | } |
| 214 | |
| 215 | |
| 216 | |
| 217 | /************************************************* |
| 218 | * Close entry point * |
| 219 | *************************************************/ |
| 220 | |
| 221 | /* See local README for interface description */ |
| 222 | |
| 223 | void |
| 224 | static dbmdb_close(void *handle) |
| 225 | { |
| 226 | EXIM_DBCLOSE((EXIM_DB *)handle); |
| 227 | } |
| 228 | |
| 229 | |
| 230 | |
| 231 | /************************************************* |
| 232 | * Version reporting entry point * |
| 233 | *************************************************/ |
| 234 | |
| 235 | /* See local README for interface description. */ |
| 236 | |
| 237 | #include "../version.h" |
| 238 | |
| 239 | void |
| 240 | dbm_version_report(FILE *f) |
| 241 | { |
| 242 | #ifdef DYNLOOKUP |
| 243 | fprintf(f, "Library version: DBM: Exim version %s\n", EXIM_VERSION_STR); |
| 244 | #endif |
| 245 | } |
| 246 | |
| 247 | |
| 248 | lookup_info dbm_lookup_info = { |
| 249 | .name = US"dbm", /* lookup name */ |
| 250 | .type = lookup_absfile, /* uses absolute file name */ |
| 251 | .open = dbmdb_open, /* open function */ |
| 252 | .check = dbmdb_check, /* check function */ |
| 253 | .find = dbmdb_find, /* find function */ |
| 254 | .close = dbmdb_close, /* close function */ |
| 255 | .tidy = NULL, /* no tidy function */ |
| 256 | .quote = NULL, /* no quoting function */ |
| 257 | .version_report = dbm_version_report /* version reporting */ |
| 258 | }; |
| 259 | |
| 260 | lookup_info dbmz_lookup_info = { |
| 261 | .name = US"dbmnz", /* lookup name */ |
| 262 | .type = lookup_absfile, /* uses absolute file name */ |
| 263 | .open = dbmdb_open, /* sic */ /* open function */ |
| 264 | .check = dbmdb_check, /* sic */ /* check function */ |
| 265 | .find = dbmnz_find, /* find function */ |
| 266 | .close = dbmdb_close, /* sic */ /* close function */ |
| 267 | .tidy = NULL, /* no tidy function */ |
| 268 | .quote = NULL, /* no quoting function */ |
| 269 | .version_report = NULL /* no version reporting (redundant) */ |
| 270 | }; |
| 271 | |
| 272 | lookup_info dbmjz_lookup_info = { |
| 273 | .name = US"dbmjz", /* lookup name */ |
| 274 | .type = lookup_absfile, /* uses absolute file name */ |
| 275 | .open = dbmdb_open, /* sic */ /* open function */ |
| 276 | .check = dbmdb_check, /* sic */ /* check function */ |
| 277 | .find = dbmjz_find, /* find function */ |
| 278 | .close = dbmdb_close, /* sic */ /* close function */ |
| 279 | .tidy = NULL, /* no tidy function */ |
| 280 | .quote = NULL, /* no quoting function */ |
| 281 | .version_report = NULL /* no version reporting (redundant) */ |
| 282 | }; |
| 283 | |
| 284 | #ifdef DYNLOOKUP |
| 285 | #define dbmdb_lookup_module_info _lookup_module_info |
| 286 | #endif |
| 287 | |
| 288 | static lookup_info *_lookup_list[] = { &dbm_lookup_info, &dbmz_lookup_info, &dbmjz_lookup_info }; |
| 289 | lookup_module_info dbmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 3 }; |
| 290 | |
| 291 | /* End of lookups/dbmdb.c */ |