Browse Source

support Android per-app routing scenarios (#245)

pull/246/head
Alexander Olkhovoy 2 months ago
committed by GitHub
parent
commit
e55d7a1f6f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 29
      README.md
  2. 16
      src/socks.rs

29
README.md

@ -21,7 +21,36 @@ A tunnel interface for HTTP and SOCKS proxies on Linux, Android, macOS, iOS and
- SOCKS5 UDP support
- Native support for proxying DNS over TCP
- UdpGW (UDP gateway) support for UDP over TCP, see the [wiki](https://github.com/tun2proxy/tun2proxy/wiki/UDP-gateway-feature) for more information
- Session info embedding for per-app routing on Android (see below)
## Session Info for Per-App Routing (Android)
To enable per-app traffic routing on Android 10+, you can embed session information (protocol, source IP, source port) in the SOCKS5 username field. This allows your proxy server to call `getConnectionOwnerUid()` to identify which app initiated the connection.
To enable this feature, append `+info` to your username:
```bash
# Without session info (normal mode)
tun2proxy --proxy "socks5://user:[email protected]:1080"
# With session info
tun2proxy --proxy "socks5://user+info:[email protected]:1080"
```
The proxy server will receive the username in the format:
```
original_username|protocol|src_ip|src_port
```
`|` is a literal field delimiter in this format. The client does not escape `|` characters in
`original_username`, so when using `+info`, the original username must not contain `|` (including
a `|` introduced via percent-encoding in the proxy URL), otherwise the value becomes ambiguous for
server-side parsers.
For example, `user+info` becomes `user|tcp|10.0.0.5|54321`.
Servers should parse this as exactly four `|`-separated fields and reject usernames that do not
meet that requirement.
## Build
Clone the repository and `cd` into the project folder. Then run the following:
```

16
src/socks.rs

@ -170,7 +170,21 @@ impl SocksProxyImpl {
fn send_auth_data(&mut self) -> std::io::Result<()> {
let tmp = UserKey::default();
let credentials = self.credentials.as_ref().unwrap_or(&tmp);
let request = password_method::Request::new(&credentials.username, &credentials.password);
const SESSION_INFO_MARKER: &str = "+info";
let username = if credentials.username.ends_with(SESSION_INFO_MARKER) {
let base_username = &credentials.username[..credentials.username.len() - SESSION_INFO_MARKER.len()];
let proto = match self.command {
protocol::Command::Connect => "tcp",
protocol::Command::UdpAssociate => "udp",
_ => "unknown",
};
format!("{}|{}|{}|{}", base_username, proto, self.info.src.ip(), self.info.src.port())
} else {
credentials.username.clone()
};
let request = password_method::Request::new(&username, &credentials.password);
request.write_to_stream(&mut self.server_outbuf)?;
self.state = SocksState::ReceiveAuthResponse;
Ok(())

Loading…
Cancel
Save