Пример #1
0
static int dnsproxy_fwd(int state, knot_pkt_t *pkt, struct query_data *qdata, void *ctx)
{
	if (pkt == NULL || qdata == NULL || ctx == NULL) {
		return KNOT_STATE_FAIL;
	}

	/* If not already satisfied. */
	if (state == KNOT_STATE_DONE) {
		return state;
	}

	struct dnsproxy *proxy = ctx;

	/* Create a forwarding request. */
	struct knot_requestor re;
	knot_requestor_init(&re, qdata->mm);
	struct capture_param param;
	param.sink = pkt;
	int ret = knot_requestor_overlay(&re, LAYER_CAPTURE, &param);
	if (ret != KNOT_EOK) {
		return KNOT_STATE_FAIL;
	}

	bool is_tcp = net_is_connected(qdata->param->socket);
	struct knot_request *req;
	req = knot_request_make(re.mm, (const struct sockaddr *)&proxy->remote,
	                        NULL, qdata->query, is_tcp ? 0 : KNOT_RQ_UDP);
	if (req == NULL) {
		return state; /* Ignore, not enough memory. */
	}

	/* Forward request. */
	ret = knot_requestor_enqueue(&re, req);
	if (ret == KNOT_EOK) {
		conf_val_t val = conf_get(conf(), C_SRV, C_TCP_HSHAKE_TIMEOUT);
		struct timeval tv = { conf_int(&val), 0 };
		ret = knot_requestor_exec(&re, &tv);
	} else {
		knot_request_free(re.mm, req);
	}

	knot_requestor_clear(&re);

	/* Check result. */
	if (ret != KNOT_EOK) {
		qdata->rcode = KNOT_RCODE_SERVFAIL;
		return KNOT_STATE_FAIL; /* Forwarding failed, SERVFAIL. */
	}

	return KNOT_STATE_DONE;
}
Пример #2
0
Файл: net.c Проект: op5/merlin
/*
 * This gets called when a connect() attempt has become writable.
 * It's entirely possible that the node we're trying to connect
 * to has connected to us while we were waiting for them, in
 * which case we need to figure out which of the two connections
 * we're supposed to use.
 */
static int conn_writable(int sd, int events, void *node_)
{
	merlin_node *node = (merlin_node *)node_;
	int result;
	int sel_sd;

	/* unregister so we don't peg one cpu at 100% */
	ldebug("CONN: In conn_writable(): node=%s; sd=%d; node->conn_sock=%d", node->name, sd, node->conn_sock);
	iobroker_unregister(nagios_iobs, sd);

	if (node->sock < 0) {
		/* no inbound connection accept()'ed yet */
		node->sock = sd;
		node->conn_sock = -1;
		if (!net_is_connected(node)) {
			node_disconnect(node, "Connection attempt failed: %s", strerror(errno));
			close(sd);
			return 0;
		}
		iobroker_register(nagios_iobs, sd, node, net_input);
		node_set_state(node, STATE_NEGOTIATING, "Connect completed successfully. Negotiating protocol");
		return 0;
	}

	sel_sd = net_negotiate_socket(node, node->conn_sock, node->sock);
	if (sel_sd < 0) {
		node_disconnect(node, "Failed to negotiate socket");
		return 0;
	}

	if (sel_sd == node->conn_sock) {
		iobroker_close(nagios_iobs, node->sock);
	} else if (sel_sd == node->sock) {
		iobroker_close(nagios_iobs, node->conn_sock);
	}

	node->sock = sel_sd;
	node->conn_sock = -1;
	node_set_state(node, STATE_NEGOTIATING, "polled for writability");
	/* now re-register for input */
	ldebug("IOB: registering %s(%d) for input events", node->name, node->sock);
	result = iobroker_register(nagios_iobs, node->sock, node, net_input);
	if (result < 0) {
		lerr("IOB: Failed to register %s(%d) for input events: %s",
		     node->name, node->sock, iobroker_strerror(result));
	}

	return 0;
}
Пример #3
0
/*
 * Handles polling results from a previous (successful) select(2)
 * This is where new connections are handled and network input is
 * scheduled for reading
 */
int net_handle_polling_results(fd_set *rd, fd_set *wr)
{
	uint i;

	/* loop the nodes and see which ones have sent something */
	for (i = 0; i < num_nodes; i++) {
		merlin_node *node = node_table[i];

		/* skip obviously bogus sockets */
		if (node->sock < 0)
			continue;

		/* handle new connections first */
		if (FD_ISSET(node->sock, wr)) {
			if (net_is_connected(node)) {
				node_set_state(node, STATE_CONNECTED, "select()'ed for writing");

				if (binlog_has_entries(node->binlog)) {
					node_send_binlog(node, NULL);
				}
			}
			continue;
		}

		/*
		 * handle input, and missing input. All nodes should send
		 * a pulse at least once in a while, so we know it's still OK.
		 * If they fail to do that, we may have to take action.
		 */
		if (FD_ISSET(node->sock, rd)) {
			net_input(node);
		}
	}

	/* check_node_activity(node); */
	return 0;
}
Пример #4
0
/*
 * Populates the fd_set's *rd and *wr with all the connected nodes'
 * sockets.
 * Returns the highest socket descriptor found, so the fd_set's can
 * be passed to select(2)
 */
int net_polling_helper(fd_set *rd, fd_set *wr, int sel_val)
{
	uint i;

	for (i = 0; i < num_nodes; i++) {
		merlin_node *node = node_table[i];

		check_node_activity(node);

		if (!net_is_connected(node) || node->state == STATE_NONE)
			continue;

		/*
		 * safeguard against bugs in net_is_connected() or any of
		 * the system and library calls it makes. node->sock has to
		 * be >= 0 for FD_SET() not to cause segfaults
		 */
		if (node->sock < 0)
			continue;

		/* the node is connected, so we can poll it for readability */
		FD_SET(node->sock, rd);

		/*
		 * if this node's binlog has entries we check for writability
		 * as well, so we can send it from the outer polling loop.
		 */
		if (binlog_has_entries(node->binlog))
			FD_SET(node->sock, wr);

		if (node->sock > sel_val)
			sel_val = node->sock;
	}

	return sel_val;
}
Пример #5
0
static void polling_loop(void)
{
	for (;;) {
		uint i;
		time_t now = time(NULL);

		if (user_sig & (1 << SIGUSR1))
			dump_daemon_nodes();

		/*
		 * log the event count. The marker to prevent us from
		 * spamming the logs is in log_event_count() in logging.c
		 */
		ipc_log_event_count();

		/* reap any children that might have finished */
		reap_child_process();

		/*
		 * reap_child_process() resets importer_pid if
		 * the import is completed.
		 * if it's not and at tops 5 seconds have passed,
		 * ask for some more time.
		 */
		if (importer_pid && !(now % 5)) {
			ipc_send_ctrl(CTRL_STALL, CTRL_GENERIC);
		}

		/*
		 * We try accepting inbound connections first. This is kinda
		 * useful since we open the listening network socket before
		 * we launch into the ipc socket code. It's not rare for other
		 * nodes to have initiated connection attempts in that short
		 * time. if they have and are currently waiting for us to just
		 * accept that connection, we can humor them and avoid the
		 * whole socket negotiation thing.
		 */
		while (net_accept_one() >= 0)
			; /* nothing */

		/*
		 * Next we try to connect to all nodes that aren't yet
		 * connected. Quite often we'll run into firewall rules that
		 * say one network can't connect to the other, but not the
		 * other way around, so it's useful to try from both sides
		 */
		for (i = 0; i < num_nodes; i++) {
			merlin_node *node = node_table[i];
			/* try connecting if we're not already */
			if (!net_is_connected(node)) {
				net_try_connect(node);
			}
		}

		/*
		 * io_poll_sockets() is the real worker. It handles network
		 * and ipc based IO and ships inbound events off to their
		 * right destination.
		 */
		io_poll_sockets();

		/*
		 * Try to commit any outstanding queries
		 */
		sql_try_commit(0);
	}
}
Пример #6
0
static int rosedb_log_message(char *stream, size_t *maxlen, knot_pkt_t *pkt,
                              const char *threat_code, struct query_data *qdata)
{
	char dname_buf[KNOT_DNAME_MAXLEN] = {'\0'};
	struct sockaddr_storage addr;
	socklen_t addr_len = sizeof(addr);
	time_t now = time(NULL);
	struct tm tm;
	gmtime_r(&now, &tm);

	/* Field 1 Timestamp (UTC). */
	STREAM_WRITE(stream, maxlen, strftime, "%Y-%m-%d %H:%M:%S\t", &tm);

	/* Field 2/3 Remote, local address. */
	const struct sockaddr *remote = (const struct sockaddr *)qdata->param->remote;
	memcpy(&addr, remote, sockaddr_len(remote));
	int client_port = sockaddr_port(&addr);
	sockaddr_port_set(&addr, 0);
	STREAM_WRITE(stream, maxlen, sockaddr_tostr, &addr);
	STREAM_WRITE(stream, maxlen, snprintf, "\t");
	getsockname(qdata->param->socket, (struct sockaddr *)&addr, &addr_len);
	int server_port = sockaddr_port(&addr);
	sockaddr_port_set(&addr, 0);
	STREAM_WRITE(stream, maxlen, sockaddr_tostr, &addr);
	STREAM_WRITE(stream, maxlen, snprintf, "\t");

	/* Field 4/5 Local, remote port. */
	STREAM_WRITE(stream, maxlen, snprintf, "%d\t%d\t", client_port, server_port);

	/* Field 6 Threat ID. */
	STREAM_WRITE(stream, maxlen, snprintf, "%s\t", threat_code);

	/* Field 7 - 13 NULL */
	STREAM_WRITE(stream, maxlen, snprintf, "\t\t\t\t\t\t\t");

	/* Field 14 QNAME */
	knot_dname_to_str(dname_buf, knot_pkt_qname(qdata->query), sizeof(dname_buf));
	STREAM_WRITE(stream, maxlen, snprintf, "%s\t", dname_buf);

	/* Field 15 Resolution (0 = local, 1 = lookup)*/
	STREAM_WRITE(stream, maxlen, snprintf, "0\t");

	/* Field 16 RDATA.
	 * - Return randomly RDATA in the answer section (probabilistic rotation).
	 * - Empty if no answer.
	 */
	const knot_pktsection_t *ans = knot_pkt_section(pkt, KNOT_ANSWER);
	if (ans->count > 0) {
		const knot_rrset_t *rr = &ans->rr[knot_random_uint16_t() % ans->count];
		int ret = knot_rrset_txt_dump_data(rr, 0, stream, *maxlen, &KNOT_DUMP_STYLE_DEFAULT);
		if (ret < 0) {
			return ret;
		}
		stream_skip(&stream, maxlen, ret);
	}
	STREAM_WRITE(stream, maxlen, snprintf, "\t");

	/* Field 17 Connection type. */
	STREAM_WRITE(stream, maxlen, snprintf, "%s\t",
	             net_is_connected(qdata->param->socket) ? "TCP" : "UDP");

	/* Field 18 Query type. */
	char type_str[16] = { '\0' };
	knot_rrtype_to_string(knot_pkt_qtype(qdata->query), type_str, sizeof(type_str));
	STREAM_WRITE(stream, maxlen, snprintf, "%s\t", type_str);

	/* Field 19 First authority. */
	const knot_pktsection_t *ns = knot_pkt_section(pkt, KNOT_AUTHORITY);
	if (ns->count > 0 && ns->rr[0].type == KNOT_RRTYPE_NS) {
		const knot_dname_t *label = knot_ns_name(&ns->rr[0].rrs, 0);
		memset(dname_buf, 0, sizeof(dname_buf));
		memcpy(dname_buf, label + 1, *label);
		STREAM_WRITE(stream, maxlen, snprintf, "%s", dname_buf);
	}

	return KNOT_EOK;
}
Пример #7
0
/*
 * Initiate a connection attempt to a node and mark it as PENDING.
 * Note that since we're using sockets in non-blocking mode (in order
 * to be able to effectively multiplex), the connection attempt will
 * never be completed in this function
 */
int net_try_connect(merlin_node *node)
{
	int sockopt = 1;
	struct sockaddr *sa = (struct sockaddr *)&node->sain;
	int connected = 0, should_log = 0;
	struct timeval connect_timeout = { MERLIN_CONNECT_TIMEOUT, 0 };
	struct sockaddr_in sain;
	time_t interval = MERLIN_CONNECT_INTERVAL;

	/* don't log obsessively */
	if (node->last_conn_attempt_logged + 30 <= time(NULL)) {
		should_log = 1;
		node->last_conn_attempt_logged = time(NULL);
	}

	if (!(node->flags & MERLIN_NODE_CONNECT)) {
		if (should_log) {
			linfo("Connect attempt blocked by config to %s node %s",
				  node_type(node), node->name);
		}
		return 0;
	}

	/* if it's not yet time to connect, don't even try it */
	if (node->last_conn_attempt + interval > time(NULL)) {
		ldebug("connect to %s blocked for %lu more seconds", node->name,
			   node->last_conn_attempt + interval - time(NULL));
		return 0;
	}

	/* mark the time so we can time it out ourselves if need be */
	node->last_conn_attempt = time(NULL);

	/* create the socket if necessary */
	if (node->sock < 0) {
		node_disconnect(node, "struct reset (no real disconnect)");
		node->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (node->sock < 0) {
			lerr("Failed to obtain socket for node %s: %s", node->name, strerror(errno));
			lerr("Aborting connection attempt to %s", node->name);
			return -1;
		}
	}

	/*
	 * don't try to connect to a node if an attempt is already pending,
	 * but do check if the connection has completed successfully
	 */
	if (node->state == STATE_PENDING || node->state == STATE_CONNECTED) {
		if (net_is_connected(node))
			node_set_state(node, STATE_CONNECTED, "Attempted connect completed");
		return 0;
	}

	sa->sa_family = AF_INET;
	if (should_log) {
		linfo("Connecting to %s %s@%s:%d", node_type(node), node->name,
		      inet_ntoa(node->sain.sin_addr),
		      ntohs(node->sain.sin_port));
	}

	(void)setsockopt(node->sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int));
	if (node->flags & MERLIN_NODE_FIXED_SRCPORT) {
		ldebug("Using fixed source port for %s node %s",
			   node_type(node), node->name);
		/*
		 * first we bind() to a local port calculated by our own
		 * listening port + the target port.
		 */
		sain.sin_family = AF_INET;
		sain.sin_port = htons(net_source_port(node));
		sain.sin_addr.s_addr = 0;
		if (bind(node->sock, (struct sockaddr *)&sain, sizeof(sain))) {
			lerr("Failed to bind() outgoing socket for node %s to port %d: %s",
				 node->name, ntohs(sain.sin_port), strerror(errno));
		}
	}

	if (fcntl(node->sock, F_SETFL, O_NONBLOCK) < 0) {
		lwarn("Failed to set socket for %s non-blocking: %s", node->name, strerror(errno));
	}
	if (setsockopt(node->sock, SOL_SOCKET, SO_RCVTIMEO,
	               &connect_timeout, sizeof(connect_timeout)) < 0)
	{
		ldebug("Failed to set receive timeout for node %s: %s",
		       node->name, strerror(errno));
	}
	if (setsockopt(node->sock, SOL_SOCKET, SO_SNDTIMEO,
	               &connect_timeout, sizeof(connect_timeout)) < 0)
	{
		ldebug("Failed to set send timeout for node %s: %s",
		       node->name, strerror(errno));
	}

	if (connect(node->sock, sa, sizeof(struct sockaddr_in)) < 0) {
		if (errno == EINPROGRESS || errno == EALREADY) {
			node_set_state(node, STATE_PENDING, "connect() already in progress");
		} else if (errno == EISCONN) {
			connected = 1;
		} else {
			if (should_log) {
				node_disconnect(node, "connect() failed to %s node '%s' (%s:%d): %s",
								node_type(node), node->name,
								inet_ntoa(node->sain.sin_addr),
								ntohs(node->sain.sin_port),
								strerror(errno));
			} else {
				node_disconnect(node, NULL);
			}
			return -1;
		}
	}

	if (connected || net_is_connected(node)) {
		linfo("Successfully connected to %s %s@%s:%d",
			  node_type(node), node->name, inet_ntoa(node->sain.sin_addr),
			  ntohs(node->sain.sin_port));
		node_set_state(node, STATE_CONNECTED, "connect() successful");
	} else {
		if (should_log) {
			linfo("Connection pending to %s %s@%s:%d",
			      node_type(node), node->name,
			      inet_ntoa(node->sain.sin_addr),
			      ntohs(node->sain.sin_port));
		}
		node_set_state(node, STATE_PENDING, "connect() in progress");
	}

	return 0;
}