Compare commits

...

31 Commits

Author SHA1 Message Date
Vladislav Yarmak
12b05ac0df bump snap version 2021-03-07 13:32:11 +02:00
Snawoot
d798373e66 Merge pull request #18 from rany2/master
Add new proxy-type pool
2021-03-07 13:31:19 +02:00
rany
1d8499dd2e Merge branch 'master' of https://github.com/Snawoot/hola-proxy 2021-03-07 12:52:30 +02:00
rany
e2c9711d73 Add new proxy type pool 2021-03-07 12:47:04 +02:00
rany
5246eff0f5 Add text file with all hola blocked domains 2021-03-07 01:54:50 +02:00
Vladislav Yarmak
45c6521777 bump snap version 2021-03-06 22:27:02 +02:00
Snawoot
db109ce9ad Merge pull request #16 from rany2/master
Fix #15
2021-03-06 22:26:05 +02:00
rany
16192a36ba Fix #15 2021-03-06 22:09:39 +02:00
Vladislav Yarmak
d75ab999c8 bump snap version 2021-03-06 21:16:37 +02:00
Snawoot
a95a6519aa Merge pull request #14 from rany2/master
Fix #13 and enhancements
2021-03-06 21:00:42 +02:00
rany
5774d898d2 update country list from readme 2021-03-06 20:13:43 +02:00
rany
8a3b18e784 update readme 2021-03-06 20:13:01 +02:00
rany
3ebfea6b86 switch back default port 2021-03-06 20:09:56 +02:00
rany
92b7a509e9 remove binary 2021-03-06 20:07:11 +02:00
rany
d4b8306cd4 Fix #13 and enhacements
* Add `-use-trial` to use trial ports.
* Mention Hola and Trial ports in `-list-proxies`
* Add GB to `-list-countries` if UK is present. This is what extension does.
* Add `lum` proxy-type to use residential ips
* Change `peer` and `lum` behavior to also modify the country to `country_code`.peer and `country_code`.pool_lum_`country_code`._shared. This lets them work properly
* Above means that `list-proxies` behavior will change based on `-proxy-type` now(The port isn't the only thing that matters)
2021-03-06 20:03:01 +02:00
Vladislav Yarmak
335894d533 add IPFS mirror 2020-10-24 20:23:54 +03:00
Vladislav Yarmak
d95706046d update readme 2020-08-10 00:50:26 +03:00
Vladislav Yarmak
6fecb99728 add endpoint info to log 2020-05-18 02:00:56 +03:00
Snawoot
0d0cbca13e Merge pull request #3 from Snawoot/readme_upd
doc update
2020-04-20 22:37:57 +03:00
Vladislav Yarmak
3462113454 doc update 2020-04-20 22:34:58 +03:00
Vladislav Yarmak
db9499a936 snap: add required plugs 2020-04-20 22:18:34 +03:00
Vladislav Yarmak
1052329228 add snap build spec 2020-04-20 21:29:45 +03:00
Vladislav Yarmak
94addea5d4 single-layer docker image 2020-04-18 14:04:43 +03:00
Vladislav Yarmak
56a10f2a32 fix docker example 2020-04-18 12:44:40 +03:00
Snawoot
801178a7de Merge pull request #2 from Snawoot/docker
add docker support
2020-04-18 12:33:59 +03:00
Vladislav Yarmak
6d627de3d7 add docker support 2020-04-18 12:30:26 +03:00
Vladislav Yarmak
1a9f547cfe minor improvements 2020-04-14 11:45:03 +03:00
Vladislav Yarmak
1984bb06ee update docs 2020-04-14 00:11:04 +03:00
Snawoot
012d6fbe16 Merge pull request #1 from Snawoot/autoresolve
workaround hola blocked hosts
2020-04-13 23:48:51 +03:00
Vladislav Yarmak
73cdcfb701 polish 2020-04-13 23:48:31 +03:00
Vladislav Yarmak
c626c7cdcc workaround hola blocked hosts 2020-04-13 23:39:58 +03:00
14 changed files with 778 additions and 59 deletions

1
.dockerignore Normal file
View File

@@ -0,0 +1 @@
bin/

4
.gitignore vendored
View File

@@ -14,3 +14,7 @@
# Dependency directories (remove the comment below to include it)
# vendor/
bin/
*.snap
# hola-proxy binary
hola-proxy

17
Dockerfile Normal file
View File

@@ -0,0 +1,17 @@
FROM golang AS build
WORKDIR /go/src/github.com/Snawoot/hola-proxy
COPY . .
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 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"]

View File

@@ -1,8 +1,31 @@
# hola-proxy
[![hola-proxy](https://snapcraft.io//hola-proxy/badge.svg)](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`).
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 pool` or `-proxy-type lum`).
---
: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
@@ -12,12 +35,37 @@ 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.
Alternatively, you may install hola-proxy from source:
#### From source
Alternatively, you may install hola-proxy from source. Run within source directory
```
go get github.com/Snawoot/hola-proxy
go install
```
#### Docker
Docker image is available as well. Here is an example for running proxy via DE as a background service:
```sh
docker run -d \
--security-opt no-new-privileges \
-p 127.0.0.1:8080:8080 \
--restart unless-stopped \
--name hola-proxy \
yarmak/hola-proxy -country de
```
#### Snap Store
[![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/hola-proxy)
```bash
sudo snap install hola-proxy
```
## Usage
@@ -42,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
@@ -77,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
@@ -110,11 +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)
```

195
blocked-domains.txt Normal file
View File

@@ -0,0 +1,195 @@
equinix.com
digitalocean.com
oceanconservancy.org
mail.yahoo.com
mail.yahoo.co.jp
mail.aol.com
mail.aol.fr
mail.com
gmx.com
couponcabin.com
outlook.live.com
hotmail.live.com
outlook.com
mailto.space
e.mail.ru
login.sso.bluewin.ch
secure.centurylink.net
secureserver.net
webmail.usfamily.net
webmail.skymesh.net.au
hanmail.net
mail.rambler.ru
freeola.co.uk
webmail-04.datacenter.cha.cantv.net
linustechtips.com
3capp-mailcom-lxa01.server.lan
noiholiday.com
hostedemail.com
heyholla.com
hellixeducation.com
unisalento.it
secrel.com.br
mailorama.fr
kiwiirc.com
derpibooru.org
trixiebooru.org
derpiboo.ru
derpicdn.net
alice.it
vcwebmail.ocn.ad.jp
tentaciones18.com
ocn.ad.jp
ocn.ne.jp
webmail.numericable.fr
webmail.scarlet.be
webmailer.de
mail.uk.tiscali.com
elisa.fi
saunalahti.fi
globalgiving.org
synacor.com
knology.net
netmail.pipex.net
sony.com
playstation.com
playstation.net
playstationnetwork.com
sonymusic.com
sonypictures.com
sonyentertainmentnetwork.com
perfora.net
hmail.com
gigablast.com
126.com
ono.com
funded.today
manenetheoffnewconne.ru
dlfeed.com
odbnfhbyagyz4.net
pinformationbwarranties.ru
musttthewithoutalsothas.ru
bjkaolp4ds4z.com
advseeking.com
companolo.com
mijnbankering-nl-server.com
froekuge.com
updateacces.org
sophisticatiretaj.net
soap4.me
boriel.com
ebb.mtn.co.za
osbservices.vodacom.co.za
apps.telkom.co.za
holadebug.com
fretebras.com.br
megacupons.online
vitacostapi.bbhosted.com
mobilepayca.com
mobilepayusa.org
mobilepayusa.mobi
mobilepayusa.net
mobilepay-web.gaiareply.eu
puffin.com
miosmsclub.com
linkedin.com
ticketsnow.com
playerline.com
ticketmaster.com
ticketmaster.com.mx
livenation.com
stubhub.com
tickets.com
craigslist.org
kufar.by
fastpeoplesearch.com
btc4you.com
elbitcoingratis.es
btcmine.net
virtualfaucet.net
rawbitcoins.net
freebitcoins4u.com
canhasbitcoin.com
bitcats.net
faucetbtc.com
bitcoins4free.me
thefreebitcoins.net
freebitcoins.me
5mindoge.com
freedoge.co.in
moondoge.co.in
moonbit.co.in
bonusbitcoin.co
bitcoinspace.net
btc-faucet.co
treebitcoin.com
flatcoin.net
baglitecoin.com
moonliteco.in
freebitco.in
github.com
smartadvices.com
generictadalafilusa.com
getitllc.com
createyourownteeshirtonline.com
mondino.it
daum.net
workbooks.com
forum.qt.io
radicalexploits.com
craigslist.ca
craigslist.co.uk
bisaboard.de
criusenergy.com
arenagroup.com
truqueira.com
cratejoy.com
gaynext.ca
hispachan.org
freenode.net
exigent.com.au
au.edu
runsignup.com
thegreekbookstore.com
arriveonline.com.au
reimageplus.com
goso.info
checkip.dyndns.org
warezshare.org
blocklist.de
twiki.org
intherooms.com
globalinnovationexchange.org
portal.webgods.de
postfinance.ch
gvtc.com
cantv.net
homepage-baukasten.de
paginawebgratis.es
own-free-website.com
sitowebfaidate.it
ma-page.fr
homepage-konstruktor.ru
bedava-sitem.com
stronygratis.pl
news.ycombinator.com
tdchosting.dk
infusionsoft.com
puregaming.es
blogalizate.com
teftv.com
porticolegal.com
physicalmed.es
www.htcmania.com
www.tebeosfera.com
grupopuntacana.rubypanel.com
www.fansdeapple.com
cerebriti.com
educaciontrespuntocero.com
pymescomercial.com
creativat.com
financiacionparaempresas.net
moon.rightdns.com
uptodate.com
judoprincast.com
office365.com

View File

@@ -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
}

7
go.mod
View File

@@ -2,4 +2,9 @@ module github.com/Snawoot/hola-proxy
go 1.13
require github.com/google/uuid v1.1.1
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
)

62
go.sum
View File

@@ -1,2 +1,64 @@
github.com/AdguardTeam/dnsproxy v0.25.0 h1:BTUPrrwB01GeQW5d2Xx4pH5HOFXcZxN1MTeNXXuy6vQ=
github.com/AdguardTeam/dnsproxy v0.25.0/go.mod h1:z2EljVLJQXFGZcP9pWABftXm9UxpLNqls7H6bMcIvEY=
github.com/AdguardTeam/golibs v0.4.0 h1:4VX6LoOqFe9p9Gf55BeD8BvJD6M6RDYmgEiHrENE9KU=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
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 v1.1.0 h1:2vAt5dD6ZmqlAxEAfzRcLBnkvdf8NI46Kn9InSwQbSI=
github.com/ameshkov/dnscrypt v1.1.0/go.mod h1:ikduAxNLCTEfd1AaCgpIA5TgroIVQ8JY3Vb095fiFJg=
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=
github.com/go-test/deep v1.0.5/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/joomcode/errorx v1.0.1 h1:CalpDWz14ZHd68fIqluJasJosAewpz2TFaJALrUxjrk=
github.com/joomcode/errorx v1.0.1/go.mod h1:kgco15ekB6cs+4Xjzo7SPeXzx38PbJzBwbnu9qfVNHQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
github.com/miekg/dns v1.1.29/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sparrc/go-ping v0.0.0-20190613174326-4e5b6552494c/go.mod h1:eMyUVp6f/5jnzM+3zahzl7q6UXLbgSc3MKg/+ow9QW0=
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=
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.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8 h1:fpnn/HnJONpIu6hkXi1u/7rR0NzilgWr4T0JmWkEitk=
golang.org/x/crypto v0.0.0-20200403201458-baeed622b8d8/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -6,9 +6,8 @@ import (
"net/http/httputil"
"crypto/tls"
"strings"
"context"
"time"
"net/url"
"bufio"
)
type AuthProvider func() string
@@ -18,9 +17,10 @@ type ProxyHandler struct {
upstream string
logger *CondLogger
httptransport http.RoundTripper
resolver *Resolver
}
func NewProxyHandler(upstream string, auth AuthProvider, logger *CondLogger) *ProxyHandler {
func NewProxyHandler(upstream string, auth AuthProvider, resolver *Resolver, logger *CondLogger) *ProxyHandler {
proxyurl, err := url.Parse("https://" + upstream)
if err != nil {
panic(err)
@@ -33,56 +33,183 @@ func NewProxyHandler(upstream string, auth AuthProvider, logger *CondLogger) *Pr
upstream: upstream,
logger: logger,
httptransport: httptransport,
resolver: resolver,
}
}
func (s *ProxyHandler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
s.logger.Info("Request: %v %v %v", req.RemoteAddr, req.Method, req.URL)
req.Header.Set("Proxy-Authorization", s.auth())
if strings.ToUpper(req.Method) == "CONNECT" {
req.Header.Set("Proxy-Authorization", s.auth())
rawreq, err := httputil.DumpRequest(req, false)
if err != nil {
s.logger.Error("Can't dump request: %v", err)
http.Error(wr, "Can't dump request", http.StatusInternalServerError)
return
}
conn, err := tls.Dial("tcp", s.upstream, nil)
if err != nil {
s.logger.Error("Can't dial tls upstream: %v", err)
http.Error(wr, "Can't dial tls upstream", http.StatusInternalServerError)
http.Error(wr, "Can't dial tls upstream", http.StatusBadGateway)
return
defer conn.Close()
}
hj, ok := wr.(http.Hijacker)
if !ok {
s.logger.Critical("Webserver doesn't support hijacking")
http.Error(wr, "Webserver doesn't support hijacking", http.StatusInternalServerError)
return
}
localconn, _, err := hj.Hijack()
_, err = conn.Write(rawreq)
if err != nil {
s.logger.Error("Can't write tls upstream: %v", err)
http.Error(wr, "Can't write tls upstream", http.StatusBadGateway)
return
}
bufrd := bufio.NewReader(conn)
proxyResp, err := http.ReadResponse(bufrd, req)
responseBytes := make([]byte, 0)
if err != nil {
s.logger.Error("Can't read response from upstream: %v", err)
http.Error(wr, "Can't read response from upstream", http.StatusBadGateway)
return
}
if (proxyResp.StatusCode == http.StatusForbidden &&
proxyResp.Header.Get("X-Hola-Error") == "Forbidden Host") {
s.logger.Info("Request %s denied by upstream. Rescuing it with resolve&rewrite workaround.",
req.URL.String())
conn.Close()
conn, err = tls.Dial("tcp", s.upstream, nil)
if err != nil {
s.logger.Error("Can't dial tls upstream: %v", err)
http.Error(wr, "Can't dial tls upstream", http.StatusBadGateway)
return
}
defer conn.Close()
err = rewriteConnectReq(req, s.resolver)
if err != nil {
s.logger.Error("Can't rewrite request: %v", err)
http.Error(wr, "Can't rewrite request", http.StatusInternalServerError)
return
}
rawreq, err = httputil.DumpRequest(req, false)
if err != nil {
s.logger.Error("Can't dump request: %v", err)
http.Error(wr, "Can't dump request", http.StatusInternalServerError)
return
}
_, err = conn.Write(rawreq)
if err != nil {
s.logger.Error("Can't write tls upstream: %v", err)
http.Error(wr, "Can't write tls upstream", http.StatusBadGateway)
return
}
} else {
defer conn.Close()
responseBytes, err = httputil.DumpResponse(proxyResp, false)
if err != nil {
s.logger.Error("Can't dump response: %v", err)
http.Error(wr, "Can't dump response", http.StatusInternalServerError)
return
}
buffered := bufrd.Buffered()
if buffered > 0 {
trailer := make([]byte, buffered)
bufrd.Read(trailer)
responseBytes = append(responseBytes, trailer...)
}
}
bufrd = nil
// Upgrade client connection
localconn, _, err := hijack(wr)
if err != nil {
s.logger.Error("Can't hijack client connection: %v", err)
http.Error(wr, "Can't hijack client connection", http.StatusInternalServerError)
return
}
var emptytime time.Time
err = localconn.SetDeadline(emptytime)
if err != nil {
s.logger.Error("Can't clear deadlines on local connection: %v", err)
http.Error(wr, "Can't clear deadlines on local connection", http.StatusInternalServerError)
return
defer localconn.Close()
if len(responseBytes) > 0 {
_, err = localconn.Write(responseBytes)
if err != nil {
return
}
}
conn.Write(rawreq)
proxy(context.TODO(), localconn, conn)
proxy(req.Context(), localconn, conn)
} else {
req.RequestURI = ""
delHopHeaders(req.Header)
orig_req := req.Clone(req.Context())
req.RequestURI = ""
req.Header.Set("Proxy-Authorization", s.auth())
resp, err := s.httptransport.RoundTrip(req)
if err != nil {
s.logger.Error("HTTP fetch error: %v", err)
http.Error(wr, "Server Error", http.StatusInternalServerError)
return
}
if (resp.StatusCode == http.StatusForbidden &&
resp.Header.Get("X-Hola-Error") == "Forbidden Host") {
s.logger.Info("Request %s denied by upstream. Rescuing it with resolve&tunnel workaround.",
req.URL.String())
resp.Body.Close()
// Prepare tunnel request
proxyReq, err := makeConnReq(orig_req.RequestURI, s.resolver)
if err != nil {
s.logger.Error("Can't rewrite request: %v", err)
http.Error(wr, "Can't rewrite request", http.StatusBadGateway)
return
}
proxyReq.Header.Set("Proxy-Authorization", s.auth())
rawreq, _ := httputil.DumpRequest(proxyReq, false)
// Prepare upstream TLS conn
conn, err := tls.Dial("tcp", s.upstream, nil)
if err != nil {
s.logger.Error("Can't dial tls upstream: %v", err)
http.Error(wr, "Can't dial tls upstream", http.StatusBadGateway)
return
}
defer conn.Close()
// Send proxy request
_, err = conn.Write(rawreq)
if err != nil {
s.logger.Error("Can't write tls upstream: %v", err)
http.Error(wr, "Can't write tls upstream", http.StatusBadGateway)
return
}
// Read proxy response
bufrd := bufio.NewReader(conn)
proxyResp, err := http.ReadResponse(bufrd, proxyReq)
if err != nil {
s.logger.Error("Can't read response from upstream: %v", err)
http.Error(wr, "Can't read response from upstream", http.StatusBadGateway)
return
}
if proxyResp.StatusCode != http.StatusOK {
delHopHeaders(proxyResp.Header)
copyHeader(wr.Header(), proxyResp.Header)
wr.WriteHeader(proxyResp.StatusCode)
}
// Send tunneled request
orig_req.RequestURI = ""
orig_req.Header.Set("Connection", "close")
rawreq, _ = httputil.DumpRequest(orig_req, false)
_, err = conn.Write(rawreq)
if err != nil {
s.logger.Error("Can't write tls upstream: %v", err)
http.Error(wr, "Can't write tls upstream", http.StatusBadGateway)
return
}
// Read tunneled response
resp, err = http.ReadResponse(bufrd, orig_req)
if err != nil {
s.logger.Error("Can't read response from upstream: %v", err)
http.Error(wr, "Can't read response from upstream", http.StatusBadGateway)
return
}
}
defer resp.Body.Close()
s.logger.Info("%v %v %v %v", req.RemoteAddr, req.Method, req.URL, resp.Status)
delHopHeaders(resp.Header)

View File

@@ -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,20 @@ 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")
params.Add("country", country)
} else if proxy_type == "pool" {
params.Add("country", country + ".pool")
} 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 +160,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 +169,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
}

45
main.go
View File

@@ -36,12 +36,13 @@ 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
timeout, rotate time.Duration
proxy_type string
resolver string
}
@@ -56,24 +57,31 @@ 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 or pool")
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")
}
return args
}
func main() {
func run() int {
args := parse_args()
if args.list_countries {
os.Exit(print_countries(args.timeout))
return print_countries(args.timeout)
}
if args.list_proxies {
os.Exit(print_proxies(args.country, args.limit, args.timeout))
return print_proxies(args.country, args.proxy_type, args.limit, args.timeout)
}
logWriter := NewLogWriter(os.Stderr)
@@ -88,19 +96,34 @@ func main() {
proxyLogger := NewCondLogger(log.New(logWriter, "PROXY : ",
log.LstdFlags | log.Lshortfile),
args.verbosity)
mainLogger.Info("Initializing configuration provider...")
auth, tunnels, err := CredService(args.rotate, args.timeout, args.country, credLogger)
mainLogger.Info("Constructing fallback DNS upstream...")
resolver, err := NewResolver(args.resolver, args.timeout)
if err != nil {
os.Exit(4)
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
return 6
}
endpoint, err := get_endpoint(tunnels, args.proxy_type)
mainLogger.Info("Initializing configuration provider...")
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, args.use_trial)
if err != nil {
mainLogger.Critical("Unable to determine proxy endpoint: %v", err)
os.Exit(5)
logWriter.Close()
return 5
}
mainLogger.Info("Endpoint: %s", endpoint)
mainLogger.Info("Starting proxy server...")
handler := NewProxyHandler(endpoint, auth, proxyLogger)
handler := NewProxyHandler(endpoint, auth, resolver, proxyLogger)
err = http.ListenAndServe(args.bind_address, handler)
mainLogger.Critical("Server terminated with a reason: %v", err)
mainLogger.Info("Shutting down...")
return 0
}
func main() {
os.Exit(run())
}

82
resolver.go Normal file
View File

@@ -0,0 +1,82 @@
package main
import (
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
"time"
)
type Resolver struct {
upstream upstream.Upstream
}
const DOT = 0x2e
func NewResolver(address string, timeout time.Duration) (*Resolver, error) {
opts := upstream.Options{Timeout: timeout}
u, err := upstream.AddressToUpstream(address, opts)
if err != nil {
return nil, err
}
return &Resolver{upstream: u}, nil
}
func (r *Resolver) ResolveA(domain string) []string {
res := make([]string, 0)
if len(domain) == 0 {
return res
}
if domain[len(domain)-1] != DOT {
domain = domain + "."
}
req := dns.Msg{}
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = []dns.Question{
{Name: domain, Qtype: dns.TypeA, Qclass: dns.ClassINET},
}
reply, err := r.upstream.Exchange(&req)
if err != nil {
return res
}
for _, rr := range reply.Answer {
if a, ok := rr.(*dns.A); ok {
res = append(res, a.A.String())
}
}
return res
}
func (r *Resolver) ResolveAAAA(domain string) []string {
res := make([]string, 0)
if len(domain) == 0 {
return res
}
if domain[len(domain)-1] != DOT {
domain = domain + "."
}
req := dns.Msg{}
req.Id = dns.Id()
req.RecursionDesired = true
req.Question = []dns.Question{
{Name: domain, Qtype: dns.TypeAAAA, Qclass: dns.ClassINET},
}
reply, err := r.upstream.Exchange(&req)
if err != nil {
return res
}
for _, rr := range reply.Answer {
if a, ok := rr.(*dns.AAAA); ok {
res = append(res, a.AAAA.String())
}
}
return res
}
func (r *Resolver) Resolve(domain string) []string {
res := r.ResolveA(domain)
if len(res) == 0 {
res = r.ResolveAAAA(domain)
}
return res
}

24
snapcraft.yaml Normal file
View File

@@ -0,0 +1,24 @@
name: hola-proxy
version: '1.3.0'
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

119
utils.go
View File

@@ -14,6 +14,8 @@ import (
"strings"
"strconv"
"net/http"
"net/url"
"bufio"
)
func basic_auth_header(login, password string) string {
@@ -60,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
@@ -75,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]})
}
}
@@ -89,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
@@ -99,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" || typ == "pool" {
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")
}
@@ -115,6 +128,7 @@ var hopHeaders = []string{
"Connection",
"Keep-Alive",
"Proxy-Authenticate",
"Proxy-Connection",
"Te", // canonicalized version of "TE"
"Trailers",
"Transfer-Encoding",
@@ -135,3 +149,94 @@ func delHopHeaders(header http.Header) {
}
}
func hijack(hijackable interface{}) (net.Conn, *bufio.ReadWriter, error) {
hj, ok := hijackable.(http.Hijacker)
if !ok {
return nil, nil, errors.New("Connection doesn't support hijacking")
}
conn, rw, err := hj.Hijack()
if err != nil {
return nil, nil, err
}
var emptytime time.Time
err = conn.SetDeadline(emptytime)
if err != nil {
conn.Close()
return nil, nil, err
}
return conn, rw, nil
}
func rewriteConnectReq(req *http.Request, resolver *Resolver) error {
origHost := req.Host
origAddr, origPort, err := net.SplitHostPort(origHost)
if err == nil {
origHost = origAddr
}
addrs := resolver.Resolve(origHost)
if len(addrs) == 0 {
return errors.New("Can't resolve host")
}
if origPort == "" {
req.URL.Host = addrs[0]
req.Host = addrs[0]
req.RequestURI = addrs[0]
} else {
req.URL.Host = net.JoinHostPort(addrs[0], origPort)
req.Host = net.JoinHostPort(addrs[0], origPort)
req.RequestURI = net.JoinHostPort(addrs[0], origPort)
}
return nil
}
func rewriteReq(req *http.Request, resolver *Resolver) error {
origHost := req.URL.Host
origAddr, origPort, err := net.SplitHostPort(origHost)
if err == nil {
origHost = origAddr
}
addrs := resolver.Resolve(origHost)
if len(addrs) == 0 {
return errors.New("Can't resolve host")
}
if origPort == "" {
req.URL.Host = addrs[0]
req.Host = addrs[0]
} else {
req.URL.Host = net.JoinHostPort(addrs[0], origPort)
req.Host = net.JoinHostPort(addrs[0], origPort)
}
req.Header.Set("Host", origHost)
return nil
}
func makeConnReq(uri string, resolver *Resolver) (*http.Request, error) {
parsed_url, err := url.Parse(uri)
if err != nil {
return nil, err
}
origAddr, origPort, err := net.SplitHostPort(parsed_url.Host)
if err != nil {
origAddr = parsed_url.Host
switch strings.ToLower(parsed_url.Scheme) {
case "https":
origPort = "443"
case "http":
origPort = "80"
default:
return nil, errors.New("Unknown scheme")
}
}
addrs := resolver.Resolve(origAddr)
if len(addrs) == 0 {
return nil, errors.New("Can't resolve host")
}
new_uri := net.JoinHostPort(addrs[0], origPort)
req, err := http.NewRequest("CONNECT", "http://" + new_uri, nil)
if err != nil {
return nil, err
}
req.RequestURI = new_uri
req.Host = new_uri
return req, nil
}