diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..cd15971 --- /dev/null +++ b/src/main.c @@ -0,0 +1,181 @@ +#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 <assert.h> +#include <bits/getopt_core.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <unistd.h> + +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; +} |
