summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c181
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;
+}