// retoor #include "tool.h" #include "r_config.h" #include #include #include #include #include #include #include #include #include #include static struct json_object *network_check_get_description(void); static char *network_check_execute(tool_t *self, struct json_object *args); static void network_check_print_action(const char *name, struct json_object *args); static struct json_object *network_port_scan_get_description(void); static char *network_port_scan_execute(tool_t *self, struct json_object *args); static const tool_vtable_t network_check_vtable = { .get_description = network_check_get_description, .execute = network_check_execute, .print_action = network_check_print_action }; static const tool_vtable_t network_port_scan_vtable = { .get_description = network_port_scan_get_description, .execute = network_port_scan_execute, .print_action = NULL }; static tool_t network_check_tool = { .vtable = &network_check_vtable, .name = "network_check" }; static tool_t network_port_scan_tool = { .vtable = &network_port_scan_vtable, .name = "network_port_scan" }; tool_t *tool_network_check_create(void) { return &network_check_tool; } tool_t *tool_network_port_scan_create(void) { return &network_port_scan_tool; } #include #include #define MAX_PARALLEL_PROBES 50 typedef struct { int sockfd; int port; bool in_use; } probe_t; static char *network_port_scan_execute(tool_t *self, struct json_object *args) { (void)self; struct json_object *host_obj = NULL, *start_obj = NULL, *end_obj = NULL; if (!json_object_object_get_ex(args, "host", &host_obj)) return strdup("Error: host missing"); json_object_object_get_ex(args, "start_port", &start_obj); json_object_object_get_ex(args, "end_port", &end_obj); const char *host = json_object_get_string(host_obj); int start_port = start_obj ? json_object_get_int(start_obj) : 0; int end_port = end_obj ? json_object_get_int(end_obj) : start_port; struct hostent *server = gethostbyname(host); if (!server) return strdup("Error: dns resolution failed"); struct json_object *results = json_object_new_array(); fprintf(stderr, " \033[1mParallel scanning %s ports %d to %d...\033[0m\n", host, start_port, end_port); probe_t probes[MAX_PARALLEL_PROBES]; memset(probes, 0, sizeof(probes)); int current_port = start_port; int active_count = 0; while (current_port <= end_port || active_count > 0) { // Fill slots while (active_count < MAX_PARALLEL_PROBES && current_port <= end_port) { int s = socket(AF_INET, SOCK_STREAM, 0); if (s >= 0) { int flags = fcntl(s, F_GETFL, 0); fcntl(s, F_SETFL, flags | O_NONBLOCK); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; memcpy(&addr.sin_addr.s_addr, server->h_addr, (size_t)server->h_length); addr.sin_port = htons((unsigned short)current_port); connect(s, (struct sockaddr *)&addr, sizeof(addr)); for (int i = 0; i < MAX_PARALLEL_PROBES; i++) { if (!probes[i].in_use) { probes[i].sockfd = s; probes[i].port = current_port; probes[i].in_use = true; active_count++; break; } } } current_port++; } if (active_count > 0) { fd_set writefds; FD_ZERO(&writefds); int max_fd = 0; for (int i = 0; i < MAX_PARALLEL_PROBES; i++) { if (probes[i].in_use) { FD_SET(probes[i].sockfd, &writefds); if (probes[i].sockfd > max_fd) max_fd = probes[i].sockfd; } } struct timeval tv = {0, 100000}; // 100ms int sel = select(max_fd + 1, NULL, &writefds, NULL, &tv); if (sel >= 0) { for (int i = 0; i < MAX_PARALLEL_PROBES; i++) { if (probes[i].in_use) { bool done = false; if (FD_ISSET(probes[i].sockfd, &writefds)) { int error = 0; socklen_t len = sizeof(error); getsockopt(probes[i].sockfd, SOL_SOCKET, SO_ERROR, &error, &len); if (error == 0) { fprintf(stderr, " -> Port %d: \033[32mOPEN\033[0m\n", probes[i].port); struct json_object *entry = json_object_new_object(); json_object_object_add(entry, "port", json_object_new_int(probes[i].port)); json_object_object_add(entry, "status", json_object_new_string("OPEN")); json_object_array_add(results, entry); } done = true; } else { // Check if timeout for this specific probe? // Simplified: if select says nothing for 1s total, we'll close it eventually. } // Close sockets that stay too long? // For brevity, we'll just use a shorter overall timeout loop or check time. // Here we'll just close if select reported it. if (done) { close(probes[i].sockfd); probes[i].in_use = false; active_count--; } } } } // Cleanup stalled probes (rudimentary timeout) static int timeout_counter = 0; if (++timeout_counter > 20) { // ~2 seconds for (int i = 0; i < MAX_PARALLEL_PROBES; i++) { if (probes[i].in_use) { close(probes[i].sockfd); probes[i].in_use = false; active_count--; } } timeout_counter = 0; } } } fprintf(stderr, " \033[32mScan complete.\033[0m\n"); char *out = strdup(json_object_to_json_string_ext(results, JSON_C_TO_STRING_PRETTY)); json_object_put(results); return out; } static struct json_object *network_port_scan_get_description(void) { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("network_port_scan")); json_object_object_add(function, "description", json_object_new_string("Scans a range of TCP ports on a host and attempts service banner grabbing.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *host = json_object_new_object(); json_object_object_add(host, "type", json_object_new_string("string")); json_object_object_add(properties, "host", host); struct json_object *start = json_object_new_object(); json_object_object_add(start, "type", json_object_new_string("integer")); json_object_object_add(properties, "start_port", start); struct json_object *end = json_object_new_object(); json_object_object_add(end, "type", json_object_new_string("integer")); json_object_object_add(properties, "end_port", end); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("host")); json_object_array_add(required, json_object_new_string("start_port")); json_object_array_add(required, json_object_new_string("end_port")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); r_config_handle cfg = r_config_get_instance(); if (r_config_use_strict(cfg)) { json_object_object_add(function, "strict", json_object_new_boolean(1)); } json_object_object_add(root, "function", function); return root; } static void network_check_print_action(const char *name, struct json_object *args) { (void)name; struct json_object *host; if (json_object_object_get_ex(args, "host", &host)) { fprintf(stderr, " -> Checking network: %s\n", json_object_get_string(host)); } } static char *network_check_execute(tool_t *self, struct json_object *args) { (void)self; struct json_object *host_obj, *port_obj; if (!json_object_object_get_ex(args, "host", &host_obj)) { return strdup("Error: missing 'host' argument"); } const char *host = json_object_get_string(host_obj); int port = 0; if (json_object_object_get_ex(args, "port", &port_obj)) { port = json_object_get_int(port_obj); } struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(host, NULL, &hints, &res) != 0) { return strdup("Error: DNS lookup failed"); } char ipstr[INET6_ADDRSTRLEN]; void *addr; if (res->ai_family == AF_INET) { struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr; addr = &(ipv4->sin_addr); } else { struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)res->ai_addr; addr = &(ipv6->sin6_addr); } inet_ntop(res->ai_family, addr, ipstr, sizeof(ipstr)); char result[1024]; if (port > 0) { int sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) { freeaddrinfo(res); return strdup("Error: could not create socket"); } // Set non-blocking for timeout int flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); struct sockaddr_in serv_addr; if (res->ai_family == AF_INET) { memcpy(&serv_addr, res->ai_addr, sizeof(serv_addr)); serv_addr.sin_port = htons(port); } int conn_res = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); if (conn_res < 0) { if (errno == EINPROGRESS) { fd_set set; struct timeval tv; FD_ZERO(&set); FD_SET(sockfd, &set); tv.tv_sec = 2; // 2 second timeout tv.tv_usec = 0; int select_res = select(sockfd + 1, NULL, &set, NULL, &tv); if (select_res > 0) { int valopt; socklen_t lon = sizeof(int); getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); if (valopt == 0) conn_res = 0; } } } close(sockfd); snprintf(result, sizeof(result), "Host: %s (IP: %s), Port %d: %s", host, ipstr, port, (conn_res == 0) ? "OPEN" : "CLOSED/TIMEOUT"); } else { snprintf(result, sizeof(result), "Host: %s (IP: %s), DNS: OK", host, ipstr); } freeaddrinfo(res); return strdup(result); } static struct json_object *network_check_get_description(void) { struct json_object *root = json_object_new_object(); json_object_object_add(root, "type", json_object_new_string("function")); struct json_object *function = json_object_new_object(); json_object_object_add(function, "name", json_object_new_string("network_check")); json_object_object_add(function, "description", json_object_new_string("Check DNS and port connectivity for a host.")); struct json_object *parameters = json_object_new_object(); json_object_object_add(parameters, "type", json_object_new_string("object")); struct json_object *properties = json_object_new_object(); struct json_object *host = json_object_new_object(); json_object_object_add(host, "type", json_object_new_string("string")); json_object_object_add(host, "description", json_object_new_string("Hostname or IP to check.")); json_object_object_add(properties, "host", host); struct json_object *port = json_object_new_object(); json_object_object_add(port, "type", json_object_new_string("integer")); json_object_object_add(port, "description", json_object_new_string("Port to check (optional).")); json_object_object_add(properties, "port", port); json_object_object_add(parameters, "properties", properties); struct json_object *required = json_object_new_array(); json_object_array_add(required, json_object_new_string("host")); json_object_array_add(required, json_object_new_string("port")); json_object_object_add(parameters, "required", required); json_object_object_add(parameters, "additionalProperties", json_object_new_boolean(0)); json_object_object_add(function, "parameters", parameters); r_config_handle cfg = r_config_get_instance(); if (r_config_use_strict(cfg)) { json_object_object_add(function, "strict", json_object_new_boolean(1)); } json_object_object_add(root, "function", function); return root; }