Files
3a35b3acc2 Modularization: Extract Tor into a separate module (#602)
* Extract Tor into a separate module

* Add Tor package as a dependency for iOS & macOS targets

* Move `tor-nolzma.xcframework` inside Tor

* Remove `libz` from Frameworks as its linked in Tor

* Remove stray `.gitkeep` from macOS target membership

* Fix missing import and access control for modularized Tor

- Add import Tor to NetworkActivationService
- Make TorManager.shutdownCompletely() public for external access

* Fix tor-nolzma.xcframework structure for Xcode builds

- Add missing Info.plist files to all framework slices
- Restructure macOS framework to use deep bundle format (Versions/)
- Keep iOS frameworks as shallow bundles (standard for iOS)

This fixes the Xcode build errors while maintaining SPM compatibility.

* Remove stale xcframework references from Xcode project

Xcode cleaned up old direct references to tor-nolzma.xcframework
since it's now managed internally by the Tor Swift package.

---------

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
2025-10-02 11:53:34 +02:00

178 lines
6.5 KiB
C

#include <pthread.h>
#include <sys/socket.h>
#include <unistd.h>
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Forward declarations from Tor's public embedding API (tor_api.h)
typedef struct tor_main_configuration_t tor_main_configuration_t;
tor_main_configuration_t *tor_main_configuration_new(void);
int tor_main_configuration_set_command_line(tor_main_configuration_t *cfg,
int argc,
char **argv);
void tor_main_configuration_free(tor_main_configuration_t *cfg);
int tor_run_main(const tor_main_configuration_t *);
static pthread_t tor_thread;
static int tor_thread_started = 0;
static int owning_fd_app = -1; // App side; close to trigger shutdown
static int owning_fd_tor = -1; // Tor side; closed when tor exits
// Persisted copies of args for restart convenience
static char data_dir_copy[PATH_MAX] = {0};
static char socks_arg_copy[64] = {0};
static char control_arg_copy[64] = {0};
static char fd_arg_copy[16] = {0};
// Keep argv array alive as required by tor_api.h until tor_run_main finishes
static char **argv_owned = NULL;
static int argv_owned_argc = 0;
// Public: return nonzero if tor thread appears to be running
int tor_host_is_running(void) {
return (tor_thread_started != 0) && (owning_fd_tor != -1);
}
static void *tor_thread_main(void *arg) {
tor_main_configuration_t *cfg = (tor_main_configuration_t *)arg;
int rc = tor_run_main(cfg); // blocks until tor exits
tor_main_configuration_free(cfg);
if (argv_owned) { free(argv_owned); argv_owned = NULL; argv_owned_argc = 0; }
// Do not close owning_fd_tor here: Tor may have already closed it and the
// fd number could have been re-used by the time we get here. On iOS 18,
// attempting to close a re-used guarded fd can trigger EXC_GUARD. Treat the
// tor-side end as owned by Tor and simply forget our reference.
owning_fd_tor = -1;
return (void*)(intptr_t)rc;
}
static int build_cfg(tor_main_configuration_t **out_cfg) {
tor_main_configuration_t *cfg = tor_main_configuration_new();
if (!cfg) return -1;
// Create a private socketpair; give one end to tor as its owning controller
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) {
tor_main_configuration_free(cfg);
return -2;
}
owning_fd_app = sv[0];
owning_fd_tor = sv[1];
snprintf(fd_arg_copy, sizeof(fd_arg_copy), "%d", owning_fd_tor);
// Preferred: configure via command-line to avoid stale torrc issues
const char *args[] = {
"tor",
"--DataDirectory", data_dir_copy,
"--ClientOnly", "1",
"--SocksPort", socks_arg_copy,
"--ControlPort", control_arg_copy,
"--CookieAuthentication", "1",
"--AvoidDiskWrites", "1",
"--MaxClientCircuitsPending", "8",
"--__OwningControllerFD", fd_arg_copy,
// Make client shutdown fast; don't linger
"--ShutdownWaitLength", "0",
NULL
};
int argc = 0; while (args[argc]) argc++;
// Allocate a stable argv array as required by tor_api.h
char **argv = (char **)malloc(sizeof(char*) * (argc + 1));
if (!argv) {
tor_main_configuration_free(cfg);
close(owning_fd_app); close(owning_fd_tor);
owning_fd_app = owning_fd_tor = -1;
return -3;
}
for (int i = 0; i <= argc; ++i) argv[i] = (char *)args[i];
argv_owned = argv;
argv_owned_argc = argc;
if (tor_main_configuration_set_command_line(cfg, argc, argv) != 0) {
tor_main_configuration_free(cfg);
free(argv);
close(owning_fd_app); close(owning_fd_tor);
owning_fd_app = owning_fd_tor = -1;
return -3;
}
*out_cfg = cfg;
return 0;
}
// Starts tor in a dedicated thread using Tor's embedding API.
// - data_dir: absolute path to DataDirectory
// - socks_addr: e.g., "127.0.0.1:39050"
// - control_addr: e.g., "127.0.0.1:39051"
// - shutdown_fast: if nonzero, we configure a faster shutdown (already default here)
int tor_host_start(const char *data_dir, const char *socks_addr, const char *control_addr, int shutdown_fast) {
if (!data_dir || !socks_addr || !control_addr) return -1;
if (strlen(data_dir) >= sizeof(data_dir_copy) || strlen(socks_addr) >= sizeof(socks_arg_copy) || strlen(control_addr) >= sizeof(control_arg_copy))
return -2;
// Don't start if an instance appears to be running
if (owning_fd_app != -1 || owning_fd_tor != -1) return -3;
// Store copies for potential restart
strncpy(data_dir_copy, data_dir, sizeof(data_dir_copy)-1);
data_dir_copy[sizeof(data_dir_copy)-1] = '\0';
strncpy(socks_arg_copy, socks_addr, sizeof(socks_arg_copy)-1);
socks_arg_copy[sizeof(socks_arg_copy)-1] = '\0';
strncpy(control_arg_copy, control_addr, sizeof(control_arg_copy)-1);
control_arg_copy[sizeof(control_arg_copy)-1] = '\0';
tor_main_configuration_t *cfg = NULL;
int rc = build_cfg(&cfg);
if (rc != 0) return rc;
(void)shutdown_fast; // already configured via argv
int prc = pthread_create(&tor_thread, NULL, tor_thread_main, cfg);
if (prc != 0) {
tor_main_configuration_free(cfg);
if (argv_owned) { free(argv_owned); argv_owned = NULL; argv_owned_argc = 0; }
close(owning_fd_app); close(owning_fd_tor);
owning_fd_app = owning_fd_tor = -1;
return -4;
}
tor_thread_started = 1;
return 0;
}
// Triggers a clean shutdown by closing the app-side owning controller fd,
// then joins the tor thread to ensure complete exit before returning.
int tor_host_shutdown(void) {
if (owning_fd_app != -1) { close(owning_fd_app); owning_fd_app = -1; }
void *ret = NULL;
if (tor_thread_started) {
// Wait for tor_run_main to return
pthread_join(tor_thread, &ret);
tor_thread_started = 0;
}
return (int)(intptr_t)ret;
}
// Convenience: shutdown then start again with the stored parameters.
int tor_host_restart(const char *data_dir, const char *socks_addr, const char *control_addr, int shutdown_fast) {
(void)data_dir; (void)socks_addr; (void)control_addr; (void)shutdown_fast;
// If caller provided new args, refresh our copies
if (data_dir && data_dir[0]) {
strncpy(data_dir_copy, data_dir, sizeof(data_dir_copy)-1);
data_dir_copy[sizeof(data_dir_copy)-1] = '\0';
}
if (socks_addr && socks_addr[0]) {
strncpy(socks_arg_copy, socks_addr, sizeof(socks_arg_copy)-1);
socks_arg_copy[sizeof(socks_arg_copy)-1] = '\0';
}
if (control_addr && control_addr[0]) {
strncpy(control_arg_copy, control_addr, sizeof(control_arg_copy)-1);
control_arg_copy[sizeof(control_arg_copy)-1] = '\0';
}
(void)tor_host_shutdown();
return tor_host_start(data_dir_copy, socks_arg_copy, control_arg_copy, shutdown_fast);
}