#include "main.h" #include "hoodsgate/control_plane/cp_policy_engine.h" #include "hoodsgate/control_plane/cp_state_manager.h" #include "hoodsgate/hoodsgate.h" #include "tailnet.h" #include #include #include #include #include #include #include #include #include bool is_directory_boolean(const char *path) { struct stat path_stat; if (stat(path, &path_stat) != 0) { return false; } return S_ISDIR(path_stat.st_mode); } bool path_exists_boolean(const char *path) { return access(path, F_OK) == 0; } bool is_readable_boolean(const char *path) { return access(path, R_OK) == 0; } bool is_writable_boolean(const char *path) { return access(path, W_OK) == 0; } bool is_executable_boolean(const char *path) { return access(path, X_OK) == 0; } bool is_rw_boolean(const char *path) { return is_readable_boolean(path) && is_writable_boolean(path); } bool is_rwx_boolean(const char *path) { return is_readable_boolean(path) && is_writable_boolean(path) && is_executable_boolean(path); } bool init_tailscale() { // determine what progs are missing, if just tailscale is missing then // install it and move forward if (!tailnet_check_required_programs_boolean()) { int missing_count = sizeof(tailnetMissingProgs) / sizeof(tailnetMissingProgs[0]); if (missing_count == 1) { if (strcmp(PROG_TAILSCALE, tailnetMissingProgs[0]) == 0) { // install tailscale via official curl script char *const tsGetInstallScript[] = { PROG_CURL, "-fsSL", "https://tailscale.com/install.sh", NULL}; char *installScript = hg_run_command_w_execvp_pchar(PROG_CURL, tsGetInstallScript, NULL); if (installScript == NULL) { fprintf(stderr, "Tailscale installation failed"); return false; } // for testing printf("%s\n", installScript); char *const tsRunScript[] = {"sh", NULL}; char *installOutput = hg_run_command_w_execvp_pchar( PROG_CURL, tsRunScript, installScript); if (installOutput == NULL) { fprintf(stderr, "Tailscale installation failed"); return false; } } } else { fprintf(stderr, "Missing one or more required programs to run Hoods Gate"); return false; } } assert(tailnet_advertise_exit_node_boolean()); return true; } // --tailscale, --control-plane // TODO: Need to implement different logic // for control-plane+data-plane exit node // and data-plane only exit nodes int main(int argc, char *argv[]) { if (geteuid() != 0) { fprintf(stderr, "Failed: this program must be run with sudo or root privileges\n"); exit(EXIT_FAILURE); } bool _tailscale = false; bool _isHost = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--tailscale") == 0) { _tailscale = true; } else if (strcmp(argv[i], "--control-plane") == 0) { _isHost = true; } else { fprintf(stderr, "Usage: %s [--tailscale] [--control-plane]\n", argv[0]); exit(EXIT_FAILURE); } } if (_tailscale) { if (!init_tailscale()) { exit(EXIT_FAILURE); } } // Initialize nftables, first run flagging? if (cp_init_nftables() < 0) { fprintf(stderr, "Failed to initialize nftables\n"); exit(EXIT_FAILURE); } printf("Starting Exit Node Control Plane...\n"); hgc_log_event_void(EVENT_DAEMON_START, 0, 0, "Control plane daemon starting"); if (_isHost) { cp_state.control_socket = cp_setup_control_socket(); if (cp_state.control_socket < 0) { fprintf(stderr, "Failed to setup control socket\n"); exit(EXIT_FAILURE); } } else { printf("This machine is not the host"); } // Main event loop while (cp_state.running) { fd_set readfds; struct timeval tv = {.tv_sec = 1, .tv_usec = 0}; FD_ZERO(&readfds); FD_SET(cp_state.control_socket, &readfds); // watches fd for data; man select int ret = select(cp_state.control_socket + 1, &readfds, NULL, NULL, &tv); if (ret < 0) { // interruption signal if (errno == EINTR) continue; perror("select"); break; } if (ret == 0) continue; // timeout on timeval struct value if (FD_ISSET(cp_state.control_socket, &readfds)) { // Data is available in the socket int client_fd = accept(cp_state.control_socket, NULL, NULL); if (client_fd < 0) { perror("accept"); continue; } char buffer[BUFFER_SIZE]; ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0); if (n > 0) { buffer[n] = '\0'; // Remove trailing newline if (buffer[n - 1] == '\n') buffer[n - 1] = '\0'; // Not implemented yet. // handle_control_command(client_fd, buffer); } else if (n < 0) { hgc_log_event_void(EVENT_ERROR, errno, 0, "recv error: %s", strerror(errno)); } close(client_fd); } } // Cleanup hgc_log_event_void(EVENT_DAEMON_STOP, 0, 0, "Control plane daemon shutting down"); printf("Shutting down...\n"); close(cp_state.control_socket); unlink(CONTROL_SOCKET_PATH); return 0; }