Add a Rust FFI, Disable sandboxing for scripts

This commit is contained in:
Bug Magnet authored and Emīls committed 2024-07-17 11:48:13 +02:00
1 parent b6fe08388d
commit 2df280fbf0
58 files changed
+704 -1421

No files matched your search

Generated
+17 -23
View File
@@ -2243,6 +2243,23 @@ dependencies = [
"uuid",
]
[[package]]
name = "mullvad-ios"
version = "0.0.0"
dependencies = [
"cbindgen",
"libc",
"log",
"oslog",
"shadowsocks-service",
"talpid-tunnel-config-client",
"talpid-types",
"tokio",
"tonic",
"tower",
"tunnel-obfuscation",
]
[[package]]
name = "mullvad-jni"
version = "0.0.0"
@@ -3614,17 +3631,6 @@ dependencies = [
"sha1",
]
[[package]]
name = "shadowsocks-proxy"
version = "0.0.0"
dependencies = [
"cbindgen",
"log",
"oslog",
"shadowsocks-service",
"tokio",
]
[[package]]
name = "shadowsocks-service"
version = "1.18.3"
@@ -4051,7 +4057,6 @@ dependencies = [
name = "talpid-tunnel-config-client"
version = "0.0.0"
dependencies = [
"cbindgen",
"classic-mceliece-rust",
"libc",
"log",
@@ -4492,17 +4497,6 @@ dependencies = [
"udp-over-tcp",
]
[[package]]
name = "tunnel-obfuscator-proxy"
version = "0.0.0"
dependencies = [
"cbindgen",
"log",
"oslog",
"tokio",
"tunnel-obfuscation",
]
[[package]]
name = "typenum"
version = "1.17.0"
+3 -4
View File
@@ -9,13 +9,12 @@ rust-version = "1.77.0"
resolver = "2"
members = [
"android/translations-converter",
"ios/MullvadREST/Transport/Shadowsocks/shadowsocks-proxy",
"ios/TunnelObfuscation/tunnel-obfuscator-proxy",
"mullvad-api",
"mullvad-cli",
"mullvad-daemon",
"mullvad-exclude",
"mullvad-fs",
"mullvad-ios",
"mullvad-jni",
"mullvad-management-interface",
"mullvad-nsis",
@@ -39,7 +38,7 @@ members = [
"talpid-windows",
"talpid-wireguard",
"tunnel-obfuscation",
"wireguard-go-rs"
"wireguard-go-rs",
]
# Keep all lints in sync with `test/Cargo.toml`
@@ -81,7 +80,7 @@ shadowsocks-service = { version = "1.16" }
windows-sys = "0.52.0"
chrono = { version = "0.4.26", default-features = false}
chrono = { version = "0.4.26", default-features = false }
clap = { version = "4.4.18", features = ["cargo", "derive"] }
once_cell = "1.13"
@@ -1,10 +0,0 @@
//
// MullvadPostQuantum.h
// MullvadPostQuantum
//
// Created by Marco Nikic on 2024-02-27.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "talpid_tunnel_config_client.h"
@@ -1,5 +0,0 @@
framework module TalpidTunnelConfigClientProxy {
header "talpid_tunnel_config_client.h"
link "libtalpid_tunnel_config_client"
export *
}
@@ -7,92 +7,3 @@
* Port used by the tunnel config service.
*/
#define CONFIG_SERVICE_PORT 1337
typedef struct PostQuantumCancelToken {
void *context;
} PostQuantumCancelToken;
/**
* Called by the Swift side to signal that the quantum-secure key exchange should be cancelled.
*
* # Safety
* `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
* `PacketTunnelProvider`.
*/
void cancel_post_quantum_key_exchange(const struct PostQuantumCancelToken *sender);
/**
* Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped
* from memory.
*
* # Safety
* `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
* `PacketTunnelProvider`.
*/
void drop_post_quantum_key_exchange_token(const struct PostQuantumCancelToken *sender);
/**
* Called by Swift whenever data has been written to the in-tunnel TCP connection when exchanging
* quantum-resistant pre shared keys.
*
* If `bytes_sent` is 0, this indicates that the connection was closed or that an error occurred.
*
* # Safety
* `sender` must be pointing to a valid instance of a `write_tx` created by the `IosTcpProvider`
* Callback to call when the TCP connection has written data.
*/
void handle_sent(uintptr_t bytes_sent, const void *sender);
/**
* Called by Swift whenever data has been read from the in-tunnel TCP connection when exchanging
* quantum-resistant pre shared keys.
*
* If `data` is null or empty, this indicates that the connection was closed or that an error
* occurred. An empty buffer is sent to the underlying reader to signal EOF.
*
* # Safety
* `sender` must be pointing to a valid instance of a `read_tx` created by the `IosTcpProvider`
*
* Callback to call when the TCP connection has received data.
*/
void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender);
/**
* Entry point for exchanging post quantum keys on iOS.
* The TCP connection must be created to go through the tunnel.
* # Safety
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types.
* They will not be valid after this function is called, and thus must be copied here.
* `packet_tunnel` and `tcp_connection` must be valid pointers to a packet tunnel and a TCP
* connection instances.
* `cancel_token` should be owned by the caller of this function.
*/
int32_t negotiate_post_quantum_key(const uint8_t *public_key,
const uint8_t *ephemeral_key,
const void *packet_tunnel,
const void *tcp_connection,
struct PostQuantumCancelToken *cancel_token,
uint64_t post_quantum_key_exchange_timeout);
/**
* Called when there is data to send on the TCP connection.
* The TCP connection must write data on the wire, then call the `handle_sent` function.
*/
extern void swift_nw_tcp_connection_send(const void *connection,
const void *data,
uintptr_t data_len,
const void *sender);
/**
* Called when there is data to read on the TCP connection.
* The TCP connection must read data from the wire, then call the `handle_read` function.
*/
extern void swift_nw_tcp_connection_read(const void *connection, const void *sender);
/**
* Called when the preshared post quantum key is ready.
* `raw_preshared_key` might be NULL if the key negotiation failed.
*/
extern void swift_post_quantum_key_ready(const void *raw_packet_tunnel,
const uint8_t *raw_preshared_key,
const uint8_t *raw_ephemeral_private_key);
@@ -7,6 +7,7 @@
//
import Foundation
import MullvadRustRuntime
import MullvadTypes
public final class ShadowsocksTransport: RESTTransport {
@@ -1,5 +0,0 @@
framework module Shadowsocks {
header "shadowsocks.h"
link "libshadowsocks_proxy"
export *
}
@@ -1,26 +0,0 @@
[package]
name = "shadowsocks-proxy"
authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[lints]
workspace = true
[lib]
crate-type = [ "rlib", "staticlib" ]
bench = false
[dependencies]
shadowsocks-service = { workspace = true, features = [ "local", "stream-cipher", "local-http", "local-tunnel" ] }
tokio = { workspace = true }
log = "0.4"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
oslog = "0.2"
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.build-dependencies]
cbindgen = { version = "0.24.3", default-features = false }
@@ -1,14 +0,0 @@
#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("failed to generate bindings")
.write_to_file("include/shadowsocks.h");
}
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {}
@@ -1,36 +0,0 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct ProxyHandle {
void *context;
uint16_t port;
} ProxyHandle;
/**
* # Safety
* `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
* be backed by the amount of bytes as stored in the respective `*_len` parameters.
*
* `proxy_config` must be pointing to a valid memory region for the size of a `ProxyHandle`
* instance.
*/
int32_t start_shadowsocks_proxy(const uint8_t *forward_address,
uintptr_t forward_address_len,
uint16_t forward_port,
const uint8_t *addr,
uintptr_t addr_len,
uint16_t port,
const uint8_t *password,
uintptr_t password_len,
const uint8_t *cipher,
uintptr_t cipher_len,
struct ProxyHandle *proxy_config);
/**
* # Safety
* `proxy_config` must be pointing to a valid instance of a `ProxyInstance`, as instantiated by
* `start_shadowsocks_proxy`.
*/
int32_t stop_shadowsocks_proxy(struct ProxyHandle *proxy_config);
@@ -1,20 +0,0 @@
use std::{net::SocketAddr, str::FromStr};
fn main() {
let socketaddr = SocketAddr::from_str("185.65.135.117:443").unwrap();
let password = "mullvad";
let cipher = "aes-256-gcm";
let forward_address = SocketAddr::from_str("45.83.223.196:443").unwrap();
let (port, handle) =
shadowsocks_proxy::run_forwarding_proxy(forward_address, socketaddr, password, cipher)
.expect("failed to start SOCKS proxy");
println!("Running proxy on port {port}");
let _ = std::io::stdin().read_line(&mut String::new());
println!("Stopping proxy");
handle.stop();
println!("Done");
}
@@ -1,58 +0,0 @@
use std::{
net::{Ipv4Addr, SocketAddr},
str::FromStr,
};
fn main() {
let socketaddr = SocketAddr::from_str("185.65.135.117:443").unwrap();
let password = "mullvad";
let cipher = "aes-256-gcm";
let cipher_ptr = cipher.as_ptr();
let cipher_size = cipher.as_bytes().len();
let password_ptr = password.as_ptr();
let password_size = password.as_bytes().len();
let forward_address = Ipv4Addr::from_str("45.83.223.196").unwrap();
let forward_address_bytes = forward_address.octets();
let forward_address_ptr = forward_address_bytes.as_ptr();
let addr = Ipv4Addr::from_str("185.65.135.117").unwrap();
let addr_bytes = addr.octets();
let addr_ptr = addr_bytes.as_ptr();
let mut ctx = shadowsocks_proxy::ProxyHandle {
port: 0,
context: std::ptr::null_mut(),
};
let retval = unsafe {
shadowsocks_proxy::start_shadowsocks_proxy(
forward_address_ptr,
forward_address_bytes.len(),
443,
addr_ptr,
addr_bytes.len(),
socketaddr.port(),
password_ptr,
password_size,
cipher_ptr,
cipher_size,
&mut ctx as *mut _,
)
};
if retval != 0 {
println!("Failed to start proxy - {retval}");
return;
}
println!("Running proxy on port {}", ctx.port);
let _ = std::io::stdin().read_line(&mut String::new());
println!("Stopping proxy");
let retval = unsafe { shadowsocks_proxy::stop_shadowsocks_proxy(&mut ctx as *mut _) };
if retval != 0 {
println!("Failed to stop proxy");
}
println!("Done");
}
@@ -1,36 +0,0 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct ProxyHandle {
void *context;
uint16_t port;
} ProxyHandle;
/**
* # Safety
* `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
* be backed by the amount of bytes as stored in the respective `*_len` parameters.
*
* `proxy_config` must be pointing to a valid memory region for the size of a `ProxyHandle`
* instance.
*/
int32_t start_shadowsocks_proxy(const uint8_t *forward_address,
uintptr_t forward_address_len,
uint16_t forward_port,
const uint8_t *addr,
uintptr_t addr_len,
uint16_t port,
const uint8_t *password,
uintptr_t password_len,
const uint8_t *cipher,
uintptr_t cipher_len,
struct ProxyHandle *proxy_config);
/**
* # Safety
* `proxy_config` must be pointing to a valid instance of a `ProxyInstance`, as instantiated by
* `start_shadowsocks_proxy`.
*/
int32_t stop_shadowsocks_proxy(struct ProxyHandle *proxy_config);
@@ -0,0 +1,10 @@
//
// MullvadRustRuntime.h
// MullvadRustRuntime
//
// Created by Marco Nikic on 2024-06-20.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "mullvad_rust_runtime.h"
@@ -7,9 +7,9 @@
//
import Foundation
import MullvadRustRuntimeProxy
import MullvadTypes
import NetworkExtension
import TalpidTunnelConfigClientProxy
import WireGuardKitTypes
/// Writes data to the in-tunnel TCP connection
@@ -90,15 +90,13 @@ func tcpConnectionReceive(
/// - rawEphemeralKey: A raw pointer to the ephemeral private key of the device
@_cdecl("swift_post_quantum_key_ready")
func receivePostQuantumKey(
rawPacketTunnel: UnsafeMutableRawPointer?,
rawPostQuantumKeyReceiver: UnsafeMutableRawPointer?,
rawPresharedKey: UnsafeMutableRawPointer?,
rawEphemeralKey: UnsafeMutableRawPointer?
) {
guard
let rawPacketTunnel,
let postQuantumKeyReceiver = Unmanaged<NEPacketTunnelProvider>.fromOpaque(rawPacketTunnel)
.takeUnretainedValue() as? PostQuantumKeyReceiving
else { return }
guard let rawPostQuantumKeyReceiver else { return }
let postQuantumKeyReceiver = Unmanaged<PostQuantumKeyReceiver>.fromOpaque(rawPostQuantumKeyReceiver)
.takeUnretainedValue()
guard
let rawPresharedKey,
@@ -7,10 +7,9 @@
//
import Foundation
import MullvadREST
import MullvadRustRuntimeProxy
import MullvadTypes
import NetworkExtension
import PacketTunnelCore
import WireGuardKitTypes
public class PostQuantumKeyExchangeActor {
@@ -29,7 +28,8 @@ public class PostQuantumKeyExchangeActor {
unowned let packetTunnel: any TunnelProvider
internal var negotiation: Negotiation?
private var timer: DispatchSourceTimer?
private var keyExchangeRetriesIterator: AnyIterator<Duration>
private var keyExchangeRetriesIterator: AnyIterator<Duration>!
private let iteratorProvider: () -> AnyIterator<Duration>
private let negotiationProvider: PostQuantumKeyNegotiating.Type
// Callback in the event of the negotiation failing on startup
@@ -39,13 +39,13 @@ public class PostQuantumKeyExchangeActor {
packetTunnel: any TunnelProvider,
onFailure: @escaping (() -> Void),
negotiationProvider: PostQuantumKeyNegotiating.Type = PostQuantumKeyNegotiator.self,
keyExchangeRetriesIterator: AnyIterator<Duration> = REST.RetryStrategy.postQuantumKeyExchange
.makeDelayIterator()
iteratorProvider: @escaping () -> AnyIterator<Duration>
) {
self.packetTunnel = packetTunnel
self.onFailure = onFailure
self.negotiationProvider = negotiationProvider
self.keyExchangeRetriesIterator = keyExchangeRetriesIterator
self.iteratorProvider = iteratorProvider
self.keyExchangeRetriesIterator = iteratorProvider()
}
private func createTCPConnection(_ gatewayEndpoint: NWHostEndpoint) -> NWTCPConnection {
@@ -93,7 +93,7 @@ public class PostQuantumKeyExchangeActor {
gatewayIP: IPv4Gateway,
devicePublicKey: privateKey.publicKey,
presharedKey: ephemeralSharedKey,
packetTunnel: packetTunnel,
postQuantumKeyReceiver: packetTunnel,
tcpConnection: inTunnelTCPConnection,
postQuantumKeyExchangeTimeout: tcpConnectionTimeout
) {
@@ -116,7 +116,7 @@ public class PostQuantumKeyExchangeActor {
/// Resets the exponential timeout for successful key exchanges, and ends the current key exchange.
public func reset() {
keyExchangeRetriesIterator = REST.RetryStrategy.postQuantumKeyExchange.makeDelayIterator()
keyExchangeRetriesIterator = iteratorProvider()
endCurrentNegotiation()
}
@@ -9,8 +9,6 @@
import Foundation
import MullvadTypes
import NetworkExtension
import PacketTunnelCore
import TalpidTunnelConfigClientProxy
import WireGuardKitTypes
// swiftlint:disable function_parameter_count
@@ -19,7 +17,7 @@ public protocol PostQuantumKeyNegotiating {
gatewayIP: IPv4Address,
devicePublicKey: PublicKey,
presharedKey: PrivateKey,
packetTunnel: any TunnelProvider,
postQuantumKeyReceiver: any TunnelProvider,
tcpConnection: NWTCPConnection,
postQuantumKeyExchangeTimeout: Duration
) -> Bool
@@ -41,19 +39,20 @@ public class PostQuantumKeyNegotiator: PostQuantumKeyNegotiating {
gatewayIP: IPv4Address,
devicePublicKey: PublicKey,
presharedKey: PrivateKey,
packetTunnel: any TunnelProvider,
postQuantumKeyReceiver: any TunnelProvider,
tcpConnection: NWTCPConnection,
postQuantumKeyExchangeTimeout: Duration
) -> Bool {
// swiftlint:disable:next force_cast
let packetTunnelPointer = Unmanaged.passUnretained(packetTunnel as! NEPacketTunnelProvider).toOpaque()
let postQuantumKeyReceiver = Unmanaged.passUnretained(postQuantumKeyReceiver as! PostQuantumKeyReceiver)
.toOpaque()
let opaqueConnection = Unmanaged.passUnretained(tcpConnection).toOpaque()
var cancelToken = PostQuantumCancelToken()
let result = negotiate_post_quantum_key(
devicePublicKey.rawValue.map { $0 },
presharedKey.rawValue.map { $0 },
packetTunnelPointer,
postQuantumKeyReceiver,
opaqueConnection,
&cancelToken,
UInt64(postQuantumKeyExchangeTimeout.timeInterval)
@@ -7,8 +7,8 @@
//
import Foundation
import MullvadRustRuntimeProxy
import Network
import Shadowsocks
/// A Swift wrapper around a Rust implementation of Shadowsocks proxy instance
public class ShadowsocksProxy {
@@ -7,9 +7,9 @@
//
import Foundation
import MullvadRustRuntimeProxy
import MullvadTypes
import Network
import TunnelObfuscatorProxy
public protocol TunnelObfuscation {
init(remoteAddress: IPAddress, tcpPort: UInt16)
@@ -0,0 +1,137 @@
// This file is generated automatically. To update it forcefully, run `cargo run -p mullvad-ios --target aarch64-apple-ios`.
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct PostQuantumCancelToken {
void *context;
} PostQuantumCancelToken;
typedef struct ProxyHandle {
void *context;
uint16_t port;
} ProxyHandle;
extern const uint16_t CONFIG_SERVICE_PORT;
/**
* Called by the Swift side to signal that the quantum-secure key exchange should be cancelled.
* After this call, the cancel token is no longer valid.
*
* # Safety
* `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
* `PacketTunnelProvider`.
*/
void cancel_post_quantum_key_exchange(const struct PostQuantumCancelToken *sender);
/**
* Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped
* from memory.
*
* # Safety
* `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
* `PacketTunnelProvider`.
*/
void drop_post_quantum_key_exchange_token(const struct PostQuantumCancelToken *sender);
/**
* Called by Swift whenever data has been written to the in-tunnel TCP connection when exchanging
* quantum-resistant pre shared keys.
*
* If `bytes_sent` is 0, this indicates that the connection was closed or that an error occurred.
*
* # Safety
* `sender` must be pointing to a valid instance of a `write_tx` created by the `IosTcpProvider`
* Callback to call when the TCP connection has written data.
*/
void handle_sent(uintptr_t bytes_sent, const void *sender);
/**
* Called by Swift whenever data has been read from the in-tunnel TCP connection when exchanging
* quantum-resistant pre shared keys.
*
* If `data` is null or empty, this indicates that the connection was closed or that an error
* occurred. An empty buffer is sent to the underlying reader to signal EOF.
*
* # Safety
* `sender` must be pointing to a valid instance of a `read_tx` created by the `IosTcpProvider`
*
* Callback to call when the TCP connection has received data.
*/
void handle_recv(const uint8_t *data, uintptr_t data_len, const void *sender);
/**
* Entry point for exchanging post quantum keys on iOS.
* The TCP connection must be created to go through the tunnel.
* # Safety
* `public_key` and `ephemeral_key` must be valid respective `PublicKey` and `PrivateKey` types.
* They will not be valid after this function is called, and thus must be copied here.
* `packet_tunnel` and `tcp_connection` must be valid pointers to a packet tunnel and a TCP
* connection instances.
* `cancel_token` should be owned by the caller of this function.
*/
int32_t negotiate_post_quantum_key(const uint8_t *public_key,
const uint8_t *ephemeral_key,
const void *packet_tunnel,
const void *tcp_connection,
struct PostQuantumCancelToken *cancel_token,
uint64_t post_quantum_key_exchange_timeout);
/**
* Called when there is data to send on the TCP connection.
* The TCP connection must write data on the wire, then call the `handle_sent` function.
*/
extern void swift_nw_tcp_connection_send(const void *connection,
const void *data,
uintptr_t data_len,
const void *sender);
/**
* Called when there is data to read on the TCP connection.
* The TCP connection must read data from the wire, then call the `handle_read` function.
*/
extern void swift_nw_tcp_connection_read(const void *connection, const void *sender);
/**
* Called when the preshared post quantum key is ready.
* `raw_preshared_key` might be NULL if the key negotiation failed.
*/
extern void swift_post_quantum_key_ready(const void *raw_packet_tunnel,
const uint8_t *raw_preshared_key,
const uint8_t *raw_ephemeral_private_key);
/**
* # Safety
* `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
* be backed by the amount of bytes as stored in the respective `*_len` parameters.
*
* `proxy_config` must be pointing to a valid memory region for the size of a `ProxyHandle`
* instance.
*/
int32_t start_shadowsocks_proxy(const uint8_t *forward_address,
uintptr_t forward_address_len,
uint16_t forward_port,
const uint8_t *addr,
uintptr_t addr_len,
uint16_t port,
const uint8_t *password,
uintptr_t password_len,
const uint8_t *cipher,
uintptr_t cipher_len,
struct ProxyHandle *proxy_config);
/**
* # Safety
* `proxy_config` must be pointing to a valid instance of a `ProxyInstance`, as instantiated by
* `start_shadowsocks_proxy`.
*/
int32_t stop_shadowsocks_proxy(struct ProxyHandle *proxy_config);
int32_t start_tunnel_obfuscator_proxy(const uint8_t *peer_address,
uintptr_t peer_address_len,
uint16_t peer_port,
struct ProxyHandle *proxy_handle);
int32_t stop_tunnel_obfuscator_proxy(struct ProxyHandle *proxy_handle);
@@ -0,0 +1,5 @@
framework module MullvadRustRuntimeProxy {
header "mullvad_rust_runtime.h"
link "libmullvad_ios"
export *
}
@@ -6,7 +6,7 @@
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
@testable import MullvadPostQuantum
@testable import MullvadRustRuntime
@testable import MullvadTypes
import NetworkExtension
@testable import PacketTunnelCore
@@ -7,7 +7,7 @@
//
@testable import MullvadMockData
@testable import MullvadPostQuantum
@testable import MullvadRustRuntime
@testable import MullvadTypes
import NetworkExtension
@testable import PacketTunnelCore
@@ -31,7 +31,8 @@ class MullvadPostQuantumTests: XCTestCase {
onFailure: {
negotiationFailure.fulfill()
},
negotiationProvider: FailedNegotiatorStub.self
negotiationProvider: FailedNegotiatorStub.self,
iteratorProvider: { AnyIterator { .milliseconds(10) } }
)
let privateKey = PrivateKey()
@@ -51,7 +52,7 @@ class MullvadPostQuantumTests: XCTestCase {
negotiationFailure.fulfill()
},
negotiationProvider: FailedNegotiatorStub.self,
keyExchangeRetriesIterator: AnyIterator { .milliseconds(10) }
iteratorProvider: { AnyIterator { .milliseconds(10) } }
)
let privateKey = PrivateKey()
@@ -69,7 +70,8 @@ class MullvadPostQuantumTests: XCTestCase {
onFailure: {
unexpectedNegotiationFailure.fulfill()
},
negotiationProvider: SuccessfulNegotiatorStub.self
negotiationProvider: SuccessfulNegotiatorStub.self,
iteratorProvider: { AnyIterator { .milliseconds(10) } }
)
let privateKey = PrivateKey()
@@ -6,8 +6,8 @@
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//
import MullvadRustRuntime
import Network
import TunnelObfuscation
import XCTest
final class TunnelObfuscationTests: XCTestCase {
@@ -0,0 +1,47 @@
//
// PostQuantumKeyReceiver.swift
// MullvadTypes
//
// Created by Marco Nikic on 2024-07-04.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
import Foundation
import NetworkExtension
import WireGuardKitTypes
public class PostQuantumKeyReceiver: PostQuantumKeyReceiving, TunnelProvider {
unowned let tunnelProvider: NEPacketTunnelProvider
public init(tunnelProvider: NEPacketTunnelProvider) {
self.tunnelProvider = tunnelProvider
}
// MARK: - PostQuantumKeyReceiving
public func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey) {
guard let receiver = tunnelProvider as? PostQuantumKeyReceiving else { return }
receiver.receivePostQuantumKey(key, ephemeralKey: ephemeralKey)
}
public func keyExchangeFailed() {
guard let receiver = tunnelProvider as? PostQuantumKeyReceiving else { return }
receiver.keyExchangeFailed()
}
// MARK: - TunnelProvider
public func createTCPConnectionThroughTunnel(
to remoteEndpoint: NWEndpoint,
enableTLS: Bool,
tlsParameters TLSParameters: NWTLSParameters?,
delegate: Any?
) -> NWTCPConnection {
tunnelProvider.createTCPConnectionThroughTunnel(
to: remoteEndpoint,
enableTLS: enableTLS,
tlsParameters: TLSParameters,
delegate: delegate
)
}
}
@@ -9,7 +9,7 @@
import Foundation
import WireGuardKitTypes
public protocol PostQuantumKeyReceiving {
public protocol PostQuantumKeyReceiving: AnyObject {
func receivePostQuantumKey(_ key: PreSharedKey, ephemeralKey: PrivateKey)
func keyExchangeFailed()
}
@@ -1,8 +1,8 @@
//
// NEPacketTunnelProvider+Extensions.swift
// PacketTunnelCore
// TunnelProvider.swift
// MullvadTypes
//
// Created by Marco Nikic on 2024-06-12.
// Created by Marco Nikic on 2024-07-04.
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
//
File diff suppressed because it is too large. Load diff
+1
View File
@@ -10,6 +10,7 @@ import BackgroundTasks
import MullvadLogging
import MullvadMockData
import MullvadREST
import MullvadRustRuntime
import MullvadSettings
import MullvadTypes
import Operations
@@ -8,13 +8,12 @@
import Foundation
import MullvadLogging
import MullvadPostQuantum
import MullvadREST
import MullvadRustRuntime
import MullvadSettings
import MullvadTypes
import NetworkExtension
import PacketTunnelCore
import TunnelObfuscation
import WireGuardKitTypes
class PacketTunnelProvider: NEPacketTunnelProvider {
@@ -32,6 +31,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
private let multihopStateListener = MultihopStateListener()
private let multihopUpdater: MultihopUpdater
private let constraintsUpdater = RelayConstraintsUpdater()
private lazy var postQuantumReceiver = {
PostQuantumKeyReceiver(tunnelProvider: self)
}()
override init() {
Self.configureLogging()
@@ -92,7 +94,11 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
protocolObfuscator: ProtocolObfuscator<UDPOverTCPObfuscator>()
)
postQuantumActor = PostQuantumKeyExchangeActor(packetTunnel: self, onFailure: self.keyExchangeFailed)
postQuantumActor = PostQuantumKeyExchangeActor(
packetTunnel: postQuantumReceiver,
onFailure: self.keyExchangeFailed,
iteratorProvider: { REST.RetryStrategy.postQuantumKeyExchange.makeDelayIterator() }
)
let urlRequestProxy = URLRequestProxy(dispatchQueue: internalQueue, transportProvider: transportProvider)
appMessageHandler = AppMessageHandler(packetTunnelActor: actor, urlRequestProxy: urlRequestProxy)
@@ -9,9 +9,9 @@
import Foundation
import MullvadLogging
import MullvadREST
import MullvadRustRuntime
import MullvadTypes
import NetworkExtension
import TunnelObfuscation
import WireGuardKitTypes
/**
@@ -7,8 +7,8 @@
//
import Foundation
import MullvadRustRuntime
import MullvadTypes
import TunnelObfuscation
public protocol ProtocolObfuscation {
func obfuscate(_ endpoint: MullvadEndpoint, settings: Settings, retryAttempts: UInt) -> MullvadEndpoint
+1 -1
View File
@@ -8,8 +8,8 @@
import Foundation
import MullvadREST
import MullvadRustRuntime
import MullvadTypes
import TunnelObfuscation
import WireGuardKitTypes
/**
@@ -7,9 +7,9 @@
//
import Foundation
@testable import MullvadRustRuntime
@testable import MullvadTypes
import Network
@testable import TunnelObfuscation
struct TunnelObfuscationStub: TunnelObfuscation {
var transportLayer: TransportLayer { .udp }
-5
View File
@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>
-19
View File
@@ -1,19 +0,0 @@
//
// TunnelObfuscation.h
// TunnelObfuscation
//
// Created by pronebird on 2023-06-23.
// Copyright © 2022 Mullvad VPN AB. All rights reserved.
//
#import <Foundation/Foundation.h>
//! Project version number for TunnelObfuscation.
FOUNDATION_EXPORT double TunnelObfuscationVersionNumber;
//! Project version string for TunnelObfuscation.
FOUNDATION_EXPORT const unsigned char TunnelObfuscationVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <TunnelObfuscation/PublicHeader.h>
@@ -1,5 +0,0 @@
framework module TunnelObfuscatorProxy {
header "tunnel_obfuscator_proxy.h"
link "libtunnel_obfuscator_proxy"
export *
}
@@ -1,23 +0,0 @@
[package]
name = "tunnel-obfuscator-proxy"
authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[lints]
workspace = true
[lib]
crate-type = [ "rlib", "staticlib" ]
bench = false
[target.'cfg(target_os = "ios")'.dependencies]
tunnel-obfuscation = { path = "../../../tunnel-obfuscation" }
tokio = { workspace = true, features = ["sync"] }
log = "0.4"
oslog = "0.2"
[target.'cfg(target_os = "ios")'.build-dependencies]
cbindgen = { version = "0.24.3", default-features = false }
@@ -1,14 +0,0 @@
#[cfg(target_os = "ios")]
fn main() {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("failed to generate bindings")
.write_to_file("include/tunnel_obfuscator_proxy.h");
}
#[cfg(not(target_os = "ios"))]
fn main() {}
@@ -1,16 +0,0 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
typedef struct ProxyHandle {
void *context;
uint16_t port;
} ProxyHandle;
int32_t start_tunnel_obfuscator_proxy(const uint8_t *peer_address,
uintptr_t peer_address_len,
uint16_t peer_port,
struct ProxyHandle *proxy_handle);
int32_t stop_tunnel_obfuscator_proxy(struct ProxyHandle *proxy_handle);
@@ -1,92 +0,0 @@
#![cfg(target_os = "ios")]
use std::{io, net::SocketAddr};
use tokio::sync::oneshot;
use tunnel_obfuscation::{create_obfuscator, Settings as ObfuscationSettings, Udp2TcpSettings};
mod ffi;
pub use ffi::{start_tunnel_obfuscator_proxy, stop_tunnel_obfuscator_proxy, ProxyHandle};
pub struct TunnelObfuscatorRuntime {
runtime: tokio::runtime::Runtime,
settings: ObfuscationSettings,
}
impl TunnelObfuscatorRuntime {
pub fn new(peer: SocketAddr) -> io::Result<Self> {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let settings = ObfuscationSettings::Udp2Tcp(Udp2TcpSettings { peer });
Ok(Self { runtime, settings })
}
pub fn run(self) -> io::Result<(SocketAddr, TunnelObfuscatorHandle)> {
let (tx, rx) = oneshot::channel();
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let (startup_tx, startup_rx) = oneshot::channel();
std::thread::spawn(move || {
self.run_service_inner(rx, startup_tx);
});
match startup_rx.blocking_recv() {
Ok(Ok(endpoint)) => Ok((endpoint, TunnelObfuscatorHandle { tx })),
Ok(Err(err)) => {
let _ = tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
Err(io::Error::new(io::ErrorKind::Other, err))
}
Err(_) => {
let _ = tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
Err(io::Error::new(
io::ErrorKind::Other,
"Tokio runtime crashed",
))
}
}
}
fn run_service_inner(
self,
shutdown_rx: oneshot::Receiver<oneshot::Sender<()>>,
startup_done_tx: oneshot::Sender<io::Result<SocketAddr>>,
) {
let Self {
settings, runtime, ..
} = self;
std::thread::spawn(move || {
runtime.spawn(async move {
match create_obfuscator(&settings).await {
Ok(obfuscator) => {
let endpoint = obfuscator.endpoint();
let _ = startup_done_tx.send(Ok(endpoint));
let _ = obfuscator.run().await;
}
Err(err) => {
let _ =
startup_done_tx.send(Err(io::Error::new(io::ErrorKind::Other, err)));
}
}
});
if let Ok(shutdown_tx) = runtime.block_on(shutdown_rx) {
std::mem::drop(runtime);
let _ = shutdown_tx.send(());
}
});
}
}
pub struct TunnelObfuscatorHandle {
tx: oneshot::Sender<oneshot::Sender<()>>,
}
impl TunnelObfuscatorHandle {
pub fn stop(self) {
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let _ = self.tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
}
}
+36
View File
@@ -0,0 +1,36 @@
[package]
name = "mullvad-ios"
description = "Entry point for all FFI code on iOS"
authors.workspace = true
repository.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[lints]
workspace = true
[target.'cfg(target_os = "ios")'.dependencies]
libc = "0.2"
log = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
tonic = { workspace = true }
tower = { workspace = true }
tunnel-obfuscation = { path = "../tunnel-obfuscation" }
oslog = "0.2"
talpid-types = { path = "../talpid-types" }
talpid-tunnel-config-client = { path = "../talpid-tunnel-config-client" }
shadowsocks-service = { workspace = true, features = [
"local",
"stream-cipher",
"local-http",
"local-tunnel",
] }
[target.'cfg(target_os = "macos")'.build-dependencies]
cbindgen = { version = "0.24.3", default-features = false }
[lib]
crate-type = ["staticlib"]
bench = false
+16
View File
@@ -0,0 +1,16 @@
fn main() {
#[cfg(target_os = "macos")]
match std::env::var("TARGET").unwrap().as_str() {
"aarch64-apple-ios" | "aarch64-apple-ios-sim" => {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_autogen_warning("// This file is generated automatically. To update it forcefully, run `cargo run -p mullvad-ios --target aarch64-apple-ios`.")
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("failed to generate bindings")
.write_to_file("../ios/MullvadRustRuntime/include/mullvad_rust_runtime.h");
}
&_ => (),
}
}
+34
View File
@@ -0,0 +1,34 @@
#![cfg(target_os = "ios")]
mod post_quantum_proxy;
mod shadowsocks_proxy;
mod tunnel_obfuscator_proxy;
#[repr(C)]
pub struct ProxyHandle {
pub context: *mut std::ffi::c_void,
pub port: u16,
}
#[no_mangle]
pub static CONFIG_SERVICE_PORT: u16 = talpid_tunnel_config_client::CONFIG_SERVICE_PORT;
mod ios {
use std::sync::OnceLock;
use tokio::runtime::{Builder, Handle, Runtime};
static RUNTIME: OnceLock<Result<Runtime, String>> = OnceLock::new();
pub fn mullvad_ios_runtime() -> Result<Handle, String> {
match RUNTIME.get_or_init(|| {
Builder::new_multi_thread()
.enable_all()
.build()
.map_err(|error| ToString::to_string(&error))
}) {
Ok(runtime) => Ok(runtime.handle().clone()),
Err(error) => Err(error.clone()),
}
}
}
use ios::*;
@@ -1,5 +1,4 @@
use super::{ios_tcp_connection::*, PostQuantumCancelToken};
use crate::{request_ephemeral_peer_with, Error, RelayConfigService};
use libc::c_void;
use std::{
future::Future,
@@ -8,8 +7,9 @@ use std::{
ptr,
sync::{Arc, Mutex},
};
use talpid_tunnel_config_client::{request_ephemeral_peer_with, Error, RelayConfigService};
use talpid_types::net::wireguard::{PrivateKey, PublicKey};
use tokio::runtime::Builder;
use tokio::runtime::Handle as TokioHandle;
use tonic::transport::channel::Endpoint;
use tower::util::service_fn;
@@ -22,6 +22,7 @@ pub unsafe fn run_post_quantum_psk_exchange(
packet_tunnel: *const c_void,
tcp_connection: *const c_void,
post_quantum_key_exchange_timeout: u64,
tokio_handle: TokioHandle,
) -> Result<PostQuantumCancelToken, Error> {
match unsafe {
IOSRuntime::new(
@@ -35,7 +36,7 @@ pub unsafe fn run_post_quantum_psk_exchange(
Ok(runtime) => {
let token = runtime.packet_tunnel.tcp_connection.clone();
runtime.run();
runtime.run(tokio_handle);
Ok(PostQuantumCancelToken {
context: Arc::into_raw(token) as *mut _,
})
@@ -57,7 +58,6 @@ unsafe impl Send for SwiftContext {}
unsafe impl Sync for SwiftContext {}
struct IOSRuntime {
runtime: tokio::runtime::Runtime,
pub_key: [u8; 32],
ephemeral_key: [u8; 32],
packet_tunnel: SwiftContext,
@@ -72,18 +72,12 @@ impl IOSRuntime {
tcp_connection: *const c_void,
post_quantum_key_exchange_timeout: u64,
) -> io::Result<Self> {
let runtime = Builder::new_multi_thread()
.enable_all()
.worker_threads(2)
.build()?;
let context = SwiftContext {
packet_tunnel,
tcp_connection: Arc::new(Mutex::new(ConnectionContext::new(tcp_connection))),
};
Ok(Self {
runtime,
pub_key,
ephemeral_key,
packet_tunnel: context,
@@ -91,9 +85,9 @@ impl IOSRuntime {
})
}
pub fn run(self) {
std::thread::spawn(move || {
self.run_service_inner();
pub fn run(self, handle: TokioHandle) {
handle.spawn(async move {
self.run_service_inner().await;
});
}
/// Creates a `RelayConfigService` using the in-tunnel TCP Connection provided by the Packet
@@ -124,60 +118,61 @@ impl IOSRuntime {
Ok((RelayConfigService::new(conn), conn_handle))
}
fn run_service_inner(self) {
let Self { runtime, .. } = self;
let packet_tunnel_ptr = self.packet_tunnel.packet_tunnel;
runtime.block_on(async move {
let (async_provider, shutdown_handle) = unsafe { match Self::ios_tcp_client(self.packet_tunnel).await {
async fn run_service_inner(self) {
let (async_provider, shutdown_handle) = unsafe {
match Self::ios_tcp_client(self.packet_tunnel.clone()).await {
Ok(result) => result,
Err(error) => {
log::error!("Failed to create iOS TCP client: {error}");
swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
swift_post_quantum_key_ready(
self.packet_tunnel.packet_tunnel,
ptr::null(),
ptr::null(),
);
return;
}
}};
let ephemeral_pub_key = PrivateKey::from(self.ephemeral_key).public_key();
}
};
let ephemeral_pub_key = PrivateKey::from(self.ephemeral_key).public_key();
tokio::select! {
ephemeral_peer = request_ephemeral_peer_with(
async_provider,
PublicKey::from(self.pub_key),
ephemeral_pub_key,
true,
false,
) => {
shutdown_handle.shutdown();
match ephemeral_peer {
Ok(peer) => {
match peer.psk {
Some(preshared_key) => unsafe {
let preshared_key_bytes = preshared_key.as_bytes();
swift_post_quantum_key_ready(packet_tunnel_ptr, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr());
},
None => {
log::error!("No suitable peer was found");
unsafe {
swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
}
tokio::select! {
ephemeral_peer = request_ephemeral_peer_with(
async_provider,
PublicKey::from(self.pub_key),
ephemeral_pub_key,
true,
false,
) => {
shutdown_handle.shutdown();
match ephemeral_peer {
Ok(peer) => {
match peer.psk {
Some(preshared_key) => unsafe {
let preshared_key_bytes = preshared_key.as_bytes();
swift_post_quantum_key_ready(self.packet_tunnel.packet_tunnel, preshared_key_bytes.as_ptr(), self.ephemeral_key.as_ptr());
},
None => {
log::error!("No suitable peer was found");
unsafe {
swift_post_quantum_key_ready(self.packet_tunnel.packet_tunnel, ptr::null(), ptr::null());
}
}
}
},
Err(error) => {
log::error!("Key exchange failed {}", error);
unsafe {
swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null());
}
}
},
Err(error) => {
log::error!("Key exchange failed {}", error);
unsafe {
swift_post_quantum_key_ready(self.packet_tunnel.packet_tunnel, ptr::null(), ptr::null());
}
}
}
_ = tokio::time::sleep(std::time::Duration::from_secs(self.post_quantum_key_exchange_timeout)) => {
shutdown_handle.shutdown();
unsafe { swift_post_quantum_key_ready(packet_tunnel_ptr, ptr::null(), ptr::null()); }
}
}
});
_ = tokio::time::sleep(std::time::Duration::from_secs(self.post_quantum_key_exchange_timeout)) => {
shutdown_handle.shutdown();
unsafe { swift_post_quantum_key_ready(self.packet_tunnel.packet_tunnel, ptr::null(), ptr::null()); }
}
}
}
}
@@ -1,7 +1,7 @@
pub mod ios_runtime;
pub mod ios_tcp_connection;
use crate::ios_ffi::ios_runtime::run_post_quantum_psk_exchange;
use ios_runtime::run_post_quantum_psk_exchange;
use ios_tcp_connection::ConnectionContext;
use libc::c_void;
use std::sync::{Arc, Mutex, Weak};
@@ -43,6 +43,7 @@ impl Drop for PostQuantumCancelToken {
unsafe impl Send for PostQuantumCancelToken {}
/// Called by the Swift side to signal that the quantum-secure key exchange should be cancelled.
/// After this call, the cancel token is no longer valid.
///
/// # Safety
/// `sender` must be pointing to a valid instance of a `PostQuantumCancelToken` created by the
@@ -52,6 +53,7 @@ pub unsafe extern "C" fn cancel_post_quantum_key_exchange(sender: *const PostQua
let sender = unsafe { &*sender };
sender.cancel();
}
/// Called by the Swift side to signal that the Rust `PostQuantumCancelToken` can be safely dropped
/// from memory.
///
@@ -130,16 +132,26 @@ pub unsafe extern "C" fn negotiate_post_quantum_key(
.init();
});
let pub_key_copy: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) };
let eph_key_copy: [u8; 32] = unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) };
let pub_key: [u8; 32] = unsafe { std::ptr::read(public_key as *const [u8; 32]) };
let eph_key: [u8; 32] = unsafe { std::ptr::read(ephemeral_key as *const [u8; 32]) };
let handle = match crate::mullvad_ios_runtime() {
Ok(handle) => handle,
Err(err) => {
log::error!("Failed to obtain a handle to a tokio runtime: {err}");
return -1;
}
};
match unsafe {
run_post_quantum_psk_exchange(
pub_key_copy,
eph_key_copy,
pub_key,
eph_key,
packet_tunnel,
tcp_connection,
post_quantum_key_exchange_timeout,
handle,
)
} {
Ok(token) => {
@@ -1,4 +1,5 @@
use super::{run_forwarding_proxy, ShadowsocksHandle};
use crate::ProxyHandle;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
#[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -7,12 +8,6 @@ use std::sync::Once;
#[cfg(any(target_os = "macos", target_os = "ios"))]
static INIT_LOGGING: Once = Once::new();
#[repr(C)]
pub struct ProxyHandle {
pub context: *mut std::ffi::c_void,
pub port: u16,
}
/// # Safety
/// `addr`, `password`, `cipher` must be valid for the lifetime of this function call and they must
/// be backed by the amount of bytes as stored in the respective `*_len` parameters.
@@ -1,3 +1,4 @@
use super::mullvad_ios_runtime;
use shadowsocks_service::{
config::{
Config, ConfigType, LocalConfig, LocalInstanceConfig, ProtocolType, ServerInstanceConfig,
@@ -10,10 +11,8 @@ use std::{
net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener},
str::FromStr,
};
use tokio::sync::oneshot;
use tokio::task::JoinHandle;
mod ffi;
pub use ffi::{start_shadowsocks_proxy, stop_shadowsocks_proxy, ProxyHandle};
pub fn run_forwarding_proxy(
forward_socket_addr: SocketAddr,
@@ -22,49 +21,38 @@ pub fn run_forwarding_proxy(
cipher: &str,
) -> io::Result<(u16, ShadowsocksHandle)> {
let runtime =
ShadowsocksRuntime::new(forward_socket_addr, bridge_socket_addr, password, cipher)?;
ShadowsocksService::new(forward_socket_addr, bridge_socket_addr, password, cipher)?;
let port = runtime.port();
let handle = runtime.run()?;
Ok((port, handle))
}
struct ShadowsocksRuntime {
runtime: tokio::runtime::Runtime,
struct ShadowsocksService {
config: Config,
local_port: u16,
}
pub struct ShadowsocksHandle {
tx: oneshot::Sender<oneshot::Sender<()>>,
abort_handle: JoinHandle<()>,
}
impl ShadowsocksHandle {
pub fn stop(self) {
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let _ = self.tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
self.abort_handle.abort();
}
}
impl ShadowsocksRuntime {
impl ShadowsocksService {
pub fn new(
forward_socket_addr: SocketAddr,
bridge_socket_addr: SocketAddr,
password: &str,
cipher: &str,
) -> io::Result<Self> {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let (config, local_port) =
Self::create_config(forward_socket_addr, bridge_socket_addr, password, cipher)?;
Ok(Self {
runtime,
config,
local_port,
})
Ok(Self { config, local_port })
}
pub fn port(&self) -> u16 {
@@ -72,57 +60,24 @@ impl ShadowsocksRuntime {
}
pub fn run(self) -> io::Result<ShadowsocksHandle> {
let (tx, rx) = oneshot::channel();
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let (startup_tx, startup_rx) = oneshot::channel();
std::thread::spawn(move || {
self.run_service_inner(rx, startup_tx);
let runtime = mullvad_ios_runtime().map_err(io::Error::other)?;
let abort_handle = runtime.spawn(async move {
self.run_service_inner().await;
});
match startup_rx.blocking_recv() {
Ok(Ok(_)) => Ok(ShadowsocksHandle { tx }),
Ok(Err(err)) => {
let _ = tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
Err(err)
}
Err(_) => {
let _ = tx.send(shutdown_tx);
let _ = shutdown_rx.blocking_recv();
Err(io::Error::new(
io::ErrorKind::Other,
"Tokio runtime crashed",
))
}
}
Ok(ShadowsocksHandle { abort_handle })
}
fn run_service_inner(
self,
shutdown_rx: oneshot::Receiver<oneshot::Sender<()>>,
startup_done_tx: oneshot::Sender<io::Result<()>>,
) {
let Self {
config, runtime, ..
} = self;
async fn run_service_inner(self) {
let Self { config, .. } = self;
std::thread::spawn(move || {
runtime.spawn(async move {
match Server::new(config).await {
Ok(server) => {
let _ = startup_done_tx.send(Ok(()));
let _ = server.run().await;
}
Err(err) => {
let _ = startup_done_tx.send(Err(err));
}
}
});
if let Ok(shutdown_tx) = runtime.block_on(shutdown_rx) {
std::mem::drop(runtime);
let _ = shutdown_tx.send(());
}
});
let _ = Server::new(config)
.await
.map_err(io::Error::from)
.expect("Could not create Shadowsocks server")
.run()
.await;
}
pub fn create_config(
@@ -1,6 +1,5 @@
#![cfg(target_os = "ios")]
use super::{TunnelObfuscatorHandle, TunnelObfuscatorRuntime};
use crate::ProxyHandle;
use std::{
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
sync::Once,
@@ -8,12 +7,6 @@ use std::{
static INIT_LOGGING: Once = Once::new();
#[repr(C)]
pub struct ProxyHandle {
pub context: *mut std::ffi::c_void,
pub port: u16,
}
#[no_mangle]
pub unsafe extern "C" fn start_tunnel_obfuscator_proxy(
peer_address: *const u8,
@@ -0,0 +1,51 @@
use std::{io, net::SocketAddr};
use tokio::task::JoinHandle;
use tunnel_obfuscation::{create_obfuscator, Settings as ObfuscationSettings, Udp2TcpSettings};
mod ffi;
use crate::mullvad_ios_runtime;
pub struct TunnelObfuscatorRuntime {
settings: ObfuscationSettings,
}
impl TunnelObfuscatorRuntime {
pub fn new(peer: SocketAddr) -> io::Result<Self> {
let settings = ObfuscationSettings::Udp2Tcp(Udp2TcpSettings { peer });
Ok(Self { settings })
}
pub fn run(self) -> io::Result<(SocketAddr, TunnelObfuscatorHandle)> {
let runtime = mullvad_ios_runtime().map_err(io::Error::other)?;
let obfuscator = runtime.block_on(async move {
create_obfuscator(&self.settings)
.await
.map_err(io::Error::other)
})?;
let endpoint = obfuscator.endpoint();
let join_handle = runtime.spawn(async move {
let _ = obfuscator.run().await;
});
Ok((
endpoint,
TunnelObfuscatorHandle {
obfuscator_abort_handle: join_handle,
},
))
}
}
pub struct TunnelObfuscatorHandle {
obfuscator_abort_handle: JoinHandle<()>,
}
impl TunnelObfuscatorHandle {
pub fn stop(self) {
self.obfuscator_abort_handle.abort();
}
}
-1
View File
@@ -35,7 +35,6 @@ tonic-build = { workspace = true, default-features = false, features = [
"transport",
"prost",
] }
cbindgen = { version = "0.24.3", default-features = false }
[target.'cfg(target_os = "ios")'.dependencies]
oslog = "0.2"
-12
View File
@@ -1,15 +1,3 @@
fn main() {
tonic_build::compile_protos("proto/ephemeralpeer.proto").unwrap();
match std::env::var("TARGET").unwrap().as_str() {
"aarch64-apple-ios" | "aarch64-apple-ios-sim" => {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
cbindgen::Builder::new()
.with_crate(crate_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("failed to generate bindings")
.write_to_file("../ios/MullvadPostQuantum/talpid-tunnel-config-client/include/talpid_tunnel_config_client.h");
}
&_ => (),
}
}
-3
View File
@@ -22,9 +22,6 @@ mod proto {
tonic::include_proto!("ephemeralpeer");
}
#[cfg(target_os = "ios")]
pub mod ios_ffi;
#[cfg(not(target_os = "ios"))]
use libc::setsockopt;