static merlin_node *find_node(struct sockaddr_in *sain) { uint i; merlin_node *first = NULL; if (!sain) return NULL; for (i = 0; i < num_nodes; i++) { merlin_node *node = node_table[i]; unsigned short source_port = ntohs(sain->sin_port); unsigned short in_port = net_source_port(node); ldebug("FINDNODE: node->sain.sin_addr.s_addr: %d", node->sain.sin_addr.s_addr); if (node->sain.sin_addr.s_addr == sain->sin_addr.s_addr) { if (source_port == in_port) { /* perfect match */ ldebug("Inbound connection matches %s exactly (%s:%d)", node->name, inet_ntoa(sain->sin_addr), in_port); return node; } if (!first && !(node->flags & MERLIN_NODE_FIXED_SRCPORT)) first = node; } } if (first) { lwarn("Inbound connection presumably from %s (%s:%d != %s:%d)", first->name, inet_ntoa(sain->sin_addr), ntohs(sain->sin_port), inet_ntoa(first->sain.sin_addr), net_source_port(first)); } return first; }
/* * 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 should_log = 0; struct timeval connect_timeout = { MERLIN_CONNECT_TIMEOUT, 0 }; struct sockaddr_in sain; time_t interval = MERLIN_CONNECT_INTERVAL; int result; /* 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("CONN: Connect attempt blocked by config to %s node %s", node_type(node), node->name); } return 0; } /* don't bother trying to connect if it's pending or done */ switch (node->state) { case STATE_NEGOTIATING: if (node->conn_sock < 0) break; case STATE_CONNECTED: case STATE_PENDING: ldebug("CONN: node %s state is %s, so bailing", node->name, node_state_name(node->state)); return 0; } /* if it's not yet time to connect, don't even try it */ if (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->conn_sock < 0) { node_disconnect(node, "struct reset (no real disconnect)"); node->conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (node->conn_sock < 0) { lerr("CONN: Failed to obtain connection socket for node %s: %s", node->name, strerror(errno)); lerr("CONN: Aborting connection attempt to %s", node->name); return -1; } } sa->sa_family = AF_INET; if (should_log) { linfo("CONN: Connecting to %s %s@%s:%d", node_type(node), node->name, inet_ntoa(node->sain.sin_addr), ntohs(node->sain.sin_port)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(int))) { ldebug("CONN: Failed to set sockopt SO_REUSEADDR for node %s connect socket %d: %s", node->name, node->conn_sock, strerror(errno)); } if (node->flags & MERLIN_NODE_FIXED_SRCPORT) { ldebug("CONN: Using fixed source port %d for %s node %s", net_source_port(node), 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->conn_sock, (struct sockaddr *)&sain, sizeof(sain))) { lerr("CONN: Failed to bind() outgoing socket %d for node %s to port %d: %s", node->conn_sock, node->name, ntohs(sain.sin_port), strerror(errno)); if (errno == EBADF || errno == EADDRINUSE) { close(node->conn_sock); node->conn_sock = -1; return -1; } } } if (fcntl(node->conn_sock, F_SETFL, O_NONBLOCK) < 0) { lwarn("CONN: Failed to set socket %d for %s non-blocking: %s", node->conn_sock, node->name, strerror(errno)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_RCVTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("CONN: Failed to set receive timeout for %d, node %s: %s", node->conn_sock, node->name, strerror(errno)); } if (setsockopt(node->conn_sock, SOL_SOCKET, SO_SNDTIMEO, &connect_timeout, sizeof(connect_timeout)) < 0) { ldebug("CONN: Failed to set send timeout for %d, node %s: %s", node->conn_sock, node->name, strerror(errno)); } if (connect(node->conn_sock, sa, sizeof(struct sockaddr_in)) < 0) { if (errno == EINPROGRESS) { /* * non-blocking socket and connect() can't be completed * immediately (ie, the normal case) */ node_set_state(node, STATE_PENDING, "Connecting"); } else if (errno == EALREADY) { ldebug("CONN: Connect already in progress for socket %d to %s. This should never happen", node->conn_sock, node->name); node_set_state(node, STATE_PENDING, "connect() already in progress"); } else { /* a real connection error */ ldebug("CONN: connect() via %d to %s failed: %s", node->conn_sock, node->name, strerror(errno)); close(node->conn_sock); node->conn_sock = -1; if (should_log) { node_disconnect(node, "CONN: 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; } } result = iobroker_register_out(nagios_iobs, node->conn_sock, node, conn_writable); if (result < 0) { node_disconnect(node, "IOB: Failed to register %s connect socket %d with iobroker: %s", node->name, node->conn_sock, iobroker_strerror(result)); close(node->conn_sock); node->conn_sock = -1; return -1; } return 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; }