mirror of
https://github.com/Snawoot/hola-proxy.git
synced 2026-04-04 13:48:13 +00:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12b05ac0df | ||
|
|
d798373e66 | ||
|
|
1d8499dd2e | ||
|
|
e2c9711d73 | ||
|
|
5246eff0f5 | ||
|
|
45c6521777 | ||
|
|
db109ce9ad | ||
|
|
16192a36ba | ||
|
|
d75ab999c8 | ||
|
|
a95a6519aa | ||
|
|
5774d898d2 | ||
|
|
8a3b18e784 | ||
|
|
3ebfea6b86 | ||
|
|
92b7a509e9 | ||
|
|
d4b8306cd4 | ||
|
|
335894d533 | ||
|
|
d95706046d | ||
|
|
6fecb99728 | ||
|
|
0d0cbca13e | ||
|
|
3462113454 | ||
|
|
db9499a936 | ||
|
|
1052329228 | ||
|
|
94addea5d4 | ||
|
|
56a10f2a32 | ||
|
|
801178a7de | ||
|
|
6d627de3d7 | ||
|
|
1a9f547cfe | ||
|
|
1984bb06ee | ||
|
|
012d6fbe16 | ||
|
|
73cdcfb701 | ||
|
|
c626c7cdcc |
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
bin/
|
||||
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
|
||||
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal 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"]
|
||||
78
README.md
78
README.md
@@ -1,8 +1,31 @@
|
||||
# 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`).
|
||||
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
|
||||
|
||||
[](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
195
blocked-domains.txt
Normal 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
|
||||
@@ -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
7
go.mod
@@ -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
62
go.sum
@@ -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=
|
||||
|
||||
171
handler.go
171
handler.go
@@ -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)
|
||||
|
||||
27
holaapi.go
27
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,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
45
main.go
@@ -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
82
resolver.go
Normal 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
24
snapcraft.yaml
Normal 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
119
utils.go
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user