Пример #1
0
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;
}
Пример #2
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;
}
Пример #3
0
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();
}
Пример #4
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, 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;
}
Пример #5
0
/* 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;
	}
}
Пример #6
0
/* 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);
	}
}