/* Distributed Checksum Clearinghouse * * helper processes for DNS white- and blacklists * * Using separate processes allows much finer control over timeout that * is possible with the common resolver library. * * --C-LICENSE-- * $Revision: 1.62 $ */ #include "helper.h" #include "dcc_heap_debug.h" #include #include #ifdef HAVE_HELPERS #define HELPERS_DEBUG() (helpers.debug > 2 || helpers.helpers_debug) static void new_gen(void); /* add to the argv list for the helper processes */ void helper_save_arg(const char *flag, const char *value) { char const **new_arg; int i; if (helpers.free_args <= 1) { /* reserve space for the argv[0] and the null terminator */ helpers.free_args += 5; i = (helpers.argc + 2*helpers.free_args) * sizeof(char *); new_arg = dcc_malloc(i); memset(new_arg, 0, i); if (helpers.argv) { for (i = 0; i < helpers.argc; ++i) new_arg[i] = helpers.argv[i]; dcc_free(helpers.argv); } else { ++helpers.argc; } helpers.argv = new_arg; } helpers.argv[helpers.argc] = flag; helpers.argv[++helpers.argc] = value; helpers.argv[++helpers.argc] = 0; --helpers.free_args; } /* initialize things for the parent */ void helper_init(int max_helpers) { helpers.sn = getpid() + time(0); /* If helpers.max_helpers was not set explicitly, * then default to max_work from dccifd or dccm. */ if (helpers.max_helpers == 0) helpers.max_helpers = max_helpers; have_helpers = helper_lock_init(); helper_lock(); new_gen(); helper_unlock(); } /* Work on killing a generation of helpers. */ static void clean_gens(void) { HELPER_GEN *gen, *next_gen; int pid_inx; assert_helper_locked(); if (helpers.is_child) dcc_logbad(EX_SOFTWARE, "bad helper child"); for (gen = helpers.gens; gen; gen = next_gen) { next_gen = gen->fwd; if (gen->ref_cnt > 0) continue; /* The children in an old generation should not phone home. */ if (gen->fin_pipe[0] >= 0) { close(gen->fin_pipe[0]); gen->fin_pipe[0] = -1; } if (gen->fin_pipe[1] >= 0) { close(gen->fin_pipe[1]); gen->fin_pipe[1] = -1; } if (gen->soc != INVALID_SOCKET) { closesocket(gen->soc); gen->soc = INVALID_SOCKET; } /* Shut down the children. */ for (pid_inx = 0; pid_inx < gen->num_pids; ++pid_inx) { if (gen->pids[pid_inx] != 0) kill(gen->pids[pid_inx], SIGTERM); } /* Delete an empty generation */ if (gen->total_helpers == 0) { if (!gen->bak) dcc_logbad(EX_SOFTWARE, "bad gen link"); gen->bak->fwd = gen->fwd; if (gen->fwd) gen->fwd->bak = gen->bak; if (gen->pids) dcc_free(gen->pids); dcc_free(gen); } } } /* Collect work-finished tokens */ static void collect_idle(HELPER_GEN *gen) { u_char buf[32]; int i; assert_helper_locked(); if (!gen || gen->fin_pipe[0] < 0) return; i = read(gen->fin_pipe[0], buf, sizeof(buf)); if (i < 0) { if (!DCC_BLOCK_ERROR()) { dcc_error_msg("DNSBL collect_idle read(): %s", ERROR_STR()); new_gen(); } } else { gen->idle_helpers += i; if (gen->idle_helpers > gen->total_helpers) { dcc_error_msg("DNSBL restart gen %d;" " %d idle helpers but %d total helpers", gen->num, gen->idle_helpers, gen->total_helpers); new_gen(); } } } /* Collect zombies of helpers that died from boredom or otherwise. */ void reap_helpers(void) { HELPER_GEN *gen; char msg[80]; const char *msgp; u_char ok; int status; pid_t pid; int pid_inx; assert_helper_locked(); if (helpers.is_child) dcc_logbad(EX_SOFTWARE, "bad helper child"); gen = helpers.gens; if (!gen) return; collect_idle(gen); do { for (pid_inx = 0; pid_inx < gen->num_pids; ++pid_inx) { pid = gen->pids[pid_inx]; if (pid == 0) continue; pid = waitpid(pid, &status, WNOHANG); if (pid <= 0) { if (pid < 0 && errno != EINTR) dcc_error_msg("helper waitpid(): %s", ERROR_STR()); continue; } gen->pids[pid_inx] = 0; #ifdef WIFSTOPPED /* Ignore helpers being debugged. */ if (WIFSTOPPED(status)) { dcc_error_msg("%ld helper stoppped", (long)pid); gen = helpers.gens; continue; } #endif /* We cannot be certain that the helper that we reaped * was idle, but it should have been. */ --gen->idle_helpers; if (--gen->total_helpers < 0) dcc_logbad(EX_SOFTWARE, "DNSBL total helpers=%d", gen->total_helpers); #if !defined(WIFEXITED) || !defined(WEXITSTATUS) #undef WIFEXITED #define WIFEXITED(s) 0 #undef WEXITSTATUS #define WEXITSTATUS(s) 0 #endif #if !defined(WTERMSIG) || !defined(WIFSIGNALED) #undef WTERMSIG #define WTERMSIG(s) 0 #undef WIFSIGNALED #define WIFSIGNALED(s) 0 #endif if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { msgp = "quit"; ok = 1; } else { snprintf(msg, sizeof(msg), "quit with exit(%d)", WEXITSTATUS(status)); msgp = msg; ok = 0; } } else if (WIFSIGNALED(status)) { if (WTERMSIG(status) == SIGTERM) { snprintf(msg, sizeof(msg), "was terminated"); ok = 1; } else { snprintf(msg, sizeof(msg), "was terminated by signal %d", WTERMSIG(status)); ok = 0; } msgp = msg; } else { snprintf(msg, sizeof(msg), "quit with status %d", status); msgp = msg; ok = 0; } if (!ok) dcc_error_msg("DNSBL helper %ld gen %d #%d %s" " %d helpers %d idle", (long)pid, gen->num, pid_inx, msgp, gen->total_helpers, gen->idle_helpers); else if (HELPERS_DEBUG()) dcc_trace_msg("DNSBL helper %ld gen %d #%d %s," " %d helpers %d idle", (long)pid, gen->num, pid_inx, msgp, gen->total_helpers, gen->idle_helpers); } } while ((gen = gen->fwd)); clean_gens(); } void stop_helpers(void) { HELPER_GEN *gen; int pid_inx; assert_helper_locked(); for (gen = helpers.gens; gen; gen = gen->fwd) { for (pid_inx = 0; pid_inx < gen->num_pids; ++pid_inx) { if (gen->pids[pid_inx] != 0) kill(gen->pids[pid_inx], SIGTERM); } } usleep(1000); reap_helpers(); } static void alloc_pids(HELPER_GEN *gen, int new_num_pids) { pid_t *new_pids; if (new_num_pids <= gen->num_pids) dcc_logbad(EX_SOFTWARE, "bad num_pids"); new_pids = dcc_malloc(new_num_pids * sizeof(pid_t)); memset(new_pids, 0, new_num_pids * sizeof(pid_t)); if (gen->pids) { memcpy(new_pids, gen->pids, gen->num_pids * sizeof(pid_t)); dcc_free(gen->pids); } gen->pids = new_pids; gen->num_pids = new_num_pids; } /* Create a new gen of helpers. */ static void new_gen(void) { HELPER_GEN *gen, *old_gen; assert_helper_locked(); gen = dcc_malloc(sizeof(*gen)); memset(gen, 0, sizeof(*gen)); gen->num = ++helpers.gen_num; gen->fin_pipe[0] = -1; gen->fin_pipe[1] = -1; gen->su.sa.sa_family = AF_UNSPEC; gen->soc = INVALID_SOCKET; alloc_pids(gen, 8); old_gen = helpers.gens; gen->fwd = old_gen; if (old_gen) { old_gen->bak = gen; --old_gen->ref_cnt; if (gen->ref_cnt < 0) dcc_logbad(EX_SOFTWARE, "bad main gen->ref_cnt=%d", gen->ref_cnt); } helpers.gens = gen; gen->ref_cnt = 1; } /* The parent is finished with its helper. */ static void help_finish(HELPER_GEN *gen, void *logp, u_char locked, u_char ok) { if (!locked) helper_lock(); collect_idle(gen); --gen->ref_cnt; if (gen->ref_cnt < 0) dcc_logbad(EX_SOFTWARE, "bad gen->ref_cnt=%d", gen->ref_cnt); if (!ok && helpers.gens == gen) { if (HELPERS_DEBUG()) thr_trace_msg(logp, "DNSBL restart; gen %d", gen->num); new_gen(); } clean_gens(); helper_unlock(); } static u_char helper_clnt_soc_connect(DCC_EMSG *emsg, DCC_CLNT_CTXT *ctxt, const DCC_SOCKU *su) { u_char result; dcc_ctxts_lock(); if (!dcc_info_lock(emsg)) { dcc_ctxts_unlock(); return 0; } if (su->sa.sa_family != AF_INET &&su->sa.sa_family != AF_INET6) dcc_logbad(EX_SOFTWARE, "bad helper socket"); result = dcc_clnt_connect(emsg, ctxt, &ctxt->soc[0], su, AF_UNSPEC); dcc_clnt_soc_flush(&ctxt->soc[0]); if (!dcc_info_unlock(0)) result = 0; dcc_ctxts_unlock(); return result; } /* Create the helper socket used by helpers to receive requests */ static u_char create_helper_soc(HELPER_GEN *gen, DCC_CLNT_CTXT *ctxt, void *logp) { sa_family_t soc_family; DCC_CLNT_SRC *src; socklen_t soc_len; static int rcvbuf; static u_char rcvbuf_set = 0; DCC_EMSG emsg; assert_helper_locked(); if (helpers.is_child) dcc_logbad(EX_SOFTWARE, "bad helper child"); /* We are finished if we already have a socket. */ if (gen->soc != INVALID_SOCKET) return 1; /* We want to create a new socket with the same choice of * IPv4 or IPv6 as the DCC client context's socket. */ soc_family = ctxt->soc[0].loc.sa.sa_family; if (soc_family == AF_UNSPEC) soc_family = AF_INET; src = get_clnt_src(ctxt, soc_family); if (src != 0) { dcc_ip2su(&gen->su, &src->ip); } else { dcc_mk_loop_su(&gen->su, soc_family, 0); } if (!udp_create(&emsg, &gen->soc, &gen->su, 0, 0, 0)) { thr_error_msg(logp, "DNSBL udp_create(): %s", emsg.c); new_gen(); return 0; } if (!set_rcvbuf(&emsg, gen->soc, &gen->su, &rcvbuf, &rcvbuf_set)) { thr_error_msg(logp, "DNSBL %s", emsg.c); new_gen(); return 0; } soc_len = sizeof(gen->su); if (0 > getsockname(gen->soc, &gen->su.sa, &soc_len)) { thr_error_msg(logp, "DNSBL helper getsockname(%d): %s", gen->soc, ERROR_STR()); new_gen(); return 0; } dcc_su2str(gen->su_name, sizeof(gen->su_name), &gen->su); if (HELPERS_DEBUG()) dcc_trace_msg("DNSBL gen %d starting with %s", gen->num, gen->su_name); return 1; } static u_char open_pipe(int fds[2], void *logp) { if (fds[0] >= 0) return 1; if (0 > pipe(fds)) { fds[0] = -1; fds[1] = -1; thr_error_msg(logp, "DNSBL helper pipe(): %s", ERROR_STR()); new_gen(); return 0; } if (-1 == fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL, 0) | O_NONBLOCK)) { thr_error_msg(logp, "DNSBL helper fcntl(O_NONBLOCK): %s", ERROR_STR()); new_gen(); return 0; } return 1; } /* Start a new helper. */ static u_char new_helper(HELPER_GEN *gen, DCC_CLNT_CTXT *ctxt, void *logp, const char *id) { pid_t pid; char arg_buf[sizeof("set:")+sizeof(HELPER_PAT)+9+9+9]; char trace_buf[200]; char *bufp; int pid_inx, buf_len, i, j; assert_helper_locked(); if (helpers.is_child) dcc_logbad(EX_SOFTWARE, "bad helper child"); /* Give the helper child a pipe on which to signal the end * of each job, even long after the original mail message has * been handled and the parent thread is gone. */ if (!open_pipe(gen->fin_pipe, logp)) return 0; if (!create_helper_soc(gen, ctxt, logp)) return 0; reap_helpers(); for (pid_inx = 0; ; ++pid_inx) { if (pid_inx >= gen->num_pids) { alloc_pids(gen, gen->num_pids * 2); pid_inx = 0; } if (gen->pids[pid_inx] == 0) break; } fflush(stdout); fflush(stderr); pid = fork(); if (pid < 0) { thr_error_msg(logp, "%s DNSBL helper fork(): %s", id, ERROR_STR()); return 0; } if (pid != 0) { /* this is the parent */ gen->pids[pid_inx] = pid; ++gen->total_helpers; ++gen->idle_helpers; return 1; } /* This is the child. */ dcc_rel_priv(); dcc_clean_stdio(); close(gen->fin_pipe[0]); snprintf(arg_buf, sizeof(arg_buf), "set:"HELPER_PAT, gen->soc, gen->fin_pipe[1], gen->num, pid_inx); helper_save_arg("-B", arg_buf); helpers.argv[0] = dnsbl_progpath.c; buf_len = sizeof(trace_buf); bufp = trace_buf; for (i = 0; i < helpers.argc && buf_len > 2; ++i) { j = snprintf(bufp, buf_len, "%s ", helpers.argv[i]); buf_len -= j; bufp += j; } if (HELPERS_DEBUG()) { clnt_threaded = 0; dcc_trace_msg("DNSBL helper gen %d #%d exec %s", gen->num, pid_inx, trace_buf); } execv(helpers.argv[0], (char * const *)helpers.argv); /* This process should continue at helper_child() via dnsbl.c */ dcc_logbad(EX_UNAVAILABLE, "execv(%s): %s", trace_buf, ERROR_STR()); exit(1); } static u_char helper_alarm_hit; static void helper_alarm(int s DCC_UNUSED) { helper_alarm_hit = 1; } static void send_idle(int fin_fd) { int i; i = write(fin_fd, "x", 1); if (i == 1) return; if (i < 0) { dcc_error_msg("DNSBL helper send_idle write(): %s", ERROR_STR()); } else if (i != 1) { dcc_error_msg("DNSBL helper send_idle write()=%d", i); } exit(1); } /* helpers start here via fork()/exec() in the parent */ void DCC_NORET helper_child(int soc, int fin_fd, int gen, int helper_num) { sigset_t sigs; DCC_SOCKU su; socklen_t su_len; char su_name[DCC_SU2STR_SIZE]; DNSBL_REQ req; int req_len; DNSBL_RESP resp; DCC_SOCKU req_su; struct timeval now; int secs, i; helpers.is_child = 1; su_len = sizeof(su); if (0 > getsockname(soc, &su.sa, &su_len)) dcc_logbad(EX_IOERR, "getsockname(%d): %s", soc, ERROR_STR()); dcc_su2str(su_name, sizeof(su_name), &su); if (HELPERS_DEBUG()) dcc_trace_msg("helper gen %d #%d starting on %s", gen, helper_num, su_name); /* this process inherits via exec() from dccm or dccifd odd signal * blocking from some pthreads implementations including FreeBSD 5.* */ signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGPIPE, SIG_IGN); sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); sigaddset(&sigs, SIGINT); sigaddset(&sigs, SIGTERM); sigaddset(&sigs, SIGALRM); sigprocmask(SIG_UNBLOCK, &sigs, 0); helper_alarm_hit = 0; for (;;) { /* Use SIGALRM to watch for excessive idle time. */ signal(SIGALRM, helper_alarm); #ifdef HAVE_SIGINTERRUPT siginterrupt(SIGALRM, 1); #endif secs = HELPER_IDLE_STOP_SECS / (helper_num + 1); if (secs < 60) secs = 60; alarm(secs); for (;;) { if (helper_alarm_hit) { if (HELPERS_DEBUG()) dcc_trace_msg("idle exit by #%d on %s", helper_num, su_name); exit(0); } su_len = sizeof(req_su); req_len = recvfrom(soc, &req, ISZ(req), 0, &req_su.sa, &su_len); if (req_len == 0) dcc_logbad(EX_IOERR, "recvfrom()=0"); if (req_len < 0) { if (errno != EINTR && !DCC_BLOCK_ERROR()) dcc_logbad(EX_IOERR, "recvfrom(): %s", ERROR_STR()); continue; } /* We might get stray UDP packets. */ if (!DCC_SUnP_EQ(&su, &req_su)) { if (HELPERS_DEBUG()) dcc_trace_msg("DNSBL helper" " request from" " %s instead of %s", dcc_su2str_err(&req_su), su_name); continue; } if (req_len != sizeof(DNSBL_REQ)) { if (HELPERS_DEBUG()) dcc_trace_msg("DNSBL helper" " recvfrom(%s)=%d" " instead of %d", dcc_su2str_err(&req_su), req_len, ISZ(DNSBL_REQ)); continue; } if (req.hdr.magic != HELPER_MAGIC_REQ || req.hdr.version != HELPER_VERSION) { if (HELPERS_DEBUG()) dcc_trace_msg("DNSBL helper" " recvfrom from %s" " has bad magic=%#08x", dcc_su2str_err(&req_su), req.hdr.magic); continue; } break; } alarm(0); gettimeofday(&now, 0); /* do not bother working if it is already too late to answer, * perhaps because a previous helper died */ i = tv_diff2us(&now, &req.hdr.start); if (i >= req.hdr.avail_us) { if (HELPERS_DEBUG()) dcc_trace_msg("%s DNSBL helper" " already too late to start;" " used %.1f of %.1f seconds", req.hdr.id, i / (DCC_US*1.0), req.hdr.avail_us / (DCC_US*1.0)); send_idle(fin_fd); continue; } memset(&resp, 0, sizeof(resp)); resp.hdr.magic = HELPER_MAGIC_RESP; resp.hdr.version = HELPER_VERSION; resp.hdr.sn = req.hdr.sn; /* do the work and send an answer if we have one */ if (!dnsbl_helper_work(&req, &resp)) { send_idle(fin_fd); continue; } /* do not answer if it is too late */ gettimeofday(&now, 0); i = tv_diff2us(&now, &req.hdr.start); if (i > (req.hdr.avail_us + DCC_US/2)) { if (HELPERS_DEBUG()) dcc_trace_msg("%s DNSBL helper" " too late to answer;" " used %.1f of %.1f seconds", req.hdr.id, i / (DCC_US*1.0), req.hdr.avail_us / (DCC_US*1.0)); send_idle(fin_fd); continue; } /* Send the answer. */ i = sendto(soc, &resp, sizeof(resp), 0, &req_su.sa, DCC_SU_LEN(&req_su)); if (i != sizeof(resp) && helpers.debug) { if (i < 0) dcc_error_msg("%s helper sendto(%s): %s", req.hdr.id, dcc_su2str_err(&req_su), ERROR_STR()); else dcc_error_msg("%s helper sendto(%s)=%d", req.hdr.id, dcc_su2str_err(&req_su), i); } send_idle(fin_fd); } } /* ask a helper to do some filtering */ u_char /* 1=got an answer */ ask_helper(DNSBL_WORK *dlw, HELPER_REQ_HDR *req, /* request sent to helper */ int req_len, HELPER_RESP_HDR *resp, /* put answer here */ int resp_len) { DCC_CLNT_CTXT *ctxt; time_t avail_us; /* spend at most this much time */ DCC_EMSG emsg; HELPER_GEN *gen; socklen_t su_len; DCC_SOCKU recv_su; char sustr[DCC_SU2STR_SIZE]; char sustr2[DCC_SU2STR_SIZE]; struct timeval now; time_t us; DCC_POLLFD pollfd; int serrno; int i; if (helpers.is_child) dcc_logbad(EX_SOFTWARE, "bad helper child"); avail_us = dlw->url_us; emsg.c[0] = '\0'; /* keep the lock until we have sent our request and wake-up call * to ensure that some other thread does not shut down all of * the helpers. */ helper_lock(); gettimeofday(&now, 0); gen = helpers.gens; ++gen->ref_cnt; req->sn = ++helpers.sn; ctxt = dlw->dcc_ctxt; if (gen->soc != INVALID_SOCKET && (gen->su.sa.sa_family != ctxt->soc[0].loc.sa.sa_family || (0 != get_clnt_src(ctxt, ctxt->soc[0].loc.sa.sa_family) && !DCC_SUnP_EQ(&ctxt->soc[0].loc, &gen->su)))) { /* Restart all of the helpers if our source IP address has * changed. * That happens when the client context socket switches between * between IPv4 and IPv6 or when the client source IP address is * changed. */ if (HELPERS_DEBUG()) thr_trace_msg(dlw->log_ctxt, "%s DNSBL IP source change", req->id); new_gen(); --gen->ref_cnt; gen = helpers.gens; ++gen->ref_cnt; } else if (DCC_IS_TIME(now.tv_sec, gen->idle_restart, HELPER_IDLE_RESTART) && gen->idle_helpers > 0) { /* If it has been a long time since we used a helper, * then the last of them might be about to die of boredom. * Fix that race by restarting all of them. */ if (HELPERS_DEBUG()) thr_trace_msg(dlw->log_ctxt, "%s DNSBL idle restart", req->id); new_gen(); --gen->ref_cnt; gen = helpers.gens; ++gen->ref_cnt; } /* Start another helper if necessary, * after collecting job-finished tokens from helpers that took * too much time. */ if (gen->idle_helpers <= 0) collect_idle(gen); if (gen->idle_helpers <= 0) { if (gen->total_helpers >= helpers.max_helpers) { thr_error_msg(dlw->log_ctxt, "%s DNSBL %d idle %d total helpers", req->id, gen->idle_helpers, gen->total_helpers); } else if (!new_helper(gen, ctxt, dlw->log_ctxt, req->id)) { help_finish(gen, dlw->log_ctxt, 1, 0); return 0; } } /* Connect the client context socket to the helper's socket. */ if (!DCC_SU_EQ(&ctxt->soc[0].rem, &gen->su) && !helper_clnt_soc_connect(&emsg, ctxt, &gen->su)) { thr_error_msg(dlw->log_ctxt, "DNSBL soc_connect(): %s", emsg.c); help_finish(gen, dlw->log_ctxt, 1, 0); return 0; } /* The resolutions of the limits on the resolver timeout is seconds, * so even on systems where the timeout limits work, * the helper might delay a second or two. * To keep the count of idle helpers as accurate as possible, always * wait at least 1 second for an answer. */ req->avail_us = avail_us; avail_us += DCC_US; gettimeofday(&now, 0); req->start = now; req->magic = HELPER_MAGIC_REQ; req->version = HELPER_VERSION; /* Send the request. */ i = send(ctxt->soc[0].s, req, req_len, 0); if (i != req_len) { if (i < 0) { serrno = errno; thr_error_msg(dlw->log_ctxt, "%s DNSBL send(to %s): %s", req->id, dcc_su2str(sustr, sizeof(sustr), &gen->su), ERROR_STR1(serrno)); } else { thr_error_msg(dlw->log_ctxt, "%s DNSBL send(to %s)=%d != %d", req->id, dcc_su2str(sustr, sizeof(sustr), &gen->su), i, req_len); } help_finish(gen, dlw->log_ctxt, 1, 0); return 0; } --gen->idle_helpers; helper_unlock(); /* Wait for the answer. */ for (;;) { us = avail_us - tv_diff2us(&now, &req->start); if (us < 0) us = 0; pollfd.fd = ctxt->soc[0].s; i = select_poll(&emsg, &pollfd, 1, 1, us); if (i < 0) { thr_error_msg(dlw->log_ctxt, "%s DNSBL %s", req->id, emsg.c); help_finish(gen, dlw->log_ctxt, 0, 0); return 0; } gettimeofday(&now, 0); if (i == 0) { if (HELPERS_DEBUG()) thr_trace_msg(dlw->log_ctxt, "%s DNSBL" " no helper answer for %s" " after %1.f sec", req->id, dlw->tgt.dom.c, tv_diff2us(&now, &req->start) / (DCC_US*1.0)); help_finish(gen, dlw->log_ctxt, 0, 1); return 0; } su_len = sizeof(recv_su); i = recvfrom(ctxt->soc[0].s, resp, resp_len, 0, &recv_su.sa, &su_len); if (i < 0) { if (DCC_BLOCK_ERROR()) continue; thr_error_msg(dlw->log_ctxt, "%s DNSBL recvfrom(): %s", req->id, ERROR_STR()); help_finish(gen, dlw->log_ctxt, 0, 0); return 0; } /* because we are using UDP, we might get stray packets */ if (i != resp_len) { if (HELPERS_DEBUG()) thr_error_msg(dlw->log_ctxt, "%s DNSBL recvfrom(%s)=%d", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), i); continue; } if (!DCC_SUnP_EQ(&gen->su, &recv_su)) { if (HELPERS_DEBUG()) thr_trace_msg(dlw->log_ctxt, "%s DNSBL" " result from %s instead of %s", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), dcc_su2str(sustr2, sizeof(sustr2), &gen->su)); continue; } if (resp->magic != HELPER_MAGIC_RESP || resp->version != HELPER_VERSION || resp->sn != req->sn) { if (HELPERS_DEBUG()) thr_trace_msg(dlw->log_ctxt, "%s DNSBL" " recvfrom(%s) magic=%#08x sn=%d", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), resp->magic, resp->sn); continue; } gen->idle_restart = now.tv_sec + HELPER_IDLE_RESTART; if (helpers.debug > 2) thr_trace_msg(dlw->log_ctxt,"%s DNSBL answer from" " %s for %s", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), dlw->tgt.dom.c); help_finish(gen, dlw->log_ctxt, 0, 1); return 1; } } #endif /* HAVE_HELPERS */