static void iter_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp) { const char *cache_key; const char *cache_val; const char *what; const char *suffix; if (dict_cache_sequence(dp, cp->first_next, &cache_key, &cache_val) == 0) { if (strcmp(cache_key, cache_val) != 0) msg_warn("value \"%s\" differs from key \"%s\"", cache_val, cache_key); suffix = cache_key + strspn(cache_key, "0123456789"); if (suffix[0] == '-' && strcmp(suffix + 1, cp->suffix) == 0) { cp->done += 1; cp->todo = cp->done + 1; /* XXX */ if ((cp->flags & DICT_CACHE_SREQ_FLAG_PURGE) && dict_cache_delete(dp, cache_key) != 0) { if (dp->error) msg_warn("purge_action: delete failed: %s: %m", STR(bp)); else msg_warn("purge_action: delete failed: %s", STR(bp)); } } cp->first_next = DICT_SEQ_FUN_NEXT; } else { what = (cp->flags & DICT_CACHE_SREQ_FLAG_PURGE) ? "purge" : "count"; if (dp->error) msg_warn("%s error after %d: %m", what, cp->done); else vstream_printf("suffix=%s %s=%d\n", cp->suffix, what, cp->done); cp->todo = 0; } }
static void delete_action(DICT_CACHE_SREQ *cp, DICT_CACHE *dp, VSTRING *bp) { make_tagged_key(bp, cp); if (dict_cache_delete(dp, STR(bp)) != 0) { if (dp->error) msg_warn("delete_action: delete failed: %s: %m", STR(bp)); else msg_warn("delete_action: delete failed: %s", STR(bp)); } cp->done += 1; }
static void verify_query_service(VSTREAM *client_stream) { VSTRING *addr = vstring_alloc(10); VSTRING *get_buf = 0; VSTRING *put_buf = 0; const char *raw_data; int addr_status; long probed; long updated; char *text; if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_ADDR, addr), ATTR_TYPE_END) == 1) { long now = (long) time((time_t *) 0); /* * Produce a default record when no usable record exists. * * If negative caching is disabled, purge an expired record from the * database. * * XXX Assume that a probe is lost if no response is received in 1000 * seconds. If this number is too small the queue will slowly fill up * with delayed probes. * * XXX Maintain a moving average for the probe turnaround time, and * allow probe "retransmission" when a probe is outstanding for, say * some minimal amount of time (1000 sec) plus several times the * observed probe turnaround time. This causes probing to back off * when the mail system becomes congested. */ #define POSITIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_exp < now) #define NEGATIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_exp < now) #define PROBE_TTL 1000 /* FIX 200501 IPv6 patch did not neuter ":" in address literals. */ translit(STR(addr), ":", "_"); if ((raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0 /* not found */ || ((get_buf = vstring_alloc(10)), vstring_strcpy(get_buf, raw_data), /* malformed */ verify_parse_entry(STR(get_buf), &addr_status, &probed, &updated, &text) < 0) || (now - probed > PROBE_TTL /* safe to probe */ && (POSITIVE_ENTRY_EXPIRED(addr_status, updated) || NEGATIVE_ENTRY_EXPIRED(addr_status, updated)))) { addr_status = DEL_RCPT_STAT_TODO; probed = 0; updated = 0; text = "Address verification in progress"; if (raw_data != 0 && var_verify_neg_cache == 0) dict_cache_delete(verify_map, STR(addr)); } if (msg_verbose) msg_info("GOT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, probed, updated, text); /* * Respond to the client. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK), SEND_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status), SEND_ATTR_STR(MAIL_ATTR_WHY, text), ATTR_TYPE_END); /* * Send a new probe when the information needs to be refreshed. * * XXX For an initial proof of concept implementation, use synchronous * mail submission. This needs to be made async for high-volume * sites, which makes it even more interesting to eliminate duplicate * queries while a probe is being built. * * If negative caching is turned off, update the database only when * refreshing an existing entry. */ #define POSITIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_try < now) #define NEGATIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_try < now) if (now - probed > PROBE_TTL && (POSITIVE_REFRESH_NEEDED(addr_status, updated) || NEGATIVE_REFRESH_NEEDED(addr_status, updated))) { if (msg_verbose) msg_info("PROBE %s status=%d probed=%ld updated=%ld", STR(addr), addr_status, now, updated); post_mail_fopen_async(make_verify_sender_addr(), STR(addr), MAIL_SRC_MASK_VERIFY, DEL_REQ_FLAG_MTA_VRFY, SMTPUTF8_FLAG_NONE, (VSTRING *) 0, verify_post_mail_action, (void *) 0); if (updated != 0 || var_verify_neg_cache != 0) { put_buf = vstring_alloc(10); verify_make_entry(put_buf, addr_status, now, updated, text); if (msg_verbose) msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, now, updated, text); dict_cache_update(verify_map, STR(addr), STR(put_buf)); } } } vstring_free(addr); if (get_buf) vstring_free(get_buf); if (put_buf) vstring_free(put_buf); }