int cmd_cb_LMIP(struct request *req) { struct tunnel_context *tun; size_t i = 0; struct in_addr addr; assert (req != NULL); if_debug(service, D_DEBUG2("Checking macs from cpe '%s'\n", req->argv[1])); addr.s_addr = inet_addr(req->argv[1]); tun = provision_get_tunnel_byip(&addr, NULL); if (!tun) { request_appendf(req, "RESULT: NOTFOUND\n"); return 0; } helper_lock(); request_appendf(req, "RESULT: OK\nBODY: "); for (; i < PROVISION_MAX_CLIENTS; i++) { const char *cur = tun->filter[i].src_mac; char com = tun->filter[i+1].src_mac[0] != '\0' ? ';' : 0x00; if (cur[0] != '\0') request_appendf(req, "%s%c", cur, com); } helper_unlock(); return 0; }
/** * call_usermodehelper_exec - start a usermode application * @sub_info: information about the subprocessa * @wait: wait for the application to finish and return status. * when -1 don't wait at all, but you get no useful error back when * the program couldn't be exec'ed. This makes it safe to call * from interrupt context. * * Runs a user-space application. The application is started * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ int call_usermodehelper_exec(struct subprocess_info *sub_info, enum umh_wait wait) { DECLARE_COMPLETION_ONSTACK(done); int retval = 0; helper_lock(); if (sub_info->path[0] == '\0') goto out; if (!khelper_wq || usermodehelper_disabled) { retval = -EBUSY; goto out; } sub_info->complete = &done; sub_info->wait = wait; queue_work(khelper_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; wait_for_completion(&done); retval = sub_info->retval; out: call_usermodehelper_freeinfo(sub_info); unlock: helper_unlock(); return retval; }
static void help_finish(u_int gen, u_char ok, u_char counted, u_char locked) { if (!locked) helper_lock(); /* forget it if the children have been restarted */ if (gen == helper.gen) { if (counted) ++helper.idle_helpers; if (!ok) { if (++helper.failures >= HELPER_MAX_FAILURES) { if (helper.debug) dcc_trace_msg("restart DNSBL helpers"); terminate_helpers(); } else { reap_helpers(1); } } } if (!locked) helper_unlock(); }
/** * call_usermodehelper_exec - start a usermode application * @sub_info: information about the subprocessa * @wait: wait for the application to finish and return status. * when -1 don't wait at all, but you get no useful error back when * the program couldn't be exec'ed. This makes it safe to call * from interrupt context. * * Runs a user-space application. The application is started * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). */ int call_usermodehelper_exec(struct subprocess_info *sub_info, int wait) { DECLARE_COMPLETION_ONSTACK(done); int retval = 0; BUG_ON(atomic_read(&sub_info->cred->usage) != 1); validate_creds(sub_info->cred); if (!sub_info->path) { call_usermodehelper_freeinfo(sub_info); return -EINVAL; } helper_lock(); if (sub_info->path[0] == '\0') goto out; if (!khelper_wq || usermodehelper_disabled) { retval = -EBUSY; goto out; } sub_info->complete = &done; sub_info->wait = wait; queue_work(khelper_wq, &sub_info->work); if (wait == UMH_NO_WAIT) /* task has freed sub_info */ goto unlock; if (wait & UMH_KILLABLE) { retval = wait_for_completion_killable(&done); if (!retval) goto wait_done; /* umh_complete() will see NULL and free sub_info */ if (xchg(&sub_info->complete, NULL)) goto unlock; /* fallthrough, umh_complete() was already called */ } wait_for_completion(&done); wait_done: retval = sub_info->retval; out: call_usermodehelper_freeinfo(sub_info); unlock: helper_unlock(); return retval; }
/* ask a helper process to do some filtering */ u_char /* 1=got an answer */ ask_helper(DCC_CLNT_CTXT *ctxt, void *logp, time_t avail_us, /* spend at most this much time */ HELPER_REQ_HDR *req, /* request sent to helper */ int req_len, HELPER_RESP_HDR *resp, /* put answer here */ int resp_len) { DCC_EMSG emsg; DCC_SOCKU send_su; socklen_t su_len; DCC_SOCKU recv_su; char sustr[DCC_SU2STR_SIZE]; char sustr2[DCC_SU2STR_SIZE]; u_char counted; u_int gen; struct timeval now; time_t us; DCC_POLLFD pollfd; int i; emsg[0] = '\0'; /* We will use the client context socket to talk to the helper, * so ensure that it is open */ if (!helper_soc_open(emsg, ctxt)) { thr_trace_msg(logp, "DNSBL reopen %s", emsg); return 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); /* 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. * Most dying helpers should be reaped by the totals timer thread. */ if (helper.idle_helpers > 0 && DCC_IS_TIME(now.tv_sec, helper.idle_restart, HELPER_IDLE_RESTART)) { reap_helpers(1); if (helper.idle_helpers > 0) terminate_helpers(); gettimeofday(&now, 0); } /* Restart all helpers if the current helper socket is the wrong * family. This should happen only when the DCC client library * has chosen a new server */ if (helper.soc != INVALID_SOCKET && ctxt->soc[0].loc.sa.sa_family != AF_UNSPEC && helper.su.sa.sa_family != ctxt->soc[0].loc.sa.sa_family) { terminate_helpers(); gettimeofday(&now, 0); } helper.idle_restart = now.tv_sec + HELPER_IDLE_RESTART; if (helper.idle_helpers - helper.slow_helpers > 0) { /* avoid taking the last idle helper because there are * usually fewer truly idle helpers than we think because * we don't always wait for them to finish */ if (helper.idle_helpers > 2 || helper.total_helpers >= helper.max_helpers || !new_helper(ctxt, logp, req->id)) --helper.idle_helpers; counted = 1; } else if (helper.total_helpers >= helper.max_helpers) { if (helper.debug > 0) thr_trace_msg(logp, "%s DNSBL %d idle, %d slow, and" " %d total DNSBL helpers", req->id, helper.idle_helpers, helper.slow_helpers, helper.total_helpers); counted = 0; } else { if (!new_helper(ctxt, logp, req->id)) { helper_unlock(); return 0; } counted = 1; } /* The resolution of the BIND timeout limits 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 * and 2 seconds for an answer to reach the parent. */ req->avail_us = avail_us; avail_us += DCC_US; req->start = now; req->magic = HELPER_MAGIC_REQ; req->version = HELPER_VERSION; req->sn = ++helper.sn; gen = helper.gen; /* snapshot the address in case another thread restarts the helpers */ send_su = helper.su; /* If the client context socket is connected but not to the helper * socket, * then either disconnect it or connect to the helper's socket */ if (ctxt->soc[0].rem.sa.sa_family != AF_UNSPEC && !DCC_SU_EQ(&ctxt->soc[0].rem, &send_su) && !helper_soc_connect(emsg, ctxt, &send_su)) { thr_trace_msg(logp, "DNSBL soc_connect(): %s", emsg); help_finish(gen, 0, counted, 1); helper_unlock(); return 0; } if (ctxt->soc[0].rem.sa.sa_family == AF_UNSPEC) { i = sendto(ctxt->soc[0].s, req, req_len, 0, &send_su.sa, DCC_SU_LEN(&send_su)); } else { i = send(ctxt->soc[0].s, req, req_len, 0); } if (i != req_len) { if (i < 0) thr_trace_msg(logp, "%s DNSBL sendto(%s): %s", req->id, dcc_su2str(sustr, sizeof(sustr), &send_su), ERROR_STR()); else thr_trace_msg(logp, "%s DNSBL sendto(%s)=%d", req->id, dcc_su2str(sustr, sizeof(sustr), &send_su), i); help_finish(gen, 0, counted, 1); helper_unlock(); return 0; } /* awaken a helper */ i = write(helper.pipe_write, "x", 1); if (i != 1) { if (i < 0) thr_trace_msg(logp, "%s DNSBL write(pipe_write=%d): %s", req->id, helper.pipe_write, ERROR_STR()); else thr_trace_msg(logp, "%s DNSBL write(pipe_write)=%d", req->id, i); help_finish(gen, 0, counted, 1); helper_unlock(); return 0; } helper_unlock(); 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(logp, "%s DNSBL %s", req->id, emsg); help_finish(gen, 0, counted, 0); return 0; } gettimeofday(&now, 0); if (i == 0) { if (helper.debug) thr_trace_msg(logp, "%s DNSBL no helper answer after" " %1.f sec", req->id, tv_diff2us(&now, &req->start) / (DCC_US*1.0)); helper_lock(); if (helper.slow_helpers<=helper.total_helpers/MAX_SLOW) ++helper.slow_helpers; help_finish(gen, 0, counted, 1); helper_unlock(); return 0; } su_len = sizeof(recv_su); i = recvfrom(ctxt->soc[0].s, resp, resp_len, 0, &recv_su.sa, &su_len); /* because we are using UDP, we might get stray packets */ if (i != resp_len) { if (i < 0) { thr_trace_msg(logp, "%s DNSBL recvfrom(): %s", req->id, ERROR_STR()); if (DCC_BLOCK_ERROR()) continue; help_finish(gen, 0, counted, 0); return 0; } if (helper.debug > 1) thr_trace_msg(logp, "%s DNSBL recvfrom(%s)=%d", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), i); continue; } if (!DCC_SUnP_EQ(&send_su, &recv_su)) { if (helper.debug != 0) thr_trace_msg(logp, "%s DNSBL recvfrom(%s)" " instead of %s", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), dcc_su2str(sustr2, sizeof(sustr2), &send_su)); continue; } if (resp->magic != HELPER_MAGIC_RESP || resp->version != HELPER_VERSION || resp->sn != req->sn) { if (helper.debug >1 ) thr_trace_msg(logp, "%s DNSBL recvfrom(%s)" " magic=%#08x sn=%d", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su), resp->magic, resp->sn); continue; } if (helper.debug > 4) thr_trace_msg(logp,"%s DNSBL answer from %s", req->id, dcc_su2str(sustr, sizeof(sustr), &recv_su)); help_finish(gen, 1, counted, 0); return 1; } }
/* collect zombies of helpers that died from boredom or otherwise */ void reap_helpers(u_char locked) { int status; pid_t pid; int pid_inx; if (!locked) helper_lock(); for (;;) { wait_again:; pid = waitpid(0, &status, WNOHANG); if (0 >= pid) { if (!locked) helper_unlock(); return; } for (pid_inx = 0; ; ++pid_inx) { if (pid_inx >= helper.max_helpers) { /* not an acknowledged child */ if (helper.debug >= 1) dcc_trace_msg("%d not a DNSBL" " helper process reaped", (int)pid); goto wait_again; } if (helper.pids[pid_inx] == pid) break; } helper.pids[pid_inx] = 0; /* races with dying helpers can confuse us */ if (--helper.total_helpers < 0) { if (helper.debug) dcc_trace_msg("DNSBL total helpers=%d", helper.total_helpers); helper.total_helpers = 0; } if (helper.slow_helpers > helper.total_helpers/MAX_SLOW) helper.slow_helpers = helper.total_helpers/MAX_SLOW; if (--helper.idle_helpers < 0) { /* We cannot be certain that a helper that quit was * idle, but it should have been. */ if (helper.debug) dcc_trace_msg("DNSBL idle helpers=%d", helper.idle_helpers); helper.idle_helpers = 0; } else if (helper.idle_helpers > helper.total_helpers) { /* The limit on the total_helpers can let us * drive idle_helpers<0 * which can make the previous fix wrong */ if (helper.debug) dcc_trace_msg("DNSBL idle helpers=%d" " total helpers=%d", helper.idle_helpers, helper.total_helpers); helper.idle_helpers = helper.total_helpers; } /* this is called from the "totals" thread * and so cannot use thr_error_msg() */ #if defined(WIFEXITED) && defined(WEXITSTATUS) if (WIFEXITED(status)) { if (WEXITSTATUS(status) == 0) { if (helper.debug > 1) dcc_trace_msg("DNSBL helper %d quit," " leaving %d helpers," " %d idle", (int)pid, helper.total_helpers, helper.idle_helpers); continue; } dcc_error_msg("DNSBL helper %d quit with exit(%d)," " leaving %d helpers, %d idle", (int)pid, WEXITSTATUS(status), helper.total_helpers, helper.idle_helpers); continue; } #endif #if defined(WTERMSIG) && defined(WIFSIGNALED) if (WIFSIGNALED(status)) { dcc_error_msg("DNSBL helper %d quit with signal %d," " leaving %d helpers, %d idle", (int)pid, WTERMSIG(status), helper.total_helpers, helper.idle_helpers); continue; } #endif dcc_error_msg("DNSBL helper %d quit with %d," " leaving %d helpers, %d idle", (int)pid, status, helper.total_helpers, helper.idle_helpers); } }