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); }
int main(int argc, char **argv) { const char *verify_sender; const char *valid_sender; msg_vstream_init(argv[0], VSTREAM_ERR); /* * Prepare to talk to the address rewriting service. */ mail_conf_read(); vstream_printf("using config files in %s\n", var_config_dir); if (chdir(var_queue_dir) < 0) msg_fatal("chdir %s: %m", var_queue_dir); /* * Parse JCL. */ if (argc != 3) msg_fatal("usage: %s address_verify_sender address_verify_sender_ttl", argv[0]); var_verify_sender = argv[1]; if (conv_time(argv[2], &var_verify_sender_ttl, 's') == 0) msg_fatal("bad time value: %s", argv[2]); verify_time = time((time_t *) 0); /* * Compute the current probe sender address. */ verify_sender = make_verify_sender_addr(); /* * Check two past time slots. */ if (var_verify_sender_ttl > 0) { verify_time -= 2 * var_verify_sender_ttl; vstream_printf("\"%s\" matches prev2: \"%s\"\n", verify_sender, (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? valid_sender : "nope"); verify_time += var_verify_sender_ttl; vstream_printf("\"%s\" matches prev1: \"%s\"\n", verify_sender, (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? valid_sender : "nope"); verify_time += var_verify_sender_ttl; } /* * Check the current time slot. */ vstream_printf("\"%s\" matches self: \"%s\"\n", verify_sender, (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? valid_sender : "nope"); /* * Check two future time slots. */ if (var_verify_sender_ttl > 0) { verify_time += var_verify_sender_ttl; vstream_printf("\"%s\" matches next1: \"%s\"\n", verify_sender, (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? valid_sender : "nope"); verify_time += var_verify_sender_ttl; vstream_printf("\"%s\" matches next2: \"%s\"\n", verify_sender, (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? valid_sender : "nope"); } vstream_fflush(VSTREAM_OUT); exit(0); }