Skip to content
Merged

Dev #39

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
daea1c4
docs: Update README to improve architecture diagram clarity and forma…
itsalfredakku Jan 9, 2026
932c653
docs: Revise ISSUES.md to enhance structure, clarity, and detail on k…
itsalfredakku Jan 9, 2026
3a33695
refactor: Update ISSUES.md for clarity and consistency; rename 'Bugs'…
itsalfredakku Jan 10, 2026
f063b46
fix: Add timeout to additional connection establishment to prevent in…
itsalfredakku Jan 10, 2026
c5d9a09
refactor: Update session and stats getters to use caller-provided buf…
itsalfredakku Jan 10, 2026
1a9ccba
Refactor packet processing logic into a shared module
itsalfredakku Jan 10, 2026
e3618dd
refactor: Implement buffer pooling and load balancing for connection …
itsalfredakku Jan 10, 2026
83b5c83
refactor: Improve code formatting and readability across multiple mod…
itsalfredakku Jan 10, 2026
d008c51
feat: Enhance SoftEtherBridge with originalServerIP and error messagi…
itsalfredakku Jan 10, 2026
99f2177
refactor: Simplify conditional statement in TunnelRunner's packet han…
itsalfredakku Jan 10, 2026
c737ec0
refactor: Optimize buffer usage in run_packet_loop for improved perfo…
itsalfredakku Jan 10, 2026
e1f544f
refactor: Replace compress function with compress_into for better buf…
itsalfredakku Jan 10, 2026
80377de
Merge pull request #37 from devstroop/dev2
itsalfredakku Jan 10, 2026
8e1f21f
refactor: Remove unnecessary whitespace in run_packet_loop for cleane…
itsalfredakku Jan 10, 2026
4267ee0
Merge pull request #38 from devstroop/dev2
itsalfredakku Jan 10, 2026
296c848
refactor: Simplify assertions in tests for better readability and mai…
itsalfredakku Jan 10, 2026
75299b4
refactor: Improve readability of compile-time assertion for buffer po…
itsalfredakku Jan 10, 2026
47b8071
refactor: Optimize decompression handling in run_packet_loop for bett…
itsalfredakku Jan 10, 2026
7db1532
refactor: Enhance JNI session handling with additional IP and authent…
itsalfredakku Jan 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
394 changes: 265 additions & 129 deletions ISSUES.md

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,11 @@ async fn main() -> anyhow::Result<()> {
## Architecture

```
┌──────────────────────────────────────────────────────────────────
│ VpnClient
├──────────────────────────────────────────────────────────────────
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │
│ │ Protocol │ │ Crypto │ │ Adapter │ │
┌────────────────────────────────────────────────────────────────┐
│ VpnClient │
├────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌────────────────────────┐ │
│ │ Protocol │ │ Crypto │ │ Adapter │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌─────────────────┐ │ │
│ │ │ HTTP │ │ │ │ SHA-0 │ │ │ │ TunAdapter │ │ │
│ │ │ Codec │ │ │ └───────┘ │ │ │ ┌────┐ ┌────┐ │ │ │
Expand All @@ -301,19 +301,19 @@ async fn main() -> anyhow::Result<()> {
│ └─────────────┘ └─────────────┘ │ │ │iOS │ │JNI │ │ │ │
│ │ │ └────┘ └─────┘ │ │ │
│ ┌─────────────┐ ┌─────────────┐ │ └─────────────────┘ │ │
│ │ Tunnel │ │ Net │ └────────────────────────┘ │
│ │ ┌───────┐ │ │ ┌───────┐ │ ┌────────────────────────┐ │
│ │ │ DHCP │ │ │ │ UDP │ │ │ ConnectionManager │ │
│ │ Tunnel │ │ Net │ └────────────────────────┘ │
│ │ ┌───────┐ │ │ ┌───────┐ │ ┌────────────────────────┐ │
│ │ │ DHCP │ │ │ │ UDP │ │ │ ConnectionManager │ │
│ │ │DHCPv6 │ │ │ │ Accel │ │ │ ┌─────────────────┐ │ │
│ │ └───────┘ │ │ │ V1+V2 │ │ │ │ Multi-Connection│ │ │
│ │ ┌───────┐ │ │ └───────┘ │ │ │ Half-Connection │ │ │
│ │ │ ARP │ │ │ │ │ └─────────────────┘ │ │
│ │ └───────┘ │ │ │ └────────────────────────┘ │
│ └─────────────┘ └─────────────┘
├──────────────────────────────────────────────────────────────────
│ Tokio Async Runtime
│ (TCP/TLS/UDP, Timers, Signals)
└──────────────────────────────────────────────────────────────────
│ │ └───────┘ │ │ │ └────────────────────────┘ │
│ └─────────────┘ └─────────────┘ │
├────────────────────────────────────────────────────────────────┤
│ Tokio Async Runtime │
│ (TCP/TLS/UDP, Timers, Signals) │
└────────────────────────────────────────────────────────────────┘
```

## Building
Expand Down
50 changes: 30 additions & 20 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,39 @@
"server": "vpn.example.com",
"port": 443,
"hub": "DEFAULT",
"username": "your_username",
"password_hash": "0000000000000000000000000000000000000000",
"timeout_seconds": 30,

"_comment_auth": "Authentication configuration. Methods: standard_password, radius_or_nt_domain, certificate, anonymous",
"auth": {
"method": "standard_password",
"username": "your_username",
"password_hash": "0000000000000000000000000000000000000000",
"_comment_radius": "For RADIUS/NT Domain auth, use 'password' instead of 'password_hash'",
"_comment_cert": "For certificate auth, use 'certificate_pem' and 'private_key_pem'"
},

"_comment_tls": "TLS configuration",
"skip_tls_verify": true,
"nat_traversal": false,
"custom_ca_pem": null,
"cert_fingerprint_sha256": null,

"_comment_tunnel": "Tunnel features",
"use_encrypt": true,
"use_compress": true,
"use_compress": false,
"udp_accel": false,
"qos": true,

"_comment_session": "Session mode",
"nat_traversal": false,
"monitor_mode": false,

"_comment_performance": "Performance settings",
"max_connections": 1,
"half_connection": false,
"mtu": 1400,

"ip_version": "auto",

"routing": {
"default_route": false,
"accept_pushed_routes": true,
Expand All @@ -19,21 +43,7 @@
"ipv6_include": [],
"ipv6_exclude": []
},
"qos": false,
"timeout_seconds": 30,


"_comment_static_ip": "Static IP configuration (optional). When set, DHCP is skipped.",
"static_ip": {
"_comment": "Set these fields to use a static IP instead of DHCP",
"ipv4_address": null,
"ipv4_netmask": null,
"ipv4_gateway": null,
"ipv4_dns1": null,
"ipv4_dns2": null,
"ipv6_address": null,
"ipv6_prefix_len": null,
"ipv6_gateway": null,
"ipv6_dns1": null,
"ipv6_dns2": null
}
"static_ip": null
}
50 changes: 45 additions & 5 deletions examples/ios/SoftEtherBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public class SoftEtherBridge {
public let dns1: UInt32
public let dns2: UInt32
public let connectedServerIP: String
public let originalServerIP: String
public let serverVersion: UInt32
public let serverBuild: UInt32
public let macAddress: [UInt8]
Expand Down Expand Up @@ -370,15 +371,17 @@ public class SoftEtherBridge {
// Create client
handle = softether_create(&cConfig, &cCallbacks)
guard let h = handle else {
throw BridgeError.createFailed
let errorMsg = SoftEtherBridge.lastError
throw BridgeError.createFailed(message: errorMsg)
}

// Start connection
let result = softether_connect(h)
if result != SOFTETHER_OK {
let errorMsg = SoftEtherBridge.lastError
softether_destroy(h)
handle = nil
throw BridgeError.connectFailed(code: Int(result.rawValue))
throw BridgeError.connectFailed(code: Int(result.rawValue), message: errorMsg)
}
}

Expand Down Expand Up @@ -495,6 +498,18 @@ public class SoftEtherBridge {
public static var version: String {
String(cString: softether_version())
}

/// Get the last error message from the library.
/// Returns nil if no error occurred.
public static var lastError: String? {
guard let ptr = softether_get_last_error() else { return nil }
return String(cString: ptr)
}

/// Clear the last error message.
public static func clearLastError() {
softether_clear_last_error()
}
}

// MARK: - Callback Context
Expand Down Expand Up @@ -527,6 +542,11 @@ private func connectedCallback(context: UnsafeMutableRawPointer?, session: Unsaf
String(cString: $0)
}
},
originalServerIP: withUnsafePointer(to: s.original_server_ip) {
$0.withMemoryRebound(to: CChar.self, capacity: 64) {
String(cString: $0)
}
},
serverVersion: s.server_version,
serverBuild: s.server_build,
macAddress: Array(withUnsafeBytes(of: s.mac_address) { Array($0) }),
Expand Down Expand Up @@ -620,11 +640,31 @@ private func excludeIpCallback(context: UnsafeMutableRawPointer?, ip: UnsafePoin

// MARK: - Errors

public enum BridgeError: Error {
public enum BridgeError: Error, LocalizedError {
case alreadyConnected
case createFailed
case connectFailed(code: Int)
case createFailed(message: String?)
case connectFailed(code: Int, message: String?)
case sendFailed(code: Int)
case disconnected(code: Int)
case queueFull // Backpressure - caller should retry

public var errorDescription: String? {
switch self {
case .alreadyConnected:
return "Already connected"
case .createFailed(let message):
return message ?? "Failed to create VPN client"
case .connectFailed(let code, let message):
if let msg = message {
return msg
}
return "Connection failed with code \(code)"
case .sendFailed(let code):
return "Send failed with code \(code)"
case .disconnected(let code):
return "Disconnected with code \(code)"
case .queueFull:
return "Queue full - retry later"
}
}
}
37 changes: 29 additions & 8 deletions include/SoftEtherVPN.h
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,26 @@ int softether_receive_packets(SoftEtherHandle handle, uint8_t* buffer, size_t bu
*/
const char* softether_version(void);

/**
* Get the last error message.
*
* Returns a pointer to a null-terminated string describing the last error
* that occurred in the current thread. The pointer is valid until the next
* call to any softether_* function on the same thread.
*
* Thread-safe: Each thread has its own error storage.
*
* @return Error message, or NULL if no error occurred
*/
const char* softether_get_last_error(void);

/**
* Clear the last error message.
*
* Resets the thread-local error state to NULL.
*/
void softether_clear_last_error(void);

// =============================================================================
// Helper Functions
// =============================================================================
Expand Down Expand Up @@ -368,22 +388,23 @@ int softether_ios_mac_to_string(const uint8_t* mac, char* buffer, size_t buffer_
int softether_ios_is_valid_ipv4(uint32_t ip);

/**
* Get session information (simplified for Swift).
* Returns pointer to internal session data that remains valid until the next call.
* Get session information into caller-provided buffer.
* This is the recommended API - caller manages memory, avoiding thread-safety issues.
*
* @param handle Client handle
* @return Pointer to session data, or NULL if not connected
* @param session_out Pointer to caller-allocated SoftEtherSession struct
* @return SOFTETHER_OK on success, error code otherwise
*/
const SoftEtherSession* softether_ios_get_session(SoftEtherHandle handle);
SoftEtherResult softether_ios_get_session(SoftEtherHandle handle, SoftEtherSession* session_out);

/**
* Get statistics (simplified for Swift).
* Returns pointer to internal stats data that remains valid until the next call.
* Get statistics into caller-provided buffer.
*
* @param handle Client handle
* @return Pointer to statistics data, or NULL on error
* @param stats_out Pointer to caller-allocated SoftEtherStats struct
* @return SOFTETHER_OK on success, error code otherwise
*/
const SoftEtherStats* softether_ios_get_stats(SoftEtherHandle handle);
SoftEtherResult softether_ios_get_stats(SoftEtherHandle handle, SoftEtherStats* stats_out);

/**
* Format byte count as human-readable string (B, KB, MB, GB).
Expand Down
Loading