int been_here_check_fixed(BH_TABLE *dup_filter, const char *string) { VSTRING *folded_string; const char *lookup_key; int status; /* * Special processing: case insensitive lookup. */ if (dup_filter->flags & BH_FLAG_FOLD) { folded_string = vstring_alloc(100); lookup_key = casefold(folded_string, string); } else { folded_string = 0; lookup_key = string; } /* * Do the duplicate check. */ status = (htable_locate(dup_filter->table, lookup_key) != 0); if (msg_verbose) msg_info("been_here_check: %s: %d", string, status); /* * Cleanup. */ if (folded_string) vstring_free(folded_string); return (status); }
void psc_free_session_state(PSC_STATE *state) { const char *myname = "psc_free_session_state"; HTABLE_INFO *ht; /* * Update the per-client session count. */ if ((ht = htable_locate(psc_client_concurrency, state->smtp_client_addr)) == 0) msg_panic("%s: unknown client address: %s", myname, state->smtp_client_addr); if (--(ht->value) == 0) htable_delete(psc_client_concurrency, state->smtp_client_addr, (void (*) (char *)) 0); if (state->smtp_client_stream != 0) { event_server_disconnect(state->smtp_client_stream); psc_check_queue_length--; } if (state->smtp_server_fd >= 0) { close(state->smtp_server_fd); psc_post_queue_length--; } if (state->send_buf != 0) state->send_buf = vstring_free(state->send_buf); myfree(state->smtp_client_addr); myfree(state->smtp_client_port); myfree(state->smtp_server_addr); myfree(state->smtp_server_port); if (state->dnsbl_reply) vstring_free(state->dnsbl_reply); if (state->helo_name) myfree(state->helo_name); if (state->sender) myfree(state->sender); if (state->cmd_buffer) vstring_free(state->cmd_buffer); if (state->expand_buf) vstring_free(state->expand_buf); myfree((char *) state); if (psc_check_queue_length < 0 || psc_post_queue_length < 0) msg_panic("bad queue length: check_queue=%d, post_queue=%d", psc_check_queue_length, psc_post_queue_length); /* * Update the stress level. */ if (psc_stress != 0 && psc_check_queue_length <= psc_lowat_check_queue_length) { psc_stress = 0; msg_info("leaving STRESS mode with %d connections", psc_check_queue_length); } }
static DICT *proxy_map_find(const char *map_type_name, int request_flags, int *statp) { DICT *dict; #define PROXY_COLON DICT_TYPE_PROXY ":" #define PROXY_COLON_LEN (sizeof(PROXY_COLON) - 1) #define READ_OPEN_FLAGS O_RDONLY #define WRITE_OPEN_FLAGS (O_RDWR | O_CREAT) /* * Canonicalize the map name. If the map is not on the approved list, * deny the request. */ #define PROXY_MAP_FIND_ERROR_RETURN(x) { *statp = (x); return (0); } while (strncmp(map_type_name, PROXY_COLON, PROXY_COLON_LEN) == 0) map_type_name += PROXY_COLON_LEN; if (strchr(map_type_name, ':') == 0) PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_BAD); if (htable_locate(proxy_auth_maps, map_type_name) == 0) { msg_warn("request for unapproved table: \"%s\"", map_type_name); msg_warn("to approve this table for %s access, list %s:%s in %s:%s", proxy_writer == 0 ? "read-only" : "read-write", DICT_TYPE_PROXY, map_type_name, MAIN_CONF_FILE, proxy_writer == 0 ? VAR_PROXY_READ_MAPS : VAR_PROXY_WRITE_MAPS); PROXY_MAP_FIND_ERROR_RETURN(PROXY_STAT_DENY); } /* * Open one instance of a map for each combination of name+flags. * * Assume that a map instance can be shared among clients with different * paranoia flag settings and with different map lookup flag settings. * * XXX The open() flags are passed implicitly, via the selection of the * service name. For a more sophisticated interface, appropriate subsets * of open() flags should be received directly from the client. */ vstring_sprintf(map_type_name_flags, "%s:%s", map_type_name, dict_flags_str(request_flags & DICT_FLAG_INST_MASK)); if (msg_verbose) msg_info("proxy_map_find: %s", STR(map_type_name_flags)); if ((dict = dict_handle(STR(map_type_name_flags))) == 0) { dict = dict_open(map_type_name, proxy_writer ? WRITE_OPEN_FLAGS : READ_OPEN_FLAGS, request_flags); if (dict == 0) msg_panic("proxy_map_find: dict_open null result"); dict_register(STR(map_type_name_flags), dict); } dict->error = 0; return (dict); }
int tls_mgr_delete(const char *unused_type, const char *key) { if (tls_cache == 0) return TLS_MGR_STAT_ERR; if (htable_locate(tls_cache, key)) { htable_delete(tls_cache, key, free_value); --cache_count; } return (TLS_MGR_STAT_OK); }
PSC_STATE *psc_new_session_state(VSTREAM *stream, const char *client_addr, const char *client_port, const char *server_addr, const char *server_port) { PSC_STATE *state; HTABLE_INFO *ht; state = (PSC_STATE *) mymalloc(sizeof(*state)); PSC_INIT_TESTS(state); if ((state->smtp_client_stream = stream) != 0) psc_check_queue_length++; state->smtp_server_fd = (-1); state->smtp_client_addr = mystrdup(client_addr); state->smtp_client_port = mystrdup(client_port); state->smtp_server_addr = mystrdup(server_addr); state->smtp_server_port = mystrdup(server_port); state->send_buf = vstring_alloc(100); state->test_name = "TEST NAME HERE"; state->dnsbl_reply = 0; state->final_reply = "421 4.3.2 Service currently unavailable\r\n"; state->rcpt_reply = "450 4.3.2 Service currently unavailable\r\n"; state->command_count = 0; state->protocol = MAIL_PROTO_SMTP; state->helo_name = 0; state->sender = 0; state->cmd_buffer = 0; state->read_state = 0; state->ehlo_discard_mask = 0; /* XXX Should be ~0 */ state->expand_buf = 0; state->where = PSC_SMTPD_CMD_CONNECT; /* * Update the stress level. */ if (psc_stress == 0 && psc_check_queue_length >= psc_hiwat_check_queue_length) { psc_stress = 1; msg_info("entering STRESS mode with %d connections", psc_check_queue_length); } /* * Update the per-client session count. */ if ((ht = htable_locate(psc_client_concurrency, client_addr)) == 0) ht = htable_enter(psc_client_concurrency, client_addr, (char *) 0); ht->value += 1; state->client_concurrency = CAST_CHAR_PTR_TO_INT(ht->value); return (state); }
NVTABLE_INFO *nvtable_update(NVTABLE * table, const char *key, const char *value) { NVTABLE_INFO *ht; if ((ht = htable_locate(table, key)) != 0) { myfree(ht->value); } else { ht = htable_enter(table, key, (char *) 0); } ht->value = mystrdup(value); return (ht); }
static void post_jail_init(char *service_name, char **unused_argv) { const char *sep = ", \t\r\n"; char *saved_filter; char *bp; char *type_name; /* * Are we proxy writer? */ if (strcmp(service_name, MAIL_SERVICE_PROXYWRITE) == 0) proxy_writer = 1; else if (strcmp(service_name, MAIL_SERVICE_PROXYMAP) != 0) msg_fatal("service name must be one of %s or %s", MAIL_SERVICE_PROXYMAP, MAIL_SERVICE_PROXYMAP); /* * Pre-allocate buffers. */ request = vstring_alloc(10); request_map = vstring_alloc(10); request_key = vstring_alloc(10); request_value = vstring_alloc(10); map_type_name_flags = vstring_alloc(10); /* * Prepare the pre-approved list of proxied tables. */ saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps : var_proxy_read_maps); proxy_auth_maps = htable_create(13); while ((type_name = mystrtok(&bp, sep)) != 0) { if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN)) continue; do { type_name += PROXY_COLON_LEN; } while (!strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN)); if (strchr(type_name, ':') != 0 && htable_locate(proxy_auth_maps, type_name) == 0) (void) htable_enter(proxy_auth_maps, type_name, (char *) 0); } myfree(saved_filter); /* * Never, ever, get killed by a master signal, as that could corrupt a * persistent database when we're in the middle of an update. */ if (proxy_writer != 0) setsid(); }
int delivered_hdr_find(DELIVERED_HDR_INFO *info, const char *address) { HTABLE_INFO *ht; /* * mail_copy() uses quote_822_local() when writing the Delivered-To: * header. We must therefore apply the same transformation when looking * up the recipient. Lowercase the delivered-to address for consistency. */ quote_822_local(info->buf, address); if (info->flags & FOLD_ADDR_ALL) fold_addr(STR(info->buf), info->flags); ht = htable_locate(info->table, STR(info->buf)); return (ht != 0); }
int tls_mgr_update(const char *unused_type, const char *key, const char *buf, ssize_t len) { HTABLE_INFO *ent; VSTRING *s; if (tls_cache == 0) return TLS_MGR_STAT_ERR; if ((ent = htable_locate(tls_cache, key)) == 0) { s = vstring_alloc(len); ent = htable_enter(tls_cache, key, (char *) s); } else { s = (VSTRING *) ent->value; } vstring_memcpy(s, buf, len); ++cache_count; return (TLS_MGR_STAT_OK); }
static void smtp_scrub_addr_list(HTABLE *cached_addr, DNS_RR **addr_list) { MAI_HOSTADDR_STR hostaddr; DNS_RR *addr; DNS_RR *next; /* * XXX Extend the DNS_RR structure with fields for the printable address * and/or binary sockaddr representations, so that we can avoid repeated * binary->string transformations for the same address. */ for (addr = *addr_list; addr; addr = next) { next = addr->next; if (dns_rr_to_pa(addr, &hostaddr) == 0) { msg_warn("cannot convert type %s record to printable address", dns_strtype(addr->type)); continue; } if (htable_locate(cached_addr, hostaddr.buf)) *addr_list = dns_rr_remove(*addr_list, addr); } }
int been_here_fixed(BH_TABLE *dup_filter, const char *string) { char *folded_string; const char *lookup_key; int status; /* * Special processing: case insensitive lookup. */ if (dup_filter->flags & BH_FLAG_FOLD) { folded_string = mystrdup(string); lookup_key = lowercase(folded_string); } else { folded_string = 0; lookup_key = string; } /* * Do the duplicate check. */ if (htable_locate(dup_filter->table, lookup_key) != 0) { status = 1; } else { if (dup_filter->limit <= 0 || dup_filter->limit > dup_filter->table->used) htable_enter(dup_filter->table, lookup_key, (char *) 0); status = 0; } if (msg_verbose) msg_info("been_here: %s: %d", string, status); /* * Cleanup. */ if (folded_string) myfree(folded_string); return (status); }
static int dict_ht_update(DICT *dict, const char *name, const char *value) { DICT_HT *dict_ht = (DICT_HT *) dict; HTABLE_INFO *ht; char *saved_value = mystrdup(value); /* * Optionally fold the key. */ if (dict->flags & DICT_FLAG_FOLD_FIX) { if (dict->fold_buf == 0) dict->fold_buf = vstring_alloc(10); vstring_strcpy(dict->fold_buf, name); name = lowercase(vstring_str(dict->fold_buf)); } if ((ht = htable_locate(dict_ht->table, name)) != 0) { myfree(ht->value); } else { ht = htable_enter(dict_ht->table, name, (char *) 0); } ht->value = saved_value; DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, DICT_STAT_SUCCESS); }
DICT *dict_thash_open(const char *path, int open_flags, int dict_flags) { DICT_THASH *dict_thash; VSTREAM *fp = 0; struct stat st; time_t before; time_t after; VSTRING *line_buffer = 0; int lineno; char *key; char *value; HTABLE *table; HTABLE_INFO *ht; /* * Let the optimizer worry about eliminating redundant code. */ #define DICT_THASH_OPEN_RETURN(d) { \ DICT *__d = (d); \ if (fp != 0) \ vstream_fclose(fp); \ if (line_buffer != 0) \ vstring_free(line_buffer); \ return (__d); \ } while (0) /* * Sanity checks. */ if (open_flags != O_RDONLY) DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_THASH, path)); /* * Read the flat text file into in-memory hash. Read the file again if it * may have changed while we were reading. */ for (before = time((time_t *) 0); /* see below */ ; before = after) { if ((fp = vstream_fopen(path, open_flags, 0644)) == 0) { DICT_THASH_OPEN_RETURN(dict_surrogate(DICT_TYPE_THASH, path, open_flags, dict_flags, "open database %s: %m", path)); } if (line_buffer == 0) line_buffer = vstring_alloc(100); lineno = 0; table = htable_create(13); while (readlline(line_buffer, fp, &lineno)) { /* * Split on the first whitespace character, then trim leading and * trailing whitespace from key and value. */ key = STR(line_buffer); value = key + strcspn(key, " \t\r\n"); if (*value) *value++ = 0; while (ISSPACE(*value)) value++; trimblanks(key, 0)[0] = 0; trimblanks(value, 0)[0] = 0; /* * Enforce the "key whitespace value" format. Disallow missing * keys or missing values. */ if (*key == 0 || *value == 0) { msg_warn("%s, line %d: expected format: key whitespace value" " -- ignoring this line", path, lineno); continue; } if (key[strlen(key) - 1] == ':') msg_warn("%s, line %d: record is in \"key: value\" format;" " is this an alias file?", path, lineno); /* * Optionally fold the key. */ if (dict_flags & DICT_FLAG_FOLD_FIX) lowercase(key); /* * Store the value under the key. Handle duplicates * appropriately. */ if ((ht = htable_locate(table, key)) != 0) { if (dict_flags & DICT_FLAG_DUP_IGNORE) { /* void */ ; } else if (dict_flags & DICT_FLAG_DUP_REPLACE) { myfree(ht->value); ht->value = mystrdup(value); } else if (dict_flags & DICT_FLAG_DUP_WARN) { msg_warn("%s, line %d: duplicate entry: \"%s\"", path, lineno, key); } else { msg_fatal("%s, line %d: duplicate entry: \"%s\"", path, lineno, key); } } else { htable_enter(table, key, mystrdup(value)); } } /* * See if the source file is hot. */ if (fstat(vstream_fileno(fp), &st) < 0) msg_fatal("fstat %s: %m", path); if (vstream_fclose(fp)) msg_fatal("read %s: %m", path); fp = 0; /* DICT_THASH_OPEN_RETURN() */ after = time((time_t *) 0); if (st.st_mtime < before - 1 || st.st_mtime > after) break; /* * Yes, it is hot. Discard the result and read the file again. */ htable_free(table, myfree); if (msg_verbose > 1) msg_info("pausing to let file %s cool down", path); doze(300000); } /* * Create the in-memory table. */ dict_thash = (DICT_THASH *) dict_alloc(DICT_TYPE_THASH, path, sizeof(*dict_thash)); dict_thash->dict.lookup = dict_thash_lookup; dict_thash->dict.sequence = dict_thash_sequence; dict_thash->dict.close = dict_thash_close; dict_thash->dict.flags = dict_flags | DICT_FLAG_DUP_WARN | DICT_FLAG_FIXED; if (dict_flags & DICT_FLAG_FOLD_FIX) dict_thash->dict.fold_buf = vstring_alloc(10); dict_thash->info = 0; dict_thash->table = table; dict_thash->dict.owner.uid = st.st_uid; dict_thash->dict.owner.status = (st.st_uid != 0); DICT_THASH_OPEN_RETURN(DICT_DEBUG (&dict_thash->dict)); }
int psc_dnsbl_request(const char *client_addr, void (*callback) (int, void *), void *context) { const char *myname = "psc_dnsbl_request"; int fd; VSTREAM *stream; HTABLE_INFO **ht; PSC_DNSBL_SCORE *score; HTABLE_INFO *hash_node; static int request_count; /* * Some spambots make several connections at nearly the same time, * causing their pregreet delays to overlap. Such connections can share * the efforts of DNSBL lookup. * * We store a reference-counted DNSBL score under its client IP address. We * increment the reference count with each score request, and decrement * the reference count with each score retrieval. * * Do not notify the requestor NOW when the DNS replies are already in. * Reason: we must not make a backwards call while we are still in the * middle of executing the corresponding forward call. Instead we create * a zero-delay timer request and call the notification function from * there. * * psc_dnsbl_request() could instead return a result value to indicate that * the DNSBL score is already available, but that would complicate the * caller with two different notification code paths: one asynchronous * code path via the callback invocation, and one synchronous code path * via the psc_dnsbl_request() result value. That would be a source of * future bugs. */ if ((hash_node = htable_locate(dnsbl_score_cache, client_addr)) != 0) { score = (PSC_DNSBL_SCORE *) hash_node->value; score->refcount += 1; PSC_CALL_BACK_EXTEND(hash_node, score); PSC_CALL_BACK_ENTER(score, callback, context); if (msg_verbose > 1) msg_info("%s: reuse blocklist score for %s refcount=%d pending=%d", myname, client_addr, score->refcount, score->pending_lookups); if (score->pending_lookups == 0) event_request_timer(callback, context, EVENT_NULL_DELAY); return (PSC_CALL_BACK_INDEX_OF_LAST(score)); } if (msg_verbose > 1) msg_info("%s: create blocklist score for %s", myname, client_addr); score = (PSC_DNSBL_SCORE *) mymalloc(sizeof(*score)); score->request_id = request_count++; score->dnsbl_name = 0; score->dnsbl_weight = 0; /* As with dnsblog(8), a value < 0 means no reply TTL. */ score->pass_ttl = -1; score->fail_ttl = -1; score->total = 0; score->refcount = 1; score->pending_lookups = 0; PSC_CALL_BACK_INIT(score); PSC_CALL_BACK_ENTER(score, callback, context); (void) htable_enter(dnsbl_score_cache, client_addr, (void *) score); /* * Send a query to all DNSBL servers. Later, DNSBL lookup will be done * with an UDP-based DNS client that is built directly into Postfix code. * We therefore do not optimize the maximum out of this temporary * implementation. */ for (ht = dnsbl_site_list; *ht; ht++) { if ((fd = LOCAL_CONNECT(psc_dnsbl_service, NON_BLOCKING, 1)) < 0) { msg_warn("%s: connect to %s service: %m", myname, psc_dnsbl_service); continue; } stream = vstream_fdopen(fd, O_RDWR); vstream_control(stream, CA_VSTREAM_CTL_CONTEXT(ht[0]->key), CA_VSTREAM_CTL_END); attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, ht[0]->key), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, client_addr), SEND_ATTR_INT(MAIL_ATTR_LABEL, score->request_id), ATTR_TYPE_END); if (vstream_fflush(stream) != 0) { msg_warn("%s: error sending to %s service: %m", myname, psc_dnsbl_service); vstream_fclose(stream); continue; } PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_dnsbl_receive, (void *) stream, var_psc_dnsbl_tmout); score->pending_lookups += 1; } return (PSC_CALL_BACK_INDEX_OF_LAST(score)); }
DICT *dict_sockmap_open(const char *mapname, int open_flags, int dict_flags) { DICT_SOCKMAP *dp; char *saved_name = 0; char *sockmap; DICT_SOCKMAP_REFC_HANDLE *ref_handle; HTABLE_INFO *client_info; /* * Let the optimizer worry about eliminating redundant code. */ #define DICT_SOCKMAP_OPEN_RETURN(d) { \ DICT *__d = (d); \ if (saved_name != 0) \ myfree(saved_name); \ return (__d); \ } while (0) /* * Sanity checks. */ if (open_flags != O_RDONLY) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s:%s map requires O_RDONLY access mode", DICT_TYPE_SOCKMAP, mapname)); if (dict_flags & DICT_FLAG_NO_UNAUTH) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s:%s map is not allowed for security-sensitive data", DICT_TYPE_SOCKMAP, mapname)); /* * Separate the socketmap name from the socketmap server name. */ saved_name = mystrdup(mapname); if ((sockmap = split_at_right(saved_name, ':')) == 0) DICT_SOCKMAP_OPEN_RETURN(dict_surrogate(DICT_TYPE_SOCKMAP, mapname, open_flags, dict_flags, "%s requires server:socketmap argument", DICT_TYPE_SOCKMAP)); /* * Use one reference-counted client handle for all socketmaps with the * same inet:host:port or unix:pathname information. * * XXX Todo: graceful degradation after endpoint syntax error. */ if (dict_sockmap_handles == 0) dict_sockmap_handles = htable_create(1); if ((client_info = htable_locate(dict_sockmap_handles, saved_name)) == 0) { ref_handle = (DICT_SOCKMAP_REFC_HANDLE *) mymalloc(sizeof(*ref_handle)); client_info = htable_enter(dict_sockmap_handles, saved_name, (char *) ref_handle); /* XXX Late initialization, so we can reuse macros for consistency. */ DICT_SOCKMAP_RH_REFCOUNT(client_info) = 1; DICT_SOCKMAP_RH_HANDLE(client_info) = auto_clnt_create(saved_name, dict_sockmap_timeout, dict_sockmap_max_idle, dict_sockmap_max_ttl); } else DICT_SOCKMAP_RH_REFCOUNT(client_info) += 1; /* * Instantiate a socket map handle. */ dp = (DICT_SOCKMAP *) dict_alloc(DICT_TYPE_SOCKMAP, mapname, sizeof(*dp)); dp->rdwr_buf = vstring_alloc(100); dp->sockmap_name = mystrdup(sockmap); dp->client_info = client_info; dp->dict.lookup = dict_sockmap_lookup; dp->dict.close = dict_sockmap_close; /* Don't look up parent domains or network superblocks. */ dp->dict.flags = dict_flags | DICT_FLAG_PATTERN; DICT_SOCKMAP_OPEN_RETURN(DICT_DEBUG (&dp->dict)); }