Compare commits

...

12 Commits

Author SHA1 Message Date
Vladislav Yarmak
518606be42 retract all versions 2026-03-09 13:58:39 +02:00
Vladislav Yarmak
25958fd032 upd readme 2025-09-30 22:12:06 +03:00
Snawoot
822530392d Merge pull request #155 from Snawoot/resolver_fixes
Resolver fixes
2025-09-24 01:03:01 +03:00
Vladislav Yarmak
971530e7ce resolver fixes 2025-09-24 01:02:09 +03:00
Snawoot
aab7d5ea92 Merge pull request #154 from Snawoot/deps_upd
Dependencies update
2025-09-22 20:03:45 +03:00
Vladislav Yarmak
ad623ba3d7 go mod tidy 2025-09-22 19:59:18 +03:00
Vladislav Yarmak
b6a68eb534 dependencies update 2025-09-22 19:59:01 +03:00
Snawoot
c1b059be3f Merge pull request #153 from Snawoot/api_hide_SNI
Hide SNI for API calls as well
2025-09-22 19:55:34 +03:00
Vladislav Yarmak
b74e0fd35a hide SNI for API calls as well 2025-09-22 19:38:50 +03:00
Snawoot
32079d234d Merge pull request #151 from Snawoot/resolver_fix
Resolver fix
2025-08-16 03:30:20 +03:00
Vladislav Yarmak
2d5002cd20 resolver fix 2025-08-16 03:29:29 +03:00
Vladislav Yarmak
4055473a06 fix readme formatting 2025-08-13 17:46:01 +03:00
6 changed files with 92 additions and 69 deletions

View File

@@ -9,27 +9,6 @@ Application is capable to forward traffic via proxies in datacenters (flag `-pro
This alternative implementation ensures your internet connection is not shared with anyone else and everything is clean and safe.
---
:heart: :heart: :heart:
You can say thanks to the author by donations to these wallets:
- ETH: `0xB71250010e8beC90C5f9ddF408251eBA9dD7320e`
- BTC:
- Legacy: `1N89PRvG1CSsUk9sxKwBwudN6TjTPQ1N8a`
- Segwit: `bc1qc0hcyxc000qf0ketv4r44ld7dlgmmu73rtlntw`
---
## Mirrors
IPFS git mirror:
```
git clone https://ipfs.io/ipns/k51qzi5uqu5dkrgx0hozpy1tlggw5o0whtquyrjlc6pprhvbmczr6qtj4ocrv0 hola-proxy
```
## Features
* Cross-platform (Windows/Mac OS/Linux/Android (via shell)/\*BSD)
@@ -174,7 +153,7 @@ zagent248.hola.org,165.22.65.3,22222,22223,22224,22225,22226,digitalocean
| list-proxies | - | output proxy list and exit |
| proxy | String | sets base proxy to use for all dial-outs. Format: `<http\|https\|socks5\|socks5h>://[login:password@]host[:port]` Examples: `http://user:password@192.168.1.1:3128`, `socks5://10.0.0.1:1080` |
| proxy-type | String | proxy type (Datacenter: direct) (Residential: lum) (default "direct") |
| resolver | String | comma-separated list of DNS/DoH/DoT resolvers used to lookup domain names blocked by Hola. Supported schemes are: dns://, https://, tls://, tcp://. (default `https://1.1.1.3/dns-query,https://8.8.8.8/dns-query,https://dns.google/dns-query,https://security.cloudflare-dns.com/dns-query,https://fidelity.vm-0.com/q,https://wikimedia-dns.org/dns-query,https://dns.adguard-dns.com/dns-query,https://dns.quad9.net/dns-query,https://doh.cleanbrowsing.org/doh/adult-filter/`) |
| resolver | String | comma-separated list of DNS/DoH/DoT resolvers used to lookup domain names blocked by Hola. Supported schemes are: `dns://`, `https://`, `tls://`, `tcp://`. (default `https://1.1.1.3/dns-query,https://8.8.8.8/dns-query,https://dns.google/dns-query,https://security.cloudflare-dns.com/dns-query,https://fidelity.vm-0.com/q,https://wikimedia-dns.org/dns-query,https://dns.adguard-dns.com/dns-query,https://dns.quad9.net/dns-query,https://doh.cleanbrowsing.org/doh/adult-filter/`) |
| rotate | Duration | rotate user ID once per given period (default 48h0m0s) |
| timeout | Duration | timeout for network operations (default 35s) |
| user-agent | String | value of User-Agent header in requests. Default: User-Agent of latest stable Chrome for Windows |

8
go.mod
View File

@@ -9,13 +9,15 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/ncruces/go-dns v1.2.7
github.com/refraction-networking/utls v1.8.0
golang.org/x/net v0.43.0
golang.org/x/net v0.44.0
)
require (
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/sys v0.36.0 // indirect
)
retract [v0.0.0-0, v1.18.3]

12
go.sum
View File

@@ -19,9 +19,9 @@ github.com/refraction-networking/utls v1.8.0 h1:L38krhiTAyj9EeiQQa2sg+hYb4qwLCqd
github.com/refraction-networking/utls v1.8.0/go.mod h1:jkSOEkLqn+S/jtpEHPOsVv/4V4EVnelwbMQl4vCWXAM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

View File

@@ -398,6 +398,12 @@ func UpdateHolaTLSConfig(config *tls.Config) {
tlsConfig = config
}
var hideSNI bool
func SetHideSNI(hide bool) {
hideSNI = hide
}
// Returns default http client with a proxy override
func httpClientWithProxy(agent *FallbackAgent) *http.Client {
t := &http.Transport{
@@ -428,7 +434,23 @@ func httpClientWithProxy(agent *FallbackAgent) *http.Client {
if tlsConfig != nil {
cfg = *tlsConfig
}
cfg.ServerName = host
if !hideSNI {
cfg.ServerName = host
} else {
cfg.InsecureSkipVerify = true
cfg.VerifyConnection = func(cs tls.ConnectionState) error {
opts := x509.VerifyOptions{
DNSName: host,
Intermediates: x509.NewCertPool(),
Roots: cfg.RootCAs,
}
for _, cert := range cs.PeerCertificates[1:] {
opts.Intermediates.AddCert(cert)
}
_, err := cs.PeerCertificates[0].Verify(opts)
return err
}
}
tlsConn := tls.UClient(conn, &cfg, tls.HelloAndroid_11_OkHttp)
if err := tlsConn.HandshakeContext(ctx); err != nil {
conn.Close()

View File

@@ -210,6 +210,7 @@ func run() int {
RootCAs: caPool,
})
}
SetHideSNI(args.hideSNI)
proxyFromURLWrapper := func(u *url.URL, next xproxy.Dialer) (xproxy.Dialer, error) {
cdialer, ok := next.(ContextDialer)

View File

@@ -14,34 +14,51 @@ import (
)
func FromURL(u string) (*net.Resolver, error) {
begin:
parsed, err := url.Parse(u)
if err != nil {
return nil, err
}
switch strings.ToLower(parsed.Scheme) {
case "", "dns":
host := parsed.Hostname()
port := parsed.Port()
host := parsed.Hostname()
port := parsed.Port()
switch scheme := strings.ToLower(parsed.Scheme); scheme {
case "":
switch {
case strings.HasPrefix(u, "//"):
u = "dns:" + u
default:
u = "dns://" + u
}
goto begin
case "udp", "dns":
if port == "" {
port = "53"
}
return NewPlainResolver(net.JoinHostPort(host, port)), nil
case "tcp":
host := parsed.Hostname()
port := parsed.Port()
if port == "" {
port = "53"
}
return NewTCPResolver(net.JoinHostPort(host, port)), nil
case "http", "https":
return dns.NewDoHResolver(u)
case "tls":
host := parsed.Hostname()
port := parsed.Port()
case "http", "https", "doh":
if port == "" {
if scheme == "http" {
port = "80"
} else {
port = "443"
}
}
if scheme == "doh" {
parsed.Scheme = "https"
u = parsed.String()
}
return dns.NewDoHResolver(u, dns.DoHAddresses(net.JoinHostPort(host, port)))
case "tls", "dot":
if port == "" {
port = "853"
}
return dns.NewDoTResolver(net.JoinHostPort(host, port))
hp := net.JoinHostPort(host, port)
return dns.NewDoTResolver(hp, dns.DoTAddresses(hp))
default:
return nil, errors.New("not implemented")
}
@@ -55,12 +72,7 @@ type FastResolver struct {
upstreams []LookupNetIPer
}
type lookupReply struct {
addrs []netip.Addr
err error
}
func FastResolverFromURLs(urls ...string) (*FastResolver, error) {
func FastResolverFromURLs(urls ...string) (LookupNetIPer, error) {
resolvers := make([]LookupNetIPer, 0, len(urls))
for i, u := range urls {
res, err := FromURL(u)
@@ -69,6 +81,9 @@ func FastResolverFromURLs(urls ...string) (*FastResolver, error) {
}
resolvers = append(resolvers, res)
}
if len(resolvers) == 1 {
return resolvers[0], nil
}
return NewFastResolver(resolvers...), nil
}
@@ -80,34 +95,38 @@ func NewFastResolver(resolvers ...LookupNetIPer) *FastResolver {
func (r FastResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
ctx, cl := context.WithCancel(ctx)
drain := make(chan lookupReply, len(r.upstreams))
defer cl()
errors := make(chan error)
success := make(chan []netip.Addr)
for _, res := range r.upstreams {
go func(res LookupNetIPer) {
addrs, err := res.LookupNetIP(ctx, network, host)
drain <- lookupReply{addrs, err}
if err == nil {
select {
case success <- addrs:
case <-ctx.Done():
}
} else {
select {
case errors <- err:
case <-ctx.Done():
}
}
}(res)
}
i := 0
var resAddrs []netip.Addr
var resErr error
for ; i < len(r.upstreams); i++ {
pair := <-drain
if pair.err != nil {
resErr = multierror.Append(resErr, pair.err)
} else {
cl()
resAddrs = pair.addrs
resErr = nil
break
for _ = range r.upstreams {
select {
case <-ctx.Done():
return nil, ctx.Err()
case resAddrs := <-success:
return resAddrs, nil
case err := <-errors:
resErr = multierror.Append(resErr, err)
}
}
go func() {
for i = i + 1; i < len(r.upstreams); i++ {
<-drain
}
}()
return resAddrs, resErr
return nil, resErr
}
func NewPlainResolver(addr string) *net.Resolver {