mirror of
https://github.com/Snawoot/hola-proxy.git
synced 2026-04-04 15:28:12 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c6521777 | ||
|
|
db109ce9ad | ||
|
|
16192a36ba | ||
|
|
d75ab999c8 | ||
|
|
a95a6519aa | ||
|
|
5774d898d2 | ||
|
|
8a3b18e784 | ||
|
|
3ebfea6b86 | ||
|
|
92b7a509e9 | ||
|
|
d4b8306cd4 | ||
|
|
335894d533 | ||
|
|
d95706046d | ||
|
|
6fecb99728 | ||
|
|
0d0cbca13e | ||
|
|
3462113454 | ||
|
|
db9499a936 | ||
|
|
1052329228 | ||
|
|
94addea5d4 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -14,3 +14,7 @@
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
bin/
|
||||
*.snap
|
||||
|
||||
# hola-proxy binary
|
||||
hola-proxy
|
||||
|
||||
@@ -6,9 +6,12 @@ RUN CGO_ENABLED=0 go build -a -tags netgo -ldflags '-s -w -extldflags "-static"'
|
||||
ADD https://curl.haxx.se/ca/cacert.pem /certs.crt
|
||||
RUN chmod 0644 /certs.crt
|
||||
|
||||
FROM scratch
|
||||
FROM scratch AS arrange
|
||||
COPY --from=build /go/src/github.com/Snawoot/hola-proxy/hola-proxy /
|
||||
COPY --from=build /certs.crt /etc/ssl/certs/ca-certificates.crt
|
||||
|
||||
FROM scratch
|
||||
COPY --from=arrange / /
|
||||
USER 9999:9999
|
||||
EXPOSE 8080/tcp
|
||||
ENTRYPOINT ["/hola-proxy", "-bind-address", "0.0.0.0:8080"]
|
||||
|
||||
60
README.md
60
README.md
@@ -1,9 +1,32 @@
|
||||
# hola-proxy
|
||||
|
||||
[](https://snapcraft.io/hola-proxy)
|
||||
|
||||
Standalone Hola proxy client. Just run it and it'll start plain HTTP proxy server forwarding traffic via Hola proxies of your choice. By default application listens port on 127.0.0.1:8080.
|
||||
|
||||
Application is capable to forward traffic via proxies in datacenters (flag `-proxy-type direct`, default) or via peer proxies on residental IPs (consumer ISP) in that country (flag `-proxy-type peer`).
|
||||
|
||||
---
|
||||
|
||||
: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)
|
||||
@@ -12,14 +35,20 @@ Application is capable to forward traffic via proxies in datacenters (flag `-pro
|
||||
|
||||
## Installation
|
||||
|
||||
#### Binary download
|
||||
|
||||
Pre-built binaries available on [releases](https://github.com/Snawoot/hola-proxy/releases/latest) page.
|
||||
|
||||
#### From source
|
||||
|
||||
Alternatively, you may install hola-proxy from source. Run within source directory
|
||||
|
||||
```
|
||||
go install
|
||||
```
|
||||
|
||||
#### Docker
|
||||
|
||||
Docker image is available as well. Here is an example for running proxy via DE as a background service:
|
||||
|
||||
```sh
|
||||
@@ -31,6 +60,14 @@ docker run -d \
|
||||
yarmak/hola-proxy -country de
|
||||
```
|
||||
|
||||
#### Snap Store
|
||||
|
||||
[](https://snapcraft.io/hola-proxy)
|
||||
|
||||
```bash
|
||||
sudo snap install hola-proxy
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
List available countries:
|
||||
@@ -53,8 +90,10 @@ dk - Denmark
|
||||
es - Spain
|
||||
fi - Finland
|
||||
fr - France
|
||||
gb - United Kingdom (Great Britain)
|
||||
gr - Greece
|
||||
hk - Hong Kong
|
||||
hr - Croatia
|
||||
hu - Hungary
|
||||
id - Indonesia
|
||||
ie - Ireland
|
||||
@@ -88,21 +127,21 @@ $ ~/go/bin/hola-proxy -country de
|
||||
Or run proxy on residental IP:
|
||||
|
||||
```
|
||||
$ ~/go/bin/hola-proxy -country de -proxy-type peer
|
||||
$ ~/go/bin/hola-proxy -country de -proxy-type lum
|
||||
```
|
||||
|
||||
Also it is possible to export proxy addresses and credentials:
|
||||
|
||||
```
|
||||
$ ~/go/bin/hola-proxy -country de -list-proxies -limit 3
|
||||
Login: user-uuid-f4c2c3a8657640048e7243a807867d52
|
||||
Password: e194c4f457e0
|
||||
Proxy-Authorization: basic dXNlci11dWlkLWY0YzJjM2E4NjU3NjQwMDQ4ZTcyNDNhODA3ODY3ZDUyOmUxOTRjNGY0NTdlMA==
|
||||
Login: user-uuid-0a67c797b3214cbdb432b089c4b801cd
|
||||
Password: cd123c465901
|
||||
Proxy-Authorization: basic dXNlci11dWlkLTBhNjdjNzk3YjMyMTRjYmRiNDMyYjA4OWM0YjgwMWNkOmNkMTIzYzQ2NTkwMQ==
|
||||
|
||||
Host,IP address,Direct port,Peer port,Vendor
|
||||
zagent90.hola.org,185.72.246.203,22222,22223,nqhost
|
||||
zagent249.hola.org,165.22.80.107,22222,22223,digitalocean
|
||||
zagent248.hola.org,165.22.65.3,22222,22223,digitalocean
|
||||
host,ip_address,direct,peer,hola,trial,trial_peer,vendor
|
||||
zagent783.hola.org,165.22.22.6,22222,22223,22224,22225,22226,digitalocean
|
||||
zagent830.hola.org,104.248.24.64,22222,22223,22224,22225,22226,digitalocean
|
||||
zagent248.hola.org,165.22.65.3,22222,22223,22224,22225,22226,digitalocean
|
||||
```
|
||||
|
||||
## Synopsis
|
||||
@@ -121,14 +160,15 @@ Usage of /home/user/go/bin/hola-proxy:
|
||||
-list-proxies
|
||||
output proxy list and exit
|
||||
-proxy-type string
|
||||
proxy type: direct or peer (default "direct")
|
||||
proxy type: direct or peer or 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)
|
||||
-timeout duration
|
||||
timeout for network operations (default 10s)
|
||||
-use-trial
|
||||
use trial ports instead of regular ports
|
||||
-verbosity int
|
||||
logging verbosity (10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical) (default 20)
|
||||
|
||||
```
|
||||
|
||||
@@ -11,6 +11,7 @@ const API_CALL_ATTEMPTS = 3
|
||||
|
||||
func CredService(interval, timeout time.Duration,
|
||||
country string,
|
||||
proxytype string,
|
||||
logger *CondLogger) (auth AuthProvider,
|
||||
tunnels *ZGetTunnelsResponse,
|
||||
err error) {
|
||||
@@ -25,7 +26,7 @@ func CredService(interval, timeout time.Duration,
|
||||
|
||||
for i := 0; i < API_CALL_ATTEMPTS ; i++ {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
tunnels, user_uuid, err = Tunnels(ctx, country, DEFAULT_LIST_LIMIT)
|
||||
tunnels, user_uuid, err = Tunnels(ctx, country, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
@@ -49,7 +50,7 @@ func CredService(interval, timeout time.Duration,
|
||||
logger.Info("Rotating credentials...")
|
||||
for i := 0; i < API_CALL_ATTEMPTS ; i++ {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
tuns, user_uuid, err = Tunnels(ctx, country, DEFAULT_LIST_LIMIT)
|
||||
tuns, user_uuid, err = Tunnels(ctx, country, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -4,6 +4,7 @@ go 1.13
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.25.0
|
||||
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/miekg/dns v1.1.29
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@@ -11,6 +11,8 @@ github.com/ameshkov/dnscrypt v1.1.0/go.mod h1:ikduAxNLCTEfd1AaCgpIA5TgroIVQ8JY3V
|
||||
github.com/ameshkov/dnsstamps v1.0.1 h1:LhGvgWDzhNJh+kBQd/AfUlq1vfVe109huiXw4JhnPug=
|
||||
github.com/ameshkov/dnsstamps v1.0.1/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
|
||||
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
||||
24
holaapi.go
24
holaapi.go
@@ -11,10 +11,11 @@ import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"math/rand"
|
||||
"github.com/campoy/unique"
|
||||
)
|
||||
|
||||
const USER_AGENT = "Mozilla/5.0 (X11; Fedora; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36"
|
||||
const EXT_VER = "1.164.641"
|
||||
const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
|
||||
const EXT_VER = "1.181.350"
|
||||
const EXT_BROWSER = "chrome"
|
||||
const PRODUCT = "cws"
|
||||
const CCGI_URL = "https://client.hola.org/client_cgi/"
|
||||
@@ -96,6 +97,13 @@ func VPNCountries(ctx context.Context) (res CountryList, err error) {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(data, &res)
|
||||
for _, a := range res {
|
||||
if a == "uk" {
|
||||
res = append(res, "gb")
|
||||
}
|
||||
}
|
||||
less := func(i, j int) bool { return res[i] < res[j] }
|
||||
unique.Slice(&res, less)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,10 +126,17 @@ func zgettunnels(ctx context.Context,
|
||||
user_uuid string,
|
||||
session_key int64,
|
||||
country string,
|
||||
proxy_type string,
|
||||
limit uint) (res *ZGetTunnelsResponse, reterr error) {
|
||||
var tunnels ZGetTunnelsResponse
|
||||
params := make(url.Values)
|
||||
params.Add("country", country)
|
||||
if proxy_type == "lum" {
|
||||
params.Add("country", country + ".pool_lum_" + country + "_shared")
|
||||
} else if proxy_type == "peer" {
|
||||
params.Add("country", country + ".peer")
|
||||
} else {
|
||||
params.Add("country", country)
|
||||
}
|
||||
params.Add("limit", strconv.FormatInt(int64(limit), 10))
|
||||
params.Add("ping_id", strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
|
||||
params.Add("ext_ver", EXT_VER)
|
||||
@@ -142,6 +157,7 @@ func zgettunnels(ctx context.Context,
|
||||
|
||||
func Tunnels(ctx context.Context,
|
||||
country string,
|
||||
proxy_type string,
|
||||
limit uint) (res *ZGetTunnelsResponse, user_uuid string, reterr error) {
|
||||
u := uuid.New()
|
||||
user_uuid = hex.EncodeToString(u[:])
|
||||
@@ -150,6 +166,6 @@ func Tunnels(ctx context.Context,
|
||||
reterr = err
|
||||
return
|
||||
}
|
||||
res, reterr = zgettunnels(ctx, user_uuid, initres.Key, country, limit)
|
||||
res, reterr = zgettunnels(ctx, user_uuid, initres.Key, country, proxy_type, limit)
|
||||
return
|
||||
}
|
||||
|
||||
15
main.go
15
main.go
@@ -36,7 +36,7 @@ func arg_fail(msg string) {
|
||||
|
||||
type CLIArgs struct {
|
||||
country string
|
||||
list_countries, list_proxies bool
|
||||
list_countries, list_proxies, use_trial bool
|
||||
limit uint
|
||||
bind_address string
|
||||
verbosity int
|
||||
@@ -57,14 +57,18 @@ func parse_args() CLIArgs {
|
||||
"(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.StringVar(&args.proxy_type, "proxy-type", "direct", "proxy type: direct or peer")
|
||||
flag.StringVar(&args.proxy_type, "proxy-type", "direct", "proxy type: direct or peer or lum")
|
||||
flag.StringVar(&args.resolver, "resolver", "https://cloudflare-dns.com/dns-query",
|
||||
"DNS/DoH/DoT resolver to workaround Hola blocked hosts. " +
|
||||
"See https://github.com/ameshkov/dnslookup/ for upstream DNS URL format.")
|
||||
flag.BoolVar(&args.use_trial, "use-trial", false, "use trial ports instead of regular ports")
|
||||
flag.Parse()
|
||||
if args.country == "" {
|
||||
arg_fail("Country can't be empty string.")
|
||||
}
|
||||
if args.proxy_type == "" {
|
||||
arg_fail("Proxy type can't be an empty string.")
|
||||
}
|
||||
if args.list_countries && args.list_proxies {
|
||||
arg_fail("list-countries and list-proxies flags are mutually exclusive")
|
||||
}
|
||||
@@ -77,7 +81,7 @@ func run() int {
|
||||
return print_countries(args.timeout)
|
||||
}
|
||||
if args.list_proxies {
|
||||
return print_proxies(args.country, args.limit, args.timeout)
|
||||
return print_proxies(args.country, args.proxy_type, args.limit, args.timeout)
|
||||
}
|
||||
|
||||
logWriter := NewLogWriter(os.Stderr)
|
||||
@@ -99,18 +103,19 @@ func run() int {
|
||||
return 6
|
||||
}
|
||||
mainLogger.Info("Initializing configuration provider...")
|
||||
auth, tunnels, err := CredService(args.rotate, args.timeout, args.country, credLogger)
|
||||
auth, tunnels, err := CredService(args.rotate, args.timeout, args.country, args.proxy_type, credLogger)
|
||||
if err != nil {
|
||||
mainLogger.Critical("Unable to instantiate credential service: %v", err)
|
||||
logWriter.Close()
|
||||
return 4
|
||||
}
|
||||
endpoint, err := get_endpoint(tunnels, args.proxy_type)
|
||||
endpoint, err := get_endpoint(tunnels, args.proxy_type, args.use_trial)
|
||||
if err != nil {
|
||||
mainLogger.Critical("Unable to determine proxy endpoint: %v", err)
|
||||
logWriter.Close()
|
||||
return 5
|
||||
}
|
||||
mainLogger.Info("Endpoint: %s", endpoint)
|
||||
mainLogger.Info("Starting proxy server...")
|
||||
handler := NewProxyHandler(endpoint, auth, resolver, proxyLogger)
|
||||
err = http.ListenAndServe(args.bind_address, handler)
|
||||
|
||||
24
snapcraft.yaml
Normal file
24
snapcraft.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
name: hola-proxy
|
||||
version: '1.2.1'
|
||||
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
|
||||
|
||||
parts:
|
||||
hola-proxy:
|
||||
plugin: go
|
||||
go-importpath: github.com/Snawoot/hola-proxy
|
||||
source: https://github.com/Snawoot/hola-proxy
|
||||
source-type: git
|
||||
build-packages:
|
||||
- gcc
|
||||
|
||||
apps:
|
||||
hola-proxy:
|
||||
command: bin/hola-proxy
|
||||
plugs:
|
||||
- network
|
||||
- network-bind
|
||||
25
utils.go
25
utils.go
@@ -62,9 +62,9 @@ func print_countries(timeout time.Duration) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func print_proxies(country string, limit uint, timeout time.Duration) int {
|
||||
func print_proxies(country string, proxy_type string, limit uint, timeout time.Duration) int {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
tunnels, user_uuid, err := Tunnels(ctx, country, limit)
|
||||
tunnels, user_uuid, err := Tunnels(ctx, country, proxy_type, limit)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
return 3
|
||||
@@ -77,13 +77,16 @@ func print_proxies(country string, limit uint, timeout time.Duration) int {
|
||||
fmt.Println("Proxy-Authorization:",
|
||||
basic_auth_header(login, password))
|
||||
fmt.Println("")
|
||||
wr.Write([]string{"Host", "IP address", "Direct port", "Peer port", "Vendor"})
|
||||
wr.Write([]string{"host", "ip_address", "direct", "peer", "hola", "trial", "trial_peer", "vendor"})
|
||||
for host, ip := range tunnels.IPList {
|
||||
if (PROTOCOL_WHITELIST[tunnels.Protocol[host]]) {
|
||||
wr.Write([]string{host,
|
||||
ip,
|
||||
strconv.FormatUint(uint64(tunnels.Port.Direct), 10),
|
||||
strconv.FormatUint(uint64(tunnels.Port.Peer), 10),
|
||||
strconv.FormatUint(uint64(tunnels.Port.Hola), 10),
|
||||
strconv.FormatUint(uint64(tunnels.Port.Trial), 10),
|
||||
strconv.FormatUint(uint64(tunnels.Port.TrialPeer), 10),
|
||||
tunnels.Vendor[host]})
|
||||
}
|
||||
}
|
||||
@@ -91,7 +94,7 @@ func print_proxies(country string, limit uint, timeout time.Duration) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func get_endpoint(tunnels *ZGetTunnelsResponse, typ string) (string, error) {
|
||||
func get_endpoint(tunnels *ZGetTunnelsResponse, typ string, trial bool) (string, error) {
|
||||
var hostname string
|
||||
for k, _ := range tunnels.IPList {
|
||||
hostname = k
|
||||
@@ -101,10 +104,18 @@ func get_endpoint(tunnels *ZGetTunnelsResponse, typ string) (string, error) {
|
||||
return "", errors.New("No tunnels found in API response")
|
||||
}
|
||||
var port uint16
|
||||
if typ == "direct" {
|
||||
port = tunnels.Port.Direct
|
||||
if typ == "direct" || typ == "lum" {
|
||||
if trial {
|
||||
port = tunnels.Port.Trial
|
||||
} else {
|
||||
port = tunnels.Port.Direct
|
||||
}
|
||||
} else if typ == "peer" {
|
||||
port = tunnels.Port.Peer
|
||||
if trial {
|
||||
port = tunnels.Port.TrialPeer
|
||||
} else {
|
||||
port = tunnels.Port.Peer
|
||||
}
|
||||
} else {
|
||||
return "", errors.New("Unsupported port type")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user