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, ¶m); 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; }
/* * 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; }
/* * 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; }
/* * 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; }
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); } }
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; }
/* * 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; }