mirror of
https://github.com/Snawoot/hola-proxy.git
synced 2026-04-04 17:38:12 +00:00
Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6f3871db1 | ||
|
|
b0435c3929 | ||
|
|
9d49f99cd3 | ||
|
|
51a9f257e7 | ||
|
|
5d3aa90a0f | ||
|
|
f062f9310c | ||
|
|
c019e84bbe | ||
|
|
65fdac74bd | ||
|
|
15fea1bf78 | ||
|
|
1a526a6381 | ||
|
|
fcfc6212ee | ||
|
|
cf49895825 | ||
|
|
becf27500b | ||
|
|
b1671d4d19 | ||
|
|
2c22c3af35 | ||
|
|
55f8125044 | ||
|
|
c3691b208b | ||
|
|
bfe19d9a85 | ||
|
|
3be0ad42f6 | ||
|
|
5e00cfb499 | ||
|
|
ba52035f5a | ||
|
|
091bc05a30 | ||
|
|
ebba7bca60 | ||
|
|
99132778c9 | ||
|
|
02bfdea676 | ||
|
|
e2dbbb9ec3 | ||
|
|
81e06c2828 | ||
|
|
440bad2860 | ||
|
|
f9d2e8985f | ||
|
|
7775e5f54e | ||
|
|
8479a86de6 | ||
|
|
3fccf32f7b | ||
|
|
332db446b4 | ||
|
|
8020fba8f3 | ||
|
|
f659f26bad | ||
|
|
ce356ce015 | ||
|
|
e8cd8ba0a8 | ||
|
|
191b1e7f51 | ||
|
|
6aa4fc334a | ||
|
|
124212a5e2 | ||
|
|
b120cb5462 | ||
|
|
972771b6af | ||
|
|
49b6ba9147 | ||
|
|
1e4c6684cc | ||
|
|
569bf1d39f | ||
|
|
92fdd4fb72 | ||
|
|
d4d37bb354 | ||
|
|
458fdf2d50 | ||
|
|
010ca056b3 | ||
|
|
e28c186971 | ||
|
|
5de4f1a616 | ||
|
|
1e13bae8b3 | ||
|
|
0811bb6fa6 | ||
|
|
1bfef0287f | ||
|
|
778339ddb2 | ||
|
|
c8350cc731 | ||
|
|
5b0e1e33c6 | ||
|
|
7bc4cc57de | ||
|
|
3105cf04f1 | ||
|
|
69b582090c | ||
|
|
9a91ec21ab | ||
|
|
3d14c627bf | ||
|
|
f0ee8a0d23 | ||
|
|
ca7c13f86a | ||
|
|
c5ddc5a5f1 | ||
|
|
8680aaf470 | ||
|
|
36822781d1 | ||
|
|
1d81ddd083 | ||
|
|
37401b39f8 | ||
|
|
d4e86ad2b4 | ||
|
|
fc9e85dba0 | ||
|
|
bbd2ea4048 | ||
|
|
6d65d22a84 | ||
|
|
2be66ef0d1 | ||
|
|
7ed761c0c3 | ||
|
|
fa21f85414 | ||
|
|
6940495771 | ||
|
|
7864816161 | ||
|
|
838463b771 | ||
|
|
2ff8391919 | ||
|
|
54fa9a4831 | ||
|
|
679ac56806 | ||
|
|
47721e844a | ||
|
|
fa8ea7356f | ||
|
|
c54c588b17 | ||
|
|
ad9ecc60ff | ||
|
|
527ab8a74a | ||
|
|
613c7778da | ||
|
|
c0d5a118be | ||
|
|
652a7ab662 | ||
|
|
1b1ea6db4f | ||
|
|
32fc635666 | ||
|
|
71de713376 | ||
|
|
39a9ba0bd3 | ||
|
|
77bfe7907b | ||
|
|
ba18da688b | ||
|
|
4af6fdf9ef | ||
|
|
b841678722 | ||
|
|
5c7b550983 | ||
|
|
203ed79b2b | ||
|
|
ce330f66a7 | ||
|
|
e82da1cb7b | ||
|
|
257fbe70a7 | ||
|
|
c9e2a2d5df | ||
|
|
98784aada9 | ||
|
|
518098ac2b | ||
|
|
60eee4ad84 | ||
|
|
223105b010 | ||
|
|
b93ba7718c | ||
|
|
8660c52f26 | ||
|
|
a003e75cb4 | ||
|
|
173fbd5d98 | ||
|
|
4d182dedd9 | ||
|
|
6501950752 | ||
|
|
9118ac4fae |
42
.github/workflows/build.yml
vendored
Normal file
42
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 'stable'
|
||||
-
|
||||
name: Read tag
|
||||
id: tag
|
||||
run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
-
|
||||
name: Build
|
||||
run: >-
|
||||
make -j $(nproc) allplus
|
||||
NDK_CC_ARM64="$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang"
|
||||
NDK_CC_ARM="$ANDROID_NDK/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang"
|
||||
VERSION=${{steps.tag.outputs.tag}}
|
||||
-
|
||||
name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: bin/*
|
||||
fail_on_unmatched_files: true
|
||||
generate_release_notes: true
|
||||
38
.github/workflows/docker-ci.yml
vendored
38
.github/workflows/docker-ci.yml
vendored
@@ -2,10 +2,8 @@ name: docker-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
release:
|
||||
types: [published]
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
@@ -13,7 +11,7 @@ jobs:
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
-
|
||||
@@ -26,33 +24,39 @@ jobs:
|
||||
commit-ish: 'HEAD'
|
||||
skip-unshallow: 'true'
|
||||
abbrev: 7
|
||||
-
|
||||
name: Determine image tag type
|
||||
uses: haya14busa/action-cond@v1
|
||||
id: imgtag
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
cond: ${{ github.event_name == 'release' }}
|
||||
if_true: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.event.release.tag_name }},${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest
|
||||
if_false: ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
|
||||
push: true
|
||||
tags: ${{ steps.imgtag.outputs.value }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
build-args: 'GIT_DESC=${{steps.tagger.outputs.tag}}'
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
FROM golang:1.19 AS build
|
||||
FROM --platform=$BUILDPLATFORM golang:1 AS build
|
||||
|
||||
ARG GIT_DESC=undefined
|
||||
|
||||
WORKDIR /go/src/github.com/Snawoot/hola-proxy
|
||||
COPY . .
|
||||
RUN CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w -extldflags "-static" -X main.version='"$GIT_DESC"
|
||||
ARG TARGETOS TARGETARCH
|
||||
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w -extldflags "-static" -X main.version='"$GIT_DESC"
|
||||
ADD https://curl.haxx.se/ca/cacert.pem /certs.crt
|
||||
RUN chmod 0644 /certs.crt
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
PROGNAME = hola-proxy
|
||||
OUTSUFFIX = bin/$(PROGNAME)
|
||||
VERSION := $(shell git describe)
|
||||
BUILDOPTS = -a -tags netgo
|
||||
BUILDOPTS = -a -tags netgo -trimpath -asmflags -trimpath
|
||||
LDFLAGS = -ldflags '-s -w -extldflags "-static" -X main.version=$(VERSION)'
|
||||
LDFLAGS_NATIVE = -ldflags '-s -w -X main.version=$(VERSION)'
|
||||
|
||||
|
||||
@@ -162,17 +162,23 @@ zagent248.hola.org,165.22.65.3,22222,22223,22224,22225,22226,digitalocean
|
||||
| cafile | String | use custom CA certificate bundle file |
|
||||
| country | String | desired proxy location (default "us") |
|
||||
| dont-use-trial | - | use regular ports instead of trial ports |
|
||||
| ext-ver | String | extension version to mimic in requests. Can be obtained from https://chrome.google.com/webstore/detail/hola-vpn-the-website-unbl/gkojfkhlekighikafcpjkiklfbnlmeio (default "999.999.999") |
|
||||
| force-port-field | Number | force specific port field/num (example 24232 or lum) |
|
||||
| hide-SNI | Boolean | hide SNI in TLS sessions with proxy server (default true) |
|
||||
| init-retries | Number | number of attempts for initialization steps, zero for unlimited retry |
|
||||
| init-retry-interval | Duration | delay between initialization retries (default 5s) |
|
||||
| limit | Unsigned Integer (Number) | amount of proxies in retrieved list (default 3) |
|
||||
| list-countries | String | list available countries and exit |
|
||||
| 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 | DNS/DoH/DoT resolver to workaround Hola blocked hosts. See https://github.com/ameshkov/dnslookup/ for upstream DNS URL format. (default "https://cloudflare-dns.com/dns-query") |
|
||||
| rotate | Duration | rotate user ID once per given period (default 1h0m0s) |
|
||||
| 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 |
|
||||
| verbosity | Number | logging verbosity (10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical) (default 20) |
|
||||
|
||||
## See also
|
||||
|
||||
* [Project wiki](https://github.com/Snawoot/hola-proxy/wiki)
|
||||
* [Community in Telegram](https://t.me/alternative_proxy)
|
||||
|
||||
63
chromever.go
Normal file
63
chromever.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type chromeVerResponse struct {
|
||||
Versions [1]struct {
|
||||
Version string `json:"version"`
|
||||
} `json:"versions"`
|
||||
}
|
||||
|
||||
const chromeVerURL = "https://versionhistory.googleapis.com/v1/chrome/platforms/win/channels/stable/versions?alt=json&orderBy=version+desc&pageSize=1&prettyPrint=false"
|
||||
|
||||
func GetChromeVer(ctx context.Context, dialer ContextDialer) (string, error) {
|
||||
if dialer == nil {
|
||||
dialer = &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: dialer.DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
defer transport.CloseIdleConnections()
|
||||
httpClient := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", chromeVerURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("chrome browser version request construction failed: %w", err)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("chrome browser version request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("chrome browser version request failed: bad status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(resp.Body)
|
||||
var chromeVerResp chromeVerResponse
|
||||
if err := dec.Decode(&chromeVerResp); err != nil {
|
||||
return "", fmt.Errorf("unable to decode chrome browser version response: %w", err)
|
||||
}
|
||||
|
||||
return chromeVerResp.Versions[0].Version, nil
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
const DEFAULT_LIST_LIMIT = 3
|
||||
|
||||
func CredService(interval, timeout time.Duration,
|
||||
extVer string,
|
||||
country string,
|
||||
proxytype string,
|
||||
logger *CondLogger,
|
||||
@@ -19,14 +20,13 @@ func CredService(interval, timeout time.Duration,
|
||||
var mux sync.Mutex
|
||||
var auth_header, user_uuid string
|
||||
auth = func() (res string) {
|
||||
(&mux).Lock()
|
||||
res = auth_header
|
||||
(&mux).Unlock()
|
||||
return
|
||||
mux.Lock()
|
||||
defer mux.Unlock()
|
||||
return auth_header
|
||||
}
|
||||
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tunnels, user_uuid, err = Tunnels(ctx, logger, client, country, proxytype,
|
||||
tunnels, user_uuid, err = Tunnels(ctx, logger, client, extVer, country, proxytype,
|
||||
DEFAULT_LIST_LIMIT, timeout, backoffInitial, backoffDeadline)
|
||||
if err != nil {
|
||||
logger.Error("Configuration bootstrap error: %v. Retrying with the fallback mechanism...", err)
|
||||
@@ -43,8 +43,10 @@ func CredService(interval, timeout time.Duration,
|
||||
logger.Critical("All attempts failed.")
|
||||
return
|
||||
}
|
||||
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
|
||||
tunnels.AgentKey)
|
||||
auth_header = basic_auth_header(TemplateLogin(user_uuid), tunnels.AgentKey)
|
||||
if interval <= 0 {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
var (
|
||||
err error
|
||||
@@ -57,7 +59,7 @@ func CredService(interval, timeout time.Duration,
|
||||
<-ticker.C
|
||||
logger.Info("Rotating credentials...")
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tuns, user_uuid, err = Tunnels(ctx, logger, client, country, proxytype,
|
||||
tuns, user_uuid, err = Tunnels(ctx, logger, client, extVer, country, proxytype,
|
||||
DEFAULT_LIST_LIMIT, timeout, backoffInitial, backoffDeadline)
|
||||
if err != nil {
|
||||
logger.Error("Credential rotation error: %v. Retrying with the fallback mechanism...", err)
|
||||
@@ -74,10 +76,9 @@ func CredService(interval, timeout time.Duration,
|
||||
logger.Critical("All rotation attempts failed.")
|
||||
continue
|
||||
}
|
||||
(&mux).Lock()
|
||||
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
|
||||
tuns.AgentKey)
|
||||
(&mux).Unlock()
|
||||
mux.Lock()
|
||||
auth_header = basic_auth_header(TemplateLogin(user_uuid), tuns.AgentKey)
|
||||
mux.Unlock()
|
||||
logger.Info("Credentials rotated successfully.")
|
||||
}
|
||||
}()
|
||||
|
||||
107
extver.go
Normal file
107
extver.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultProdVersion = "113.0"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoVerData = errors.New("no version data returned")
|
||||
)
|
||||
|
||||
type StoreExtUpdateResponse struct {
|
||||
XMLName xml.Name `xml:"gupdate"`
|
||||
App *struct {
|
||||
AppID string `xml:"appid,attr"`
|
||||
Status string `xml:"status,attr"`
|
||||
UpdateCheck *struct {
|
||||
Version string `xml:"version,attr"`
|
||||
Status string `xml:"status,attr"`
|
||||
} `xml:"updatecheck"`
|
||||
} `xml:"app"`
|
||||
}
|
||||
|
||||
func GetExtVer(ctx context.Context,
|
||||
prodVersion *string,
|
||||
id string,
|
||||
dialer ContextDialer,
|
||||
) (string, error) {
|
||||
if prodVersion == nil {
|
||||
prodVersion = &defaultProdVersion
|
||||
}
|
||||
if dialer == nil {
|
||||
dialer = &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
DialContext: dialer.DialContext,
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
defer transport.CloseIdleConnections()
|
||||
httpClient := &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
|
||||
reqURL := (&url.URL{
|
||||
Scheme: "https",
|
||||
Host: "clients2.google.com",
|
||||
Path: "/service/update2/crx",
|
||||
RawQuery: url.Values{
|
||||
"prodversion": {*prodVersion},
|
||||
"acceptformat": {"crx2,crx3"},
|
||||
"x": {url.Values{
|
||||
"id": {id},
|
||||
"uc": {""},
|
||||
}.Encode()},
|
||||
}.Encode(),
|
||||
}).String()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("chrome web store request construction failed: %w", err)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("chrome web store request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return "", fmt.Errorf("chrome web store: bad status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
reader := io.LimitReader(resp.Body, 64*1024)
|
||||
var respData *StoreExtUpdateResponse
|
||||
|
||||
dec := xml.NewDecoder(reader)
|
||||
err = dec.Decode(&respData)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unmarshaling of chrome web store response failed: %w", err)
|
||||
}
|
||||
|
||||
if respData != nil && respData.App != nil &&
|
||||
respData.App.UpdateCheck != nil && respData.App.UpdateCheck.Version != "" {
|
||||
return respData.App.UpdateCheck.Version, nil
|
||||
}
|
||||
|
||||
return "", ErrNoVerData
|
||||
}
|
||||
50
go.mod
50
go.mod
@@ -1,34 +1,36 @@
|
||||
module github.com/Snawoot/hola-proxy
|
||||
|
||||
go 1.20
|
||||
go 1.24.2
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1
|
||||
github.com/AdguardTeam/dnsproxy v0.75.3
|
||||
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e
|
||||
github.com/cenkalti/backoff/v4 v4.2.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/miekg/dns v1.1.53
|
||||
golang.org/x/net v0.9.0
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/miekg/dns v1.1.65
|
||||
github.com/refraction-networking/utls v1.7.1
|
||||
golang.org/x/net v0.39.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/golibs v0.13.0 // indirect
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 // indirect
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6 // indirect
|
||||
github.com/AdguardTeam/golibs v0.32.8 // indirect
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0 // indirect
|
||||
github.com/ameshkov/dnsstamps v1.0.3 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.7.0 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1 // indirect
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1 // indirect
|
||||
github.com/quic-go/quic-go v0.33.0 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6 // indirect
|
||||
golang.org/x/mod v0.8.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.6.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.51.0 // indirect
|
||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
||||
go.uber.org/mock v0.5.1 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
)
|
||||
|
||||
146
go.sum
146
go.sum
@@ -1,90 +1,74 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1 h1:JpStBK05uCgA3ldleaNLRmIwE9V7vRg7/kVJQSdnQYg=
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1/go.mod h1:Y7g7jRTd/u7+KJ/QvnGI2PCE8vnisp6EsW47/Sz0DZw=
|
||||
github.com/AdguardTeam/golibs v0.13.0 h1:hVBeNQXT/BgcjKz/4FMpFGvEYqXiXDJG+b5XpGCUOLk=
|
||||
github.com/AdguardTeam/golibs v0.13.0/go.mod h1:rIglKDHdLvFT1UbhumBLHO9S4cvWS9MEyT1njommI/Y=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6 h1:rE7AFbPWebq7me7RVS66Cipd1m7ef1yf2+C8QzjQXXE=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.2.6/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
|
||||
github.com/AdguardTeam/dnsproxy v0.75.3 h1:pxlMNO+cP1A3px40PY/old6SAE82pkdLPUA2P3KY8u0=
|
||||
github.com/AdguardTeam/dnsproxy v0.75.3/go.mod h1:50OyTHao+uQzUJiXay08hgfvWQ3o2Q2WV99W8u8ypDE=
|
||||
github.com/AdguardTeam/golibs v0.32.8 h1:O3mc3kYcPkW3kbmd+gqzFNgUka13a+iBgFLThwOYSQE=
|
||||
github.com/AdguardTeam/golibs v0.32.8/go.mod h1:McV1QFFlKLElKa306V4OL/T2kr7564PhsayfvTWYBVs=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0 h1:if6ZG2cuQmcP2TwSY+D0+8+xbPfoatufGlOQTMNkI9o=
|
||||
github.com/ameshkov/dnscrypt/v2 v2.4.0/go.mod h1:WpEFV2uhebXb8Jhes/5/fSdpmhGV8TL22RDaeWwV6hI=
|
||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e h1:V9a67dfYqPLAvzk5hMQOXYJlZ4SLIXgyKIE+ZiHzgGQ=
|
||||
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b h1:8htHrh2bw9c7Idkb7YNac+ZpTqLMjRpI+FWu51ltaQc=
|
||||
github.com/google/pprof v0.0.0-20230111200839-76d1ae5aea2b/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
|
||||
github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
|
||||
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4=
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU=
|
||||
github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
|
||||
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1 h1:aJcKNMkH5ASEJB9FXNeZCyTEIHU1J7MmHyz1Q1TSG1A=
|
||||
github.com/quic-go/qtls-go1-19 v0.2.1/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1 h1:KbChDlg82d3IHqaj2bn6GfKRj84Per2VGf5XV3wSwQk=
|
||||
github.com/quic-go/qtls-go1-20 v0.1.1/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
|
||||
github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
|
||||
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6 h1:3p+wVC0x0TCIPgd3LCQlpgVlEtjziEC5v42w7+B8t8M=
|
||||
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
|
||||
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
|
||||
github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0=
|
||||
github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/mock v0.5.1 h1:ASgazW/qBmR+A32MYFDB6E2POoTgOwT509VP0CT/fjs=
|
||||
go.uber.org/mock v0.5.1/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
75
holaapi.go
75
holaapi.go
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
@@ -16,30 +15,45 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/campoy/unique"
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
"github.com/google/uuid"
|
||||
tls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36"
|
||||
const EXT_VER = "1.210.946"
|
||||
const EXT_BROWSER = "chrome"
|
||||
const PRODUCT = "cws"
|
||||
const CCGI_URL = "https://client.hola.org/client_cgi/"
|
||||
const VPN_COUNTRIES_URL = CCGI_URL + "vpn_countries.json"
|
||||
const BG_INIT_URL = CCGI_URL + "background_init"
|
||||
const ZGETTUNNELS_URL = CCGI_URL + "zgettunnels"
|
||||
const LOGIN_PREFIX = "user-uuid-"
|
||||
const FALLBACK_CONF_URL = "https://www.dropbox.com/s/jemizcvpmf2qb9v/cloud_failover.conf?dl=1"
|
||||
const AGENT_SUFFIX = ".hola.org"
|
||||
|
||||
var FALLBACK_CONF_URLS = []string{
|
||||
"https://www.dropbox.com/s/jemizcvpmf2qb9v/cloud_failover.conf?dl=1",
|
||||
"https://vdkd6nz8qr.s3.amazonaws.com/cloud_failover.conf",
|
||||
}
|
||||
|
||||
var LOGIN_TEMPLATE = template.Must(template.New("LOGIN_TEMPLATE").Parse("user-uuid-{{.uuid}}-is_prem-{{.prem}}"))
|
||||
var TemporaryBanError = errors.New("temporary ban detected")
|
||||
var PermanentBanError = errors.New("permanent ban detected")
|
||||
var EmptyResponseError = errors.New("empty response")
|
||||
|
||||
var userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
|
||||
|
||||
func SetUserAgent(ua string) {
|
||||
userAgent = ua
|
||||
}
|
||||
|
||||
func GetUserAgent() string {
|
||||
return userAgent
|
||||
}
|
||||
|
||||
type CountryList []string
|
||||
|
||||
type BgInitResponse struct {
|
||||
@@ -157,7 +171,7 @@ func do_req(ctx context.Context, client *http.Client, method, url string, query,
|
||||
if query != nil {
|
||||
req.URL.RawQuery = query.Encode()
|
||||
}
|
||||
req.Header.Set("User-Agent", USER_AGENT)
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -193,10 +207,10 @@ func VPNCountries(ctx context.Context, client *http.Client) (res CountryList, er
|
||||
return
|
||||
}
|
||||
|
||||
func background_init(ctx context.Context, client *http.Client, user_uuid string) (res BgInitResponse, reterr error) {
|
||||
func background_init(ctx context.Context, client *http.Client, extVer, user_uuid string) (res BgInitResponse, reterr error) {
|
||||
post_data := make(url.Values)
|
||||
post_data.Add("login", "1")
|
||||
post_data.Add("ver", EXT_VER)
|
||||
post_data.Add("ver", extVer)
|
||||
qs := make(url.Values)
|
||||
qs.Add("uuid", user_uuid)
|
||||
resp, err := do_req(ctx, client, "POST", BG_INIT_URL, qs, post_data)
|
||||
@@ -220,6 +234,7 @@ func zgettunnels(ctx context.Context,
|
||||
client *http.Client,
|
||||
user_uuid string,
|
||||
session_key int64,
|
||||
extVer string,
|
||||
country string,
|
||||
proxy_type string,
|
||||
limit uint) (res *ZGetTunnelsResponse, reterr error) {
|
||||
@@ -239,7 +254,7 @@ func zgettunnels(ctx context.Context,
|
||||
}
|
||||
params.Add("limit", strconv.FormatInt(int64(limit), 10))
|
||||
params.Add("ping_id", strconv.FormatFloat(rand.New(RandomSource).Float64(), 'f', -1, 64))
|
||||
params.Add("ext_ver", EXT_VER)
|
||||
params.Add("ext_ver", extVer)
|
||||
params.Add("browser", EXT_BROWSER)
|
||||
params.Add("product", PRODUCT)
|
||||
params.Add("uuid", user_uuid)
|
||||
@@ -263,7 +278,8 @@ func zgettunnels(ctx context.Context,
|
||||
|
||||
func fetchFallbackConfig(ctx context.Context) (*FallbackConfig, error) {
|
||||
client := httpClientWithProxy(nil)
|
||||
confRaw, err := do_req(ctx, client, "", FALLBACK_CONF_URL, nil, nil)
|
||||
fallbackConfURL := FALLBACK_CONF_URLS[rand.New(RandomSource).Intn(len(FALLBACK_CONF_URLS))]
|
||||
confRaw, err := do_req(ctx, client, "", fallbackConfURL, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -325,6 +341,7 @@ func GetFallbackProxies(ctx context.Context) (*FallbackConfig, error) {
|
||||
func Tunnels(ctx context.Context,
|
||||
logger *CondLogger,
|
||||
client *http.Client,
|
||||
extVer string,
|
||||
country string,
|
||||
proxy_type string,
|
||||
limit uint,
|
||||
@@ -336,7 +353,7 @@ func Tunnels(ctx context.Context,
|
||||
user_uuid = hex.EncodeToString(u[:])
|
||||
ctx1, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
initres, err := background_init(ctx1, client, user_uuid)
|
||||
initres, err := background_init(ctx1, client, extVer, user_uuid)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
return
|
||||
@@ -354,7 +371,7 @@ func Tunnels(ctx context.Context,
|
||||
err = backoff.RetryNotify(func() error {
|
||||
ctx1, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
res, reterr = zgettunnels(ctx1, client, user_uuid, initres.Key, country, proxy_type, limit)
|
||||
res, reterr = zgettunnels(ctx1, client, user_uuid, initres.Key, extVer, country, proxy_type, limit)
|
||||
return reterr
|
||||
}, bo, func(err error, dur time.Duration) {
|
||||
logger.Info("zgettunnels error: %v; will retry after %v", err, dur.Truncate(time.Millisecond))
|
||||
@@ -387,9 +404,7 @@ func httpClientWithProxy(agent *FallbackAgent) *http.Client {
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
var dialer ContextDialer = baseDialer
|
||||
var rootCAs *x509.CertPool
|
||||
@@ -397,9 +412,30 @@ func httpClientWithProxy(agent *FallbackAgent) *http.Client {
|
||||
rootCAs = tlsConfig.RootCAs
|
||||
}
|
||||
if agent != nil {
|
||||
dialer = NewProxyDialer(agent.NetAddr(), agent.Hostname(), rootCAs, nil, dialer)
|
||||
dialer = NewProxyDialer(agent.NetAddr(), agent.Hostname(), rootCAs, nil, true, dialer)
|
||||
}
|
||||
t.DialContext = dialer.DialContext
|
||||
t.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
host, _, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("hostname extraction error: %w", err)
|
||||
}
|
||||
conn, err := dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't prepare underlying connection for TLS session: %w", err)
|
||||
}
|
||||
var cfg tls.Config
|
||||
if tlsConfig != nil {
|
||||
cfg = *tlsConfig
|
||||
}
|
||||
cfg.ServerName = host
|
||||
tlsConn := tls.UClient(conn, &cfg, tls.HelloAndroid_11_OkHttp)
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
conn.Close()
|
||||
return nil, fmt.Errorf("UClient handshake failed: %w", err)
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: t,
|
||||
}
|
||||
@@ -431,3 +467,12 @@ func EnsureTransaction(ctx context.Context, getFBTimeout time.Duration, txn func
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func TemplateLogin(user_uuid string) string {
|
||||
var b strings.Builder
|
||||
LOGIN_TEMPLATE.Execute(&b, map[string]string{
|
||||
"uuid": user_uuid,
|
||||
"prem": "0",
|
||||
})
|
||||
return b.String()
|
||||
}
|
||||
|
||||
126
main.go
126
main.go
@@ -1,7 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"flag"
|
||||
@@ -12,11 +12,17 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
tls "github.com/refraction-networking/utls"
|
||||
xproxy "golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
const (
|
||||
HolaExtStoreID = "gkojfkhlekighikafcpjkiklfbnlmeio"
|
||||
)
|
||||
|
||||
var (
|
||||
PROTOCOL_WHITELIST map[string]bool
|
||||
version = "undefined"
|
||||
@@ -42,6 +48,7 @@ func arg_fail(msg string) {
|
||||
}
|
||||
|
||||
type CLIArgs struct {
|
||||
extVer string
|
||||
country string
|
||||
list_countries, list_proxies, use_trial bool
|
||||
limit uint
|
||||
@@ -58,10 +65,16 @@ type CLIArgs struct {
|
||||
maxPause time.Duration
|
||||
backoffInitial time.Duration
|
||||
backoffDeadline time.Duration
|
||||
initRetries int
|
||||
initRetryInterval time.Duration
|
||||
hideSNI bool
|
||||
userAgent *string
|
||||
}
|
||||
|
||||
func parse_args() CLIArgs {
|
||||
var args CLIArgs
|
||||
flag.StringVar(&args.extVer, "ext-ver", "", "extension version to mimic in requests. "+
|
||||
"Can be obtained from https://chrome.google.com/webstore/detail/hola-vpn-the-website-unbl/gkojfkhlekighikafcpjkiklfbnlmeio")
|
||||
flag.StringVar(&args.force_port_field, "force-port-field", "", "force specific port field/num (example 24232 or lum)") // would be nice to not show in help page
|
||||
flag.StringVar(&args.country, "country", "us", "desired proxy location")
|
||||
flag.BoolVar(&args.list_countries, "list-countries", false, "list available countries and exit")
|
||||
@@ -70,10 +83,12 @@ func parse_args() CLIArgs {
|
||||
flag.StringVar(&args.bind_address, "bind-address", "127.0.0.1:8080", "HTTP proxy listen address")
|
||||
flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity "+
|
||||
"(10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical)")
|
||||
flag.DurationVar(&args.timeout, "timeout", 10*time.Second, "timeout for network operations")
|
||||
flag.DurationVar(&args.rotate, "rotate", 1*time.Hour, "rotate user ID once per given period")
|
||||
flag.DurationVar(&args.timeout, "timeout", 35*time.Second, "timeout for network operations")
|
||||
flag.DurationVar(&args.rotate, "rotate", 48*time.Hour, "rotate user ID once per given period")
|
||||
flag.DurationVar(&args.backoffInitial, "backoff-initial", 3*time.Second, "initial average backoff delay for zgettunnels (randomized by +/-50%)")
|
||||
flag.DurationVar(&args.backoffDeadline, "backoff-deadline", 5*time.Minute, "total duration of zgettunnels method attempts")
|
||||
flag.IntVar(&args.initRetries, "init-retries", 0, "number of attempts for initialization steps, zero for unlimited retry")
|
||||
flag.DurationVar(&args.initRetryInterval, "init-retry-interval", 5*time.Second, "delay between initialization retries")
|
||||
flag.StringVar(&args.proxy_type, "proxy-type", "direct", "proxy type: direct or lum") // or skip but not mentioned
|
||||
// skip would be used something like this: `./bin/hola-proxy -proxy-type skip -force-port-field 24232 -country ua.peer` for debugging
|
||||
flag.StringVar(&args.resolver, "resolver", "https://cloudflare-dns.com/dns-query",
|
||||
@@ -85,6 +100,13 @@ func parse_args() CLIArgs {
|
||||
"Format: <http|https|socks5|socks5h>://[login:password@]host[:port] "+
|
||||
"Examples: http://user:password@192.168.1.1:3128, socks5://10.0.0.1:1080")
|
||||
flag.StringVar(&args.caFile, "cafile", "", "use custom CA certificate bundle file")
|
||||
flag.Func("user-agent",
|
||||
"value of User-Agent header in requests. Default: User-Agent of latest stable Chrome for Windows",
|
||||
func(s string) error {
|
||||
args.userAgent = &s
|
||||
return nil
|
||||
})
|
||||
flag.BoolVar(&args.hideSNI, "hide-SNI", true, "hide SNI in TLS sessions with proxy server")
|
||||
flag.Parse()
|
||||
if args.country == "" {
|
||||
arg_fail("Country can't be empty string.")
|
||||
@@ -166,16 +188,64 @@ func run() int {
|
||||
UpdateHolaDialer(dialer)
|
||||
}
|
||||
|
||||
if args.list_countries {
|
||||
return print_countries(args.timeout)
|
||||
}
|
||||
try := retryPolicy(args.initRetries, args.initRetryInterval, mainLogger)
|
||||
|
||||
if args.list_proxies {
|
||||
return print_proxies(mainLogger, args.country, args.proxy_type, args.limit, args.timeout,
|
||||
args.backoffInitial, args.backoffDeadline)
|
||||
if args.list_countries {
|
||||
return print_countries(try, args.timeout)
|
||||
}
|
||||
|
||||
mainLogger.Info("hola-proxy client version %s is starting...", version)
|
||||
|
||||
var userAgent string
|
||||
if args.userAgent == nil {
|
||||
err := try("get latest version of Chrome browser", func() error {
|
||||
ctx, cl := context.WithTimeout(context.Background(), args.timeout)
|
||||
defer cl()
|
||||
ver, err := GetChromeVer(ctx, dialer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mainLogger.Info("latest Chrome version is %q", ver)
|
||||
majorVer, _, _ := strings.Cut(ver, ".")
|
||||
userAgent = fmt.Sprintf(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/%s.0.0.0 Safari/537.36",
|
||||
majorVer)
|
||||
mainLogger.Info("discovered latest Chrome User-Agent: %q", userAgent)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
mainLogger.Critical("Can't detect latest Chrome version. "+
|
||||
"Try to specify proper user agent with -user-agent parameter. Error: %v",
|
||||
err)
|
||||
return 8
|
||||
}
|
||||
} else {
|
||||
userAgent = *args.userAgent
|
||||
}
|
||||
SetUserAgent(userAgent)
|
||||
|
||||
if args.extVer == "" {
|
||||
err := try("get latest version of browser extension", func() error {
|
||||
ctx, cl := context.WithTimeout(context.Background(), args.timeout)
|
||||
defer cl()
|
||||
extVer, err := GetExtVer(ctx, nil, HolaExtStoreID, dialer)
|
||||
if err == nil {
|
||||
mainLogger.Info("discovered latest browser extension version: %s", extVer)
|
||||
args.extVer = extVer
|
||||
}
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
mainLogger.Critical("Can't detect latest browser extension version. Try to specify -ext-ver parameter. Error: %v", err)
|
||||
return 8
|
||||
}
|
||||
mainLogger.Warning("Detected latest extension version: %q. Pass -ext-ver parameter to skip resolve and speedup startup", args.extVer)
|
||||
}
|
||||
if args.list_proxies {
|
||||
return print_proxies(try, mainLogger, args.extVer, args.country, args.proxy_type, args.limit, args.timeout,
|
||||
args.backoffInitial, args.backoffDeadline)
|
||||
}
|
||||
|
||||
mainLogger.Info("Constructing fallback DNS upstream...")
|
||||
resolver, err := NewResolver(args.resolver, args.timeout)
|
||||
if err != nil {
|
||||
@@ -183,11 +253,16 @@ func run() int {
|
||||
return 6
|
||||
}
|
||||
|
||||
mainLogger.Info("Initializing configuration provider...")
|
||||
auth, tunnels, err := CredService(args.rotate, args.timeout, args.country, args.proxy_type, credLogger,
|
||||
args.backoffInitial, args.backoffDeadline)
|
||||
var (
|
||||
auth AuthProvider
|
||||
tunnels *ZGetTunnelsResponse
|
||||
)
|
||||
err = try("run credentials service", func() error {
|
||||
auth, tunnels, err = CredService(args.rotate, args.timeout, args.extVer, args.country,
|
||||
args.proxy_type, credLogger, args.backoffInitial, args.backoffDeadline)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
mainLogger.Critical("Unable to instantiate credential service: %v", err)
|
||||
return 4
|
||||
}
|
||||
endpoint, err := get_endpoint(tunnels, args.proxy_type, args.use_trial, args.force_port_field)
|
||||
@@ -195,8 +270,8 @@ func run() int {
|
||||
mainLogger.Critical("Unable to determine proxy endpoint: %v", err)
|
||||
return 5
|
||||
}
|
||||
handlerDialer := NewProxyDialer(endpoint.NetAddr(), endpoint.TLSName, caPool, auth, dialer)
|
||||
requestDialer := NewPlaintextDialer(endpoint.NetAddr(), endpoint.TLSName, caPool, dialer)
|
||||
handlerDialer := NewProxyDialer(endpoint.NetAddr(), endpoint.TLSName, caPool, auth, args.hideSNI, dialer)
|
||||
requestDialer := NewPlaintextDialer(endpoint.NetAddr(), endpoint.TLSName, caPool, args.hideSNI, dialer)
|
||||
mainLogger.Info("Endpoint: %s", endpoint.URL().String())
|
||||
mainLogger.Info("Starting proxy server...")
|
||||
handler := NewProxyHandler(handlerDialer, requestDialer, auth, resolver, proxyLogger)
|
||||
@@ -210,3 +285,24 @@ func run() int {
|
||||
func main() {
|
||||
os.Exit(run())
|
||||
}
|
||||
|
||||
func retryPolicy(retries int, retryInterval time.Duration, logger *CondLogger) func(string, func() error) error {
|
||||
return func(name string, f func() error) error {
|
||||
var err error
|
||||
for i := 1; retries <= 0 || i <= retries; i++ {
|
||||
if i > 1 {
|
||||
logger.Warning("Retrying action %q in %v...", name, retryInterval)
|
||||
time.Sleep(retryInterval)
|
||||
}
|
||||
logger.Info("Attempting action %q, attempt #%d...", name, i)
|
||||
err = f()
|
||||
if err == nil {
|
||||
logger.Info("Action %q succeeded on attempt #%d", name, i)
|
||||
return nil
|
||||
}
|
||||
logger.Warning("Action %q failed: %v", name, err)
|
||||
}
|
||||
logger.Critical("All attempts for action %q have failed. Last error: %v", name, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
22
plaintext.go
22
plaintext.go
@@ -2,10 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
tls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
type PlaintextDialer struct {
|
||||
@@ -13,14 +14,16 @@ type PlaintextDialer struct {
|
||||
tlsServerName string
|
||||
next ContextDialer
|
||||
caPool *x509.CertPool
|
||||
hideSNI bool
|
||||
}
|
||||
|
||||
func NewPlaintextDialer(address, tlsServerName string, caPool *x509.CertPool, next ContextDialer) *PlaintextDialer {
|
||||
func NewPlaintextDialer(address, tlsServerName string, caPool *x509.CertPool, hideSNI bool, next ContextDialer) *PlaintextDialer {
|
||||
return &PlaintextDialer{
|
||||
fixedAddress: address,
|
||||
tlsServerName: tlsServerName,
|
||||
next: next,
|
||||
caPool: caPool,
|
||||
hideSNI: hideSNI,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +43,12 @@ func (d *PlaintextDialer) DialContext(ctx context.Context, network, address stri
|
||||
// Custom cert verification logic:
|
||||
// DO NOT send SNI extension of TLS ClientHello
|
||||
// DO peer certificate verification against specified servername
|
||||
conn = tls.Client(conn, &tls.Config{
|
||||
ServerName: "",
|
||||
sni := d.tlsServerName
|
||||
if d.hideSNI {
|
||||
sni = ""
|
||||
}
|
||||
tlsConn := tls.UClient(conn, &tls.Config{
|
||||
ServerName: sni,
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
opts := x509.VerifyOptions{
|
||||
@@ -55,7 +62,12 @@ func (d *PlaintextDialer) DialContext(ctx context.Context, network, address stri
|
||||
_, err := cs.PeerCertificates[0].Verify(opts)
|
||||
return err
|
||||
},
|
||||
})
|
||||
}, tls.HelloAndroid_11_OkHttp)
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
return tlsConn, nil
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
name: hola-proxy
|
||||
version: '1.7.2'
|
||||
summary: Standalone Hola proxy client.
|
||||
description: |
|
||||
Standalone Hola proxy client. Just run it and it'll start plain HTTP proxy server forwarding traffic via Hola proxies of your choice.
|
||||
|
||||
confinement: strict
|
||||
base: core18
|
||||
base: core22
|
||||
adopt-info: hola-proxy
|
||||
|
||||
parts:
|
||||
hola-proxy:
|
||||
plugin: go
|
||||
source: .
|
||||
build-snaps: [go/latest/stable]
|
||||
build-packages:
|
||||
- gcc
|
||||
- make
|
||||
- git-core
|
||||
source: https://github.com/Snawoot/hola-proxy
|
||||
source-type: git
|
||||
override-pull: |
|
||||
craftctl default
|
||||
craftctl set version="$(git describe --long --tags --always --match=v*.*.* | sed 's/v//')"
|
||||
override-build:
|
||||
make &&
|
||||
cp bin/hola-proxy "$SNAPCRAFT_PART_INSTALL"
|
||||
|
||||
19
upstream.go
19
upstream.go
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -14,6 +13,8 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
tls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,15 +40,17 @@ type ProxyDialer struct {
|
||||
auth AuthProvider
|
||||
next ContextDialer
|
||||
caPool *x509.CertPool
|
||||
hideSNI bool
|
||||
}
|
||||
|
||||
func NewProxyDialer(address, tlsServerName string, caPool *x509.CertPool, auth AuthProvider, nextDialer ContextDialer) *ProxyDialer {
|
||||
func NewProxyDialer(address, tlsServerName string, caPool *x509.CertPool, auth AuthProvider, hideSNI bool, nextDialer ContextDialer) *ProxyDialer {
|
||||
return &ProxyDialer{
|
||||
address: address,
|
||||
tlsServerName: tlsServerName,
|
||||
auth: auth,
|
||||
next: nextDialer,
|
||||
caPool: caPool,
|
||||
hideSNI: hideSNI,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +84,7 @@ func ProxyDialerFromURL(u *url.URL, caPool *x509.CertPool, next ContextDialer) (
|
||||
return authHeader
|
||||
}
|
||||
}
|
||||
return NewProxyDialer(address, tlsServerName, caPool, auth, next), nil
|
||||
return NewProxyDialer(address, tlsServerName, caPool, auth, false, next), nil
|
||||
}
|
||||
|
||||
func (d *ProxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
@@ -100,8 +103,12 @@ func (d *ProxyDialer) DialContext(ctx context.Context, network, address string)
|
||||
// Custom cert verification logic:
|
||||
// DO NOT send SNI extension of TLS ClientHello
|
||||
// DO peer certificate verification against specified servername
|
||||
conn = tls.Client(conn, &tls.Config{
|
||||
ServerName: "",
|
||||
sni := d.tlsServerName
|
||||
if d.hideSNI {
|
||||
sni = ""
|
||||
}
|
||||
conn = tls.UClient(conn, &tls.Config{
|
||||
ServerName: sni,
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: func(cs tls.ConnectionState) error {
|
||||
opts := x509.VerifyOptions{
|
||||
@@ -115,7 +122,7 @@ func (d *ProxyDialer) DialContext(ctx context.Context, network, address string)
|
||||
_, err := cs.PeerCertificates[0].Verify(opts)
|
||||
return err
|
||||
},
|
||||
})
|
||||
}, tls.HelloAndroid_11_OkHttp)
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
|
||||
70
utils.go
70
utils.go
@@ -106,27 +106,33 @@ func proxyh2(ctx context.Context, leftreader io.ReadCloser, leftwriter io.Writer
|
||||
return
|
||||
}
|
||||
|
||||
func print_countries(timeout time.Duration) int {
|
||||
func print_countries(try func(string, func() error) error, timeout time.Duration) int {
|
||||
var (
|
||||
countries CountryList
|
||||
err error
|
||||
tx_res bool
|
||||
tx_err error
|
||||
)
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
ctx1, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
countries, err = VPNCountries(ctx1, client)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction error: %v. Retrying with the fallback mechanism...\n", err)
|
||||
return false
|
||||
err = try("list VPN countries", func() error {
|
||||
tx_res, tx_err = EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
ctx1, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
countries, err = VPNCountries(ctx1, client)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction error: %v. Retrying with the fallback mechanism...\n", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if tx_err != nil {
|
||||
return fmt.Errorf("transaction recovery mechanism failure: %v", err)
|
||||
}
|
||||
return true
|
||||
if !tx_res {
|
||||
return errors.New("all fallback proxies failed.")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if tx_err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction recovery mechanism failure: %v.\n", tx_err)
|
||||
return 4
|
||||
}
|
||||
if !tx_res {
|
||||
fmt.Fprintf(os.Stderr, "All attempts failed.")
|
||||
if err != nil {
|
||||
return 3
|
||||
}
|
||||
for _, code := range countries {
|
||||
@@ -135,32 +141,38 @@ func print_countries(timeout time.Duration) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func print_proxies(logger *CondLogger, country string, proxy_type string,
|
||||
func print_proxies(try func(string, func() error) error, logger *CondLogger, extVer, country string, proxy_type string,
|
||||
limit uint, timeout time.Duration, backoffInitial time.Duration, backoffDeadline time.Duration,
|
||||
) int {
|
||||
var (
|
||||
tunnels *ZGetTunnelsResponse
|
||||
user_uuid string
|
||||
err error
|
||||
tx_res bool
|
||||
tx_err error
|
||||
)
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tunnels, user_uuid, err = Tunnels(ctx, logger, client, country, proxy_type, limit, timeout, backoffInitial, backoffDeadline)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction error: %v. Retrying with the fallback mechanism...\n", err)
|
||||
return false
|
||||
err = try("list proxies", func() error {
|
||||
tx_res, tx_err = EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tunnels, user_uuid, err = Tunnels(ctx, logger, client, extVer, country, proxy_type, limit, timeout, backoffInitial, backoffDeadline)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction error: %v. Retrying with the fallback mechanism...\n", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if tx_err != nil {
|
||||
return fmt.Errorf("transaction recovery mechanism failure: %v", err)
|
||||
}
|
||||
return true
|
||||
if !tx_res {
|
||||
return errors.New("all fallback proxies failed.")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if tx_err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Transaction recovery mechanism failure: %v.\n", tx_err)
|
||||
return 4
|
||||
}
|
||||
if !tx_res {
|
||||
fmt.Fprintf(os.Stderr, "All attempts failed.")
|
||||
if err != nil {
|
||||
return 3
|
||||
}
|
||||
wr := csv.NewWriter(os.Stdout)
|
||||
login := LOGIN_PREFIX + user_uuid
|
||||
login := TemplateLogin(user_uuid)
|
||||
password := tunnels.AgentKey
|
||||
fmt.Println("Login:", login)
|
||||
fmt.Println("Password:", password)
|
||||
|
||||
Reference in New Issue
Block a user