1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
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;
}
|