mirror of
https://github.com/Snawoot/hola-proxy.git
synced 2026-04-04 07:28:12 +00:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
528e2b2a71 | ||
|
|
0a473f9662 | ||
|
|
17860682be | ||
|
|
1f6c87a797 | ||
|
|
72beef10c9 | ||
|
|
ead89d5245 | ||
|
|
6edd098c82 | ||
|
|
752d2ba789 | ||
|
|
311d1ad74d | ||
|
|
6ac04587cb | ||
|
|
8c3538ab4c | ||
|
|
ff3c0f68fc | ||
|
|
4f72e317fc | ||
|
|
4ddd1284fb | ||
|
|
4fe6636003 | ||
|
|
b5ac6d38e5 | ||
|
|
0c377308c0 | ||
|
|
3b21036b20 |
@@ -1,8 +1,10 @@
|
||||
FROM golang 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"'
|
||||
RUN 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
|
||||
|
||||
|
||||
10
Makefile
10
Makefile
@@ -1,7 +1,8 @@
|
||||
PROGNAME = hola-proxy
|
||||
OUTSUFFIX = bin/$(PROGNAME)
|
||||
VERSION := $(shell git describe)
|
||||
BUILDOPTS = -a -tags netgo
|
||||
LDFLAGS = -ldflags '-s -w -extldflags "-static"'
|
||||
LDFLAGS = -ldflags '-s -w -extldflags "-static" -X main.version=$(VERSION)'
|
||||
|
||||
src = $(wildcard *.go)
|
||||
|
||||
@@ -59,9 +60,12 @@ fmt:
|
||||
go fmt ./...
|
||||
|
||||
run:
|
||||
go run .
|
||||
go run $(LDFLAGS) .
|
||||
|
||||
.PHONY: clean all native fmt \
|
||||
install:
|
||||
go install $(BUILDOPTS) $(LDFLAGS) .
|
||||
|
||||
.PHONY: clean all native fmt install \
|
||||
bin-native \
|
||||
bin-linux-amd64 \
|
||||
bin-linux-386 \
|
||||
|
||||
@@ -44,7 +44,7 @@ Pre-built binaries available on [releases](https://github.com/Snawoot/hola-proxy
|
||||
Alternatively, you may install hola-proxy from source. Run within source directory
|
||||
|
||||
```
|
||||
go install
|
||||
make install
|
||||
```
|
||||
|
||||
#### Docker
|
||||
|
||||
@@ -2,12 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const DEFAULT_LIST_LIMIT = 3
|
||||
const API_CALL_ATTEMPTS = 3
|
||||
|
||||
func CredService(interval, timeout time.Duration,
|
||||
country string,
|
||||
@@ -24,15 +24,21 @@ func CredService(interval, timeout time.Duration,
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < API_CALL_ATTEMPTS; i++ {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
tunnels, user_uuid, err = Tunnels(ctx, country, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err == nil {
|
||||
break
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tunnels, user_uuid, err = Tunnels(ctx, client, country, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err != nil {
|
||||
logger.Error("Configuration bootstrap error: %v. Retrying with the fallback mechanism...", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if tx_err != nil {
|
||||
logger.Critical("Transaction recovery mechanism failure: %v", tx_err)
|
||||
err = tx_err
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logger.Critical("Configuration bootstrap failed: %v", err)
|
||||
if !tx_res {
|
||||
logger.Critical("All attempts failed.")
|
||||
return
|
||||
}
|
||||
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
|
||||
@@ -48,23 +54,28 @@ func CredService(interval, timeout time.Duration,
|
||||
for {
|
||||
<-ticker.C
|
||||
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, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err == nil {
|
||||
break
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tuns, user_uuid, err = Tunnels(ctx, client, country, proxytype, DEFAULT_LIST_LIMIT)
|
||||
if err != nil {
|
||||
logger.Error("Credential rotation error: %v. Retrying with the fallback mechanism...", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if tx_err != nil {
|
||||
logger.Critical("Transaction recovery mechanism failure: %v", tx_err)
|
||||
err = tx_err
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
logger.Error("Credential rotation failed after %d attempts. Error: %v",
|
||||
API_CALL_ATTEMPTS, err)
|
||||
} else {
|
||||
(&mux).Lock()
|
||||
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
|
||||
tuns.AgentKey)
|
||||
(&mux).Unlock()
|
||||
logger.Info("Credentials rotated successfully.")
|
||||
if !tx_res {
|
||||
logger.Critical("All rotation attempts failed.")
|
||||
continue
|
||||
}
|
||||
(&mux).Lock()
|
||||
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
|
||||
tuns.AgentKey)
|
||||
(&mux).Unlock()
|
||||
logger.Info("Credentials rotated successfully.")
|
||||
}
|
||||
}()
|
||||
return
|
||||
|
||||
23
csrand.go
Normal file
23
csrand.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
crand "crypto/rand"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
type secureRandomSource struct{}
|
||||
|
||||
var RandomSource secureRandomSource
|
||||
|
||||
var int63Limit = big.NewInt(0).Lsh(big.NewInt(1), 63)
|
||||
|
||||
func (_ secureRandomSource) Seed(_ int64) {
|
||||
}
|
||||
|
||||
func (_ secureRandomSource) Int63() int64 {
|
||||
randNum, err := crand.Int(crand.Reader, int63Limit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return randNum.Int64()
|
||||
}
|
||||
213
holaapi.go
213
holaapi.go
@@ -3,17 +3,22 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/campoy/unique"
|
||||
"github.com/google/uuid"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/campoy/unique"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
|
||||
@@ -25,6 +30,8 @@ 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 TemporaryBanError = errors.New("temporary ban detected")
|
||||
var PermanentBanError = errors.New("permanent ban detected")
|
||||
@@ -57,11 +64,66 @@ type ZGetTunnelsResponse struct {
|
||||
Ztun map[string][]string `json:"ztun"`
|
||||
}
|
||||
|
||||
func do_req(ctx context.Context, method, url string, query, data url.Values) ([]byte, error) {
|
||||
type FallbackAgent struct {
|
||||
Name string `json:"name"`
|
||||
IP string `json:"ip"`
|
||||
Port uint16 `json:"port"`
|
||||
}
|
||||
|
||||
type fallbackConfResponse struct {
|
||||
Agents []FallbackAgent `json:"agents"`
|
||||
UpdatedAt int64 `json:"updated_ts"`
|
||||
TTL int64 `json:"ttl_ms"`
|
||||
}
|
||||
|
||||
type FallbackConfig struct {
|
||||
Agents []FallbackAgent
|
||||
UpdatedAt time.Time
|
||||
TTL time.Duration
|
||||
}
|
||||
|
||||
func (c *FallbackConfig) UnmarshalJSON(data []byte) error {
|
||||
r := fallbackConfResponse{}
|
||||
err := json.Unmarshal(data, &r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Agents = r.Agents
|
||||
c.UpdatedAt = time.Unix(r.UpdatedAt/1000, (r.UpdatedAt%1000)*1000000)
|
||||
c.TTL = time.Duration(r.TTL * 1000000)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *FallbackConfig) Expired() bool {
|
||||
return time.Now().After(c.UpdatedAt.Add(c.TTL))
|
||||
}
|
||||
|
||||
func (c *FallbackConfig) ShuffleAgents() {
|
||||
rand.New(RandomSource).Shuffle(len(c.Agents), func(i, j int) {
|
||||
c.Agents[i], c.Agents[j] = c.Agents[j], c.Agents[i]
|
||||
})
|
||||
}
|
||||
|
||||
func (c *FallbackConfig) Clone() *FallbackConfig {
|
||||
return &FallbackConfig{
|
||||
Agents: append([]FallbackAgent(nil), c.Agents...),
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
TTL: c.TTL,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *FallbackAgent) ToProxy() *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: net.JoinHostPort(a.Name+AGENT_SUFFIX,
|
||||
fmt.Sprintf("%d", a.Port)),
|
||||
}
|
||||
}
|
||||
|
||||
func do_req(ctx context.Context, client *http.Client, method, url string, query, data url.Values) ([]byte, error) {
|
||||
var (
|
||||
client http.Client
|
||||
req *http.Request
|
||||
err error
|
||||
req *http.Request
|
||||
err error
|
||||
)
|
||||
if method == "" {
|
||||
method = "GET"
|
||||
@@ -91,7 +153,7 @@ func do_req(ctx context.Context, method, url string, query, data url.Values) ([]
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK, http.StatusCreated, http.StatusAccepted, http.StatusNoContent:
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Bad HTTP response: %d %s", resp.StatusCode, resp.Status))
|
||||
return nil, errors.New(fmt.Sprintf("Bad HTTP response: %s", resp.Status))
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
@@ -101,10 +163,10 @@ func do_req(ctx context.Context, method, url string, query, data url.Values) ([]
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func VPNCountries(ctx context.Context) (res CountryList, err error) {
|
||||
func VPNCountries(ctx context.Context, client *http.Client) (res CountryList, err error) {
|
||||
params := make(url.Values)
|
||||
params.Add("browser", EXT_BROWSER)
|
||||
data, err := do_req(ctx, "", VPN_COUNTRIES_URL, params, nil)
|
||||
data, err := do_req(ctx, client, "", VPN_COUNTRIES_URL, params, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -119,13 +181,13 @@ func VPNCountries(ctx context.Context) (res CountryList, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func background_init(ctx context.Context, user_uuid string) (res BgInitResponse, reterr error) {
|
||||
func background_init(ctx context.Context, client *http.Client, user_uuid string) (res BgInitResponse, reterr error) {
|
||||
post_data := make(url.Values)
|
||||
post_data.Add("login", "1")
|
||||
post_data.Add("ver", EXT_VER)
|
||||
qs := make(url.Values)
|
||||
qs.Add("uuid", user_uuid)
|
||||
resp, err := do_req(ctx, "POST", BG_INIT_URL, qs, post_data)
|
||||
resp, err := do_req(ctx, client, "POST", BG_INIT_URL, qs, post_data)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
return
|
||||
@@ -143,6 +205,7 @@ func background_init(ctx context.Context, user_uuid string) (res BgInitResponse,
|
||||
}
|
||||
|
||||
func zgettunnels(ctx context.Context,
|
||||
client *http.Client,
|
||||
user_uuid string,
|
||||
session_key int64,
|
||||
country string,
|
||||
@@ -163,14 +226,14 @@ func zgettunnels(ctx context.Context,
|
||||
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("ping_id", strconv.FormatFloat(rand.New(RandomSource).Float64(), 'f', -1, 64))
|
||||
params.Add("ext_ver", EXT_VER)
|
||||
params.Add("browser", EXT_BROWSER)
|
||||
params.Add("product", PRODUCT)
|
||||
params.Add("uuid", user_uuid)
|
||||
params.Add("session_key", strconv.FormatInt(session_key, 10))
|
||||
params.Add("is_premium", "0")
|
||||
data, err := do_req(ctx, "", ZGETTUNNELS_URL, params, nil)
|
||||
data, err := do_req(ctx, client, "", ZGETTUNNELS_URL, params, nil)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
return
|
||||
@@ -180,17 +243,137 @@ func zgettunnels(ctx context.Context,
|
||||
return
|
||||
}
|
||||
|
||||
func fetchFallbackConfig(ctx context.Context) (*FallbackConfig, error) {
|
||||
confRaw, err := do_req(ctx, &http.Client{}, "", FALLBACK_CONF_URL, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l := len(confRaw)
|
||||
if l < 4 {
|
||||
return nil, errors.New("bad response length from fallback conf URL")
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Grow(l)
|
||||
buf.Write(confRaw[l-3:])
|
||||
buf.Write(confRaw[:l-3])
|
||||
|
||||
b64dec := base64.NewDecoder(base64.RawStdEncoding, buf)
|
||||
jdec := json.NewDecoder(b64dec)
|
||||
fbc := &FallbackConfig{}
|
||||
|
||||
err = jdec.Decode(fbc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fbc.Expired() {
|
||||
return nil, errors.New("fetched expired fallback config")
|
||||
}
|
||||
|
||||
fbc.ShuffleAgents()
|
||||
return fbc, nil
|
||||
}
|
||||
|
||||
var (
|
||||
fbcMux sync.Mutex
|
||||
cachedFBC *FallbackConfig
|
||||
)
|
||||
|
||||
func GetFallbackProxies(ctx context.Context) (*FallbackConfig, error) {
|
||||
fbcMux.Lock()
|
||||
defer fbcMux.Unlock()
|
||||
|
||||
var (
|
||||
fbc *FallbackConfig
|
||||
err error
|
||||
)
|
||||
|
||||
if cachedFBC == nil || cachedFBC.Expired() {
|
||||
fbc, err = fetchFallbackConfig(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cachedFBC = fbc
|
||||
} else {
|
||||
fbc = cachedFBC
|
||||
}
|
||||
|
||||
return fbc.Clone(), nil
|
||||
}
|
||||
|
||||
func Tunnels(ctx context.Context,
|
||||
client *http.Client,
|
||||
country string,
|
||||
proxy_type string,
|
||||
limit uint) (res *ZGetTunnelsResponse, user_uuid string, reterr error) {
|
||||
u := uuid.New()
|
||||
user_uuid = hex.EncodeToString(u[:])
|
||||
initres, err := background_init(ctx, user_uuid)
|
||||
initres, err := background_init(ctx, client, user_uuid)
|
||||
if err != nil {
|
||||
reterr = err
|
||||
return
|
||||
}
|
||||
res, reterr = zgettunnels(ctx, user_uuid, initres.Key, country, proxy_type, limit)
|
||||
res, reterr = zgettunnels(ctx, client, user_uuid, initres.Key, country, proxy_type, limit)
|
||||
return
|
||||
}
|
||||
|
||||
// Returns default http client with a proxy override
|
||||
func httpClientWithProxy(agent *FallbackAgent) *http.Client {
|
||||
t := &http.Transport{
|
||||
ForceAttemptHTTP2: true,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
}
|
||||
dialer := &net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}
|
||||
if agent == nil {
|
||||
t.DialContext = dialer.DialContext
|
||||
} else {
|
||||
t.Proxy = http.ProxyURL(agent.ToProxy())
|
||||
addr := net.JoinHostPort(agent.IP, fmt.Sprintf("%d", agent.Port))
|
||||
t.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
return dialer.DialContext(ctx, "tcp4", addr)
|
||||
}
|
||||
}
|
||||
return &http.Client{
|
||||
Transport: t,
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureTransaction(baseCtx context.Context, txnTimeout time.Duration, txn func(context.Context, *http.Client) bool) (bool, error) {
|
||||
client := httpClientWithProxy(nil)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
ctx, cancel := context.WithTimeout(baseCtx, txnTimeout)
|
||||
defer cancel()
|
||||
|
||||
if txn(ctx, client) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Fallback needed
|
||||
fbc, err := GetFallbackProxies(baseCtx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, agent := range fbc.Agents {
|
||||
client = httpClientWithProxy(&agent)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
ctx, cancel = context.WithTimeout(baseCtx, txnTimeout)
|
||||
defer cancel()
|
||||
|
||||
if txn(ctx, client) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
2
hooks/build
Normal file
2
hooks/build
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
docker build --build-arg GIT_DESC="$(git describe)" -f "$DOCKERFILE_PATH" -t "$IMAGE_NAME" .
|
||||
10
main.go
10
main.go
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
var (
|
||||
PROTOCOL_WHITELIST map[string]bool
|
||||
version = "undefined"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -44,6 +45,7 @@ type CLIArgs struct {
|
||||
proxy_type string
|
||||
resolver string
|
||||
force_port_field string
|
||||
showVersion bool
|
||||
}
|
||||
|
||||
func parse_args() CLIArgs {
|
||||
@@ -64,6 +66,7 @@ func parse_args() CLIArgs {
|
||||
"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, "dont-use-trial", false, "use regular ports instead of trial ports") // would be nice to not show in help page
|
||||
flag.BoolVar(&args.showVersion, "version", false, "show program version and exit")
|
||||
flag.Parse()
|
||||
if args.country == "" {
|
||||
arg_fail("Country can't be empty string.")
|
||||
@@ -79,6 +82,11 @@ func parse_args() CLIArgs {
|
||||
|
||||
func run() int {
|
||||
args := parse_args()
|
||||
if args.showVersion {
|
||||
fmt.Println(version)
|
||||
return 0
|
||||
}
|
||||
|
||||
if args.list_countries {
|
||||
return print_countries(args.timeout)
|
||||
}
|
||||
@@ -98,6 +106,7 @@ func run() int {
|
||||
proxyLogger := NewCondLogger(log.New(logWriter, "PROXY : ",
|
||||
log.LstdFlags|log.Lshortfile),
|
||||
args.verbosity)
|
||||
mainLogger.Info("hola-proxy client version %s is starting...", version)
|
||||
mainLogger.Info("Constructing fallback DNS upstream...")
|
||||
resolver, err := NewResolver(args.resolver, args.timeout)
|
||||
if err != nil {
|
||||
@@ -120,6 +129,7 @@ func run() int {
|
||||
mainLogger.Info("Endpoint: %s", endpoint)
|
||||
mainLogger.Info("Starting proxy server...")
|
||||
handler := NewProxyHandler(endpoint, auth, resolver, proxyLogger)
|
||||
mainLogger.Info("Init complete.")
|
||||
err = http.ListenAndServe(args.bind_address, handler)
|
||||
mainLogger.Critical("Server terminated with a reason: %v", err)
|
||||
mainLogger.Info("Shutting down...")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: hola-proxy
|
||||
version: '1.4.2'
|
||||
version: '1.4.4'
|
||||
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.
|
||||
@@ -10,15 +10,18 @@ base: core18
|
||||
parts:
|
||||
hola-proxy:
|
||||
plugin: go
|
||||
go-importpath: github.com/Snawoot/hola-proxy
|
||||
source: https://github.com/Snawoot/hola-proxy
|
||||
source-type: git
|
||||
source: .
|
||||
build-packages:
|
||||
- gcc
|
||||
override-build:
|
||||
make &&
|
||||
cp bin/hola-proxy "$SNAPCRAFT_PART_INSTALL"
|
||||
stage:
|
||||
- hola-proxy
|
||||
|
||||
apps:
|
||||
hola-proxy:
|
||||
command: bin/hola-proxy
|
||||
command: hola-proxy
|
||||
plugs:
|
||||
- network
|
||||
- network-bind
|
||||
|
||||
45
utils.go
45
utils.go
@@ -50,10 +50,24 @@ func proxy(ctx context.Context, left, right net.Conn) {
|
||||
}
|
||||
|
||||
func print_countries(timeout time.Duration) int {
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
countries, err := VPNCountries(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
var (
|
||||
countries CountryList
|
||||
err error
|
||||
)
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
countries, err = VPNCountries(ctx, 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 {
|
||||
fmt.Fprintf(os.Stderr, "Transaction recovery mechanism failure: %v.\n", tx_err)
|
||||
return 4
|
||||
}
|
||||
if !tx_res {
|
||||
fmt.Fprintf(os.Stderr, "All attempts failed.")
|
||||
return 3
|
||||
}
|
||||
for _, code := range countries {
|
||||
@@ -63,10 +77,25 @@ func print_countries(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, proxy_type, limit)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
var (
|
||||
tunnels *ZGetTunnelsResponse
|
||||
user_uuid string
|
||||
err error
|
||||
)
|
||||
tx_res, tx_err := EnsureTransaction(context.Background(), timeout, func(ctx context.Context, client *http.Client) bool {
|
||||
tunnels, user_uuid, err = Tunnels(ctx, client, country, proxy_type, limit)
|
||||
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 {
|
||||
fmt.Fprintf(os.Stderr, "Transaction recovery mechanism failure: %v.\n", tx_err)
|
||||
return 4
|
||||
}
|
||||
if !tx_res {
|
||||
fmt.Fprintf(os.Stderr, "All attempts failed.")
|
||||
return 3
|
||||
}
|
||||
wr := csv.NewWriter(os.Stdout)
|
||||
|
||||
Reference in New Issue
Block a user