Skip to content

Commit 4c13017

Browse files
authored
Merge pull request #149 from SenseUnit/socks5s
Support upstream SOCKS5 over TLS
2 parents c0435b8 + 4e1c6c1 commit 4c13017

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,17 @@ Supported proxy schemes are:
395395
* `h2c` - HTTP/2 proxy over plaintext connection with the CONNECT method support. Examples: `h2c://example.org:8080`.
396396
* `fetchrandom` - request server to send random data in the first request via every new HTTP/2 connection. Useful to trick TLS-in-TLS detection. Value format: length as a number or range `x-y`. Example: `fetchrandom=100000-500000`.
397397
* `socks5`, `socks5h` - SOCKS5 proxy with hostname resolving via remote proxy. Example: `socks5://127.0.0.1:9050`.
398+
* `socks5s`, `socks5hs` - SOCKS5 proxy over TLS with hostname resolving via remote proxy. Example: `socks5s://example.com:10443`. This method also supports additional parameters passed in query string:
399+
* `cafile` - file with CA certificates in PEM format used to verify TLS peer.
400+
* `sni` - override value of ServerName Indication extension.
401+
* `peername` - expect specified name in peer certificate. Empty string relaxes any name constraints.
402+
* `cert` - file with user certificate for mutual TLS authentication. Should be used in conjunction with `key`.
403+
* `key` - file with private key matching user certificate specified with `cert` option.
404+
* `ciphers` - colon-separated list of enabled TLS ciphersuites.
405+
* `curves` - colon-separated list of enabled TLS key exchange curves.
406+
* `min-tls-version` - minimum TLS version.
407+
* `max-tls-version` - maximum TLS version.
408+
* `utls-fp` - TLS fingerprint parroting with uTLS library. See the [list](https://pkg.go.dev/github.com/refraction-networking/utls#pkg-variables) of allowed client IDs. Example: `utls-fp=HelloChrome_Auto`.
398409
* `set-src-hints` - not an actual proxy, but a signal to use different source IP address hints for this connection. It's useful to route traffic across multiple network interfaces, including VPN connections. URL has to have one query parameter `hints` with a comma-separated list of IP addresses. See `-ip-hints` command line option for more details. Example: `set-src-hints://?hints=10.2.0.2`
399410
* `cached` - pseudo-dialer which caches construction of another dialer specified by URL passed in `url` parameter of query string. Useful for dialers which are constructed dynamically from JS router script and which load certificate files. Example: `cache://?url=https%3A%2F%2Fexample.org%3Fcert%3Dcert.pem%26key%3Dkey.pem&ttl=5m`. Query string parameters are:
400411
* `url` - actual proxy URL. Note that just like any query string parameter this one has to be URL-encoded to be passed as query string value.

dialer/dialer.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ func init() {
2222
xproxy.RegisterDialerType("h2c", H2ProxyDialerFromURL)
2323
xproxy.RegisterDialerType("set-src-hints", NewHintsSettingDialerFromURL)
2424
xproxy.RegisterDialerType("cached", GetCachedDialer)
25+
xproxy.RegisterDialerType("socks5s", SOCKS5SDialerFromURL)
26+
xproxy.RegisterDialerType("socks5hs", SOCKS5SDialerFromURL)
2527
}
2628

2729
type LegacyDialer interface {

dialer/socks5s.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package dialer
2+
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"fmt"
7+
"net"
8+
"net/url"
9+
10+
"github.com/SenseUnit/dumbproxy/tlsutil"
11+
xproxy "golang.org/x/net/proxy"
12+
)
13+
14+
func SOCKS5SDialerFromURL(u *url.URL, next xproxy.Dialer) (xproxy.Dialer, error) {
15+
var (
16+
tlsConfig *tls.Config
17+
tlsFactory func(net.Conn, *tls.Config) net.Conn
18+
err error
19+
)
20+
tlsConfig, err = tlsutil.TLSConfigFromURL(u)
21+
if err != nil {
22+
return nil, fmt.Errorf("TLS configuration failed: %w", err)
23+
}
24+
tlsFactory, err = tlsutil.TLSFactoryFromURL(u)
25+
if err != nil {
26+
return nil, fmt.Errorf("TLS configuration failed: %w", err)
27+
}
28+
u.Scheme = "socks5"
29+
u.RawQuery = ""
30+
return xproxy.FromURL(u, NewTLSWrappingDialer(tlsConfig, tlsFactory, MaybeWrapWithContextDialer(next)))
31+
}
32+
33+
type TLSWrappingDialer struct {
34+
next Dialer
35+
tlsConfig *tls.Config
36+
tlsFactory func(net.Conn, *tls.Config) net.Conn
37+
}
38+
39+
func NewTLSWrappingDialer(tlsConfig *tls.Config, tlsFactory func(net.Conn, *tls.Config) net.Conn, next Dialer) *TLSWrappingDialer {
40+
return &TLSWrappingDialer{
41+
next: next,
42+
tlsConfig: tlsConfig,
43+
tlsFactory: tlsFactory,
44+
}
45+
}
46+
47+
func (d *TLSWrappingDialer) Dial(network, address string) (net.Conn, error) {
48+
return d.DialContext(context.Background(), network, address)
49+
}
50+
51+
func (d *TLSWrappingDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
52+
conn, err := d.next.DialContext(ctx, network, address)
53+
if err != nil {
54+
return nil, err
55+
}
56+
return d.tlsFactory(conn, d.tlsConfig), nil
57+
}

0 commit comments

Comments
 (0)