Compare commits

...

15 Commits

Author SHA1 Message Date
Vladislav Yarmak
e89c83ce05 bump snap version 2021-03-08 00:08:11 +02:00
Snawoot
2f38f5b4d1 Merge pull request #20 from rany2/master
Use trial ports by default and add new virt proxy-type
2021-03-08 00:06:02 +02:00
rany
a880f1d9e3 Remove localhost shortcut 2021-03-07 22:34:38 +02:00
rany
4c10fac620 simplify bind-address to accept port and make it listen to 127.0.0.1 2021-03-07 21:03:57 +02:00
rany
92f28d996a Add a few more flags for debugging 2021-03-07 20:54:47 +02:00
rany
a106ef9e56 Add new virt proxy type (unknown use but used by extension for some sites) + gofmt everything 2021-03-07 19:57:46 +02:00
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
12 changed files with 1250 additions and 1036 deletions

View File

@@ -4,7 +4,7 @@
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`).
---
@@ -153,6 +153,8 @@ Usage of /home/user/go/bin/hola-proxy:
HTTP proxy listen address (default "127.0.0.1:8080")
-country string
desired proxy location (default "us")
-dont-use-trial
use regular ports instead of trial ports (default true)
-limit uint
amount of proxies in retrieved list (default 3)
-list-countries
@@ -160,15 +162,13 @@ Usage of /home/user/go/bin/hola-proxy:
-list-proxies
output proxy list and exit
-proxy-type string
proxy type: direct or peer or lum (default "direct")
proxy type: direct or peer or lum or virt or pool (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

@@ -1,58 +1,58 @@
package main
import (
"log"
"fmt"
"fmt"
"log"
)
const (
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0
CRITICAL = 50
ERROR = 40
WARNING = 30
INFO = 20
DEBUG = 10
NOTSET = 0
)
type CondLogger struct {
logger *log.Logger
verbosity int
logger *log.Logger
verbosity int
}
func (cl *CondLogger) Log(verb int, format string, v ...interface{}) error {
if verb >= cl.verbosity {
return cl.logger.Output(2, fmt.Sprintf(format, v...))
}
return nil
if verb >= cl.verbosity {
return cl.logger.Output(2, fmt.Sprintf(format, v...))
}
return nil
}
func (cl *CondLogger) log(verb int, format string, v ...interface{}) error {
if verb >= cl.verbosity {
return cl.logger.Output(3, fmt.Sprintf(format, v...))
}
return nil
if verb >= cl.verbosity {
return cl.logger.Output(3, fmt.Sprintf(format, v...))
}
return nil
}
func (cl *CondLogger) Critical(s string, v ...interface{}) error {
return cl.log(CRITICAL, "CRITICAL " + s, v...)
return cl.log(CRITICAL, "CRITICAL "+s, v...)
}
func (cl *CondLogger) Error(s string, v ...interface{}) error {
return cl.log(ERROR, "ERROR " + s, v...)
return cl.log(ERROR, "ERROR "+s, v...)
}
func (cl *CondLogger) Warning(s string, v ...interface{}) error {
return cl.log(WARNING, "WARNING " + s, v...)
return cl.log(WARNING, "WARNING "+s, v...)
}
func (cl *CondLogger) Info(s string, v ...interface{}) error {
return cl.log(INFO, "INFO " + s, v...)
return cl.log(INFO, "INFO "+s, v...)
}
func (cl *CondLogger) Debug(s string, v ...interface{}) error {
return cl.log(DEBUG, "DEBUG " + s, v...)
return cl.log(DEBUG, "DEBUG "+s, v...)
}
func NewCondLogger(logger *log.Logger, verbosity int) *CondLogger {
return &CondLogger{verbosity: verbosity, logger: logger}
return &CondLogger{verbosity: verbosity, logger: logger}
}

View File

@@ -1,71 +1,71 @@
package main
import (
"time"
"sync"
"context"
"context"
"sync"
"time"
)
const DEFAULT_LIST_LIMIT = 3
const API_CALL_ATTEMPTS = 3
func CredService(interval, timeout time.Duration,
country string,
proxytype string,
logger *CondLogger) (auth AuthProvider,
tunnels *ZGetTunnelsResponse,
err error) {
var mux sync.Mutex
var auth_header, user_uuid string
auth = func () (res string) {
(&mux).Lock()
res = auth_header
(&mux).Unlock()
return
}
country string,
proxytype string,
logger *CondLogger) (auth AuthProvider,
tunnels *ZGetTunnelsResponse,
err error) {
var mux sync.Mutex
var auth_header, user_uuid string
auth = func() (res string) {
(&mux).Lock()
res = auth_header
(&mux).Unlock()
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
}
}
if err != nil {
logger.Critical("Configuration bootstrap failed: %v", err)
return
}
auth_header = basic_auth_header(LOGIN_PREFIX + user_uuid,
tunnels.AgentKey)
go func() {
var (
err error
tuns *ZGetTunnelsResponse
user_uuid string
)
ticker := time.NewTicker(interval)
defer ticker.Stop()
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
}
}
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.")
}
}
}()
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
}
}
if err != nil {
logger.Critical("Configuration bootstrap failed: %v", err)
return
}
auth_header = basic_auth_header(LOGIN_PREFIX+user_uuid,
tunnels.AgentKey)
go func() {
var (
err error
tuns *ZGetTunnelsResponse
user_uuid string
)
ticker := time.NewTicker(interval)
defer ticker.Stop()
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
}
}
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.")
}
}
}()
return
}

View File

@@ -1,220 +1,220 @@
package main
import (
"io"
"net/http"
"net/http/httputil"
"crypto/tls"
"strings"
"net/url"
"bufio"
"bufio"
"crypto/tls"
"io"
"net/http"
"net/http/httputil"
"net/url"
"strings"
)
type AuthProvider func() string
type ProxyHandler struct {
auth AuthProvider
upstream string
logger *CondLogger
httptransport http.RoundTripper
resolver *Resolver
auth AuthProvider
upstream string
logger *CondLogger
httptransport http.RoundTripper
resolver *Resolver
}
func NewProxyHandler(upstream string, auth AuthProvider, resolver *Resolver, logger *CondLogger) *ProxyHandler {
proxyurl, err := url.Parse("https://" + upstream)
if err != nil {
panic(err)
}
proxyurl, err := url.Parse("https://" + upstream)
if err != nil {
panic(err)
}
httptransport := &http.Transport{
Proxy: http.ProxyURL(proxyurl),
}
return &ProxyHandler{
auth: auth,
upstream: upstream,
logger: logger,
httptransport: httptransport,
resolver: resolver,
}
Proxy: http.ProxyURL(proxyurl),
}
return &ProxyHandler{
auth: auth,
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)
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
}
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.StatusBadGateway)
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.StatusBadGateway)
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
}
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
}
_, 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
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
}
defer localconn.Close()
// 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
}
defer localconn.Close()
if len(responseBytes) > 0 {
_, err = localconn.Write(responseBytes)
if err != nil {
return
}
}
proxy(req.Context(), localconn, conn)
} else {
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()
if len(responseBytes) > 0 {
_, err = localconn.Write(responseBytes)
if err != nil {
return
}
}
proxy(req.Context(), localconn, conn)
} else {
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 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()
// 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
}
// 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)
}
// 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
}
// 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)
copyHeader(wr.Header(), resp.Header)
wr.WriteHeader(resp.StatusCode)
io.Copy(wr, resp.Body)
}
// 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)
copyHeader(wr.Header(), resp.Header)
wr.WriteHeader(resp.StatusCode)
io.Copy(wr, resp.Body)
}
}

View File

@@ -1,17 +1,17 @@
package main
import (
"context"
"net/http"
"net/url"
"io/ioutil"
"encoding/json"
"encoding/hex"
"github.com/google/uuid"
"bytes"
"strconv"
"math/rand"
"github.com/campoy/unique"
"bytes"
"context"
"encoding/hex"
"encoding/json"
"github.com/campoy/unique"
"github.com/google/uuid"
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"strconv"
)
const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.72 Safari/537.36"
@@ -27,145 +27,150 @@ const LOGIN_PREFIX = "user-uuid-"
type CountryList []string
type BgInitResponse struct {
Ver string `json:"ver"`
Key int64 `json:"key"`
Country string `json:"country"`
Ver string `json:"ver"`
Key int64 `json:"key"`
Country string `json:"country"`
}
type PortMap struct {
Direct uint16 `json:"direct"`
Hola uint16 `json:"hola"`
Peer uint16 `json:"peer"`
Trial uint16 `json:"trial"`
TrialPeer uint16 `json:"trial_peer"`
Direct uint16 `json:"direct"`
Hola uint16 `json:"hola"`
Peer uint16 `json:"peer"`
Trial uint16 `json:"trial"`
TrialPeer uint16 `json:"trial_peer"`
}
type ZGetTunnelsResponse struct {
AgentKey string `json:"agent_key"`
AgentTypes map[string]string `json:"agent_types"`
IPList map[string]string `json:"ip_list"`
Port PortMap `json:"port"`
Protocol map[string]string `json:"protocol"`
Vendor map[string]string `json:"vendor"`
Ztun map[string][]string `json:"ztun"`
AgentKey string `json:"agent_key"`
AgentTypes map[string]string `json:"agent_types"`
IPList map[string]string `json:"ip_list"`
Port PortMap `json:"port"`
Protocol map[string]string `json:"protocol"`
Vendor map[string]string `json:"vendor"`
Ztun map[string][]string `json:"ztun"`
}
func do_req(ctx context.Context, method, url string, query, data url.Values) ([]byte, error) {
var (
client http.Client
req *http.Request
err error
)
if method == "" {
method = "GET"
}
if data == nil {
req, err = http.NewRequestWithContext(ctx, method, url, nil)
} else {
req, err = http.NewRequestWithContext(ctx,
method,
url,
bytes.NewReader([]byte(data.Encode())))
}
if err != nil {
return nil, err
}
if data != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
if query != nil {
req.URL.RawQuery = query.Encode()
}
req.Header.Set("User-Agent", USER_AGENT)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
return body, nil
var (
client http.Client
req *http.Request
err error
)
if method == "" {
method = "GET"
}
if data == nil {
req, err = http.NewRequestWithContext(ctx, method, url, nil)
} else {
req, err = http.NewRequestWithContext(ctx,
method,
url,
bytes.NewReader([]byte(data.Encode())))
}
if err != nil {
return nil, err
}
if data != nil {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
if query != nil {
req.URL.RawQuery = query.Encode()
}
req.Header.Set("User-Agent", USER_AGENT)
resp, err := client.Do(req)
if err != nil {
return nil, err
}
body, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return nil, err
}
return body, nil
}
func VPNCountries(ctx context.Context) (res CountryList, err error) {
params := make(url.Values)
params.Add("browser", EXT_BROWSER)
data, err := do_req(ctx, "", VPN_COUNTRIES_URL, params, nil)
if err != nil {
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
params := make(url.Values)
params.Add("browser", EXT_BROWSER)
data, err := do_req(ctx, "", VPN_COUNTRIES_URL, params, nil)
if err != nil {
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
}
func background_init(ctx context.Context, 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)
if err != nil {
reterr = err
return
}
reterr = json.Unmarshal(resp, &res)
return
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)
if err != nil {
reterr = err
return
}
reterr = json.Unmarshal(resp, &res)
return
}
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)
if proxy_type == "lum" {
params.Add("country", country + ".pool_lum_" + country + "_shared")
} else if proxy_type == "peer" {
params.Add("country", country + ".peer")
} else {
params.Add("country", country)
}
params.Add("limit", strconv.FormatInt(int64(limit), 10))
params.Add("ping_id", strconv.FormatFloat(rand.Float64(), 'f', -1, 64))
params.Add("ext_ver", EXT_VER)
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)
if err != nil {
reterr = err
return
}
reterr = json.Unmarshal(data, &tunnels)
res = &tunnels
return
user_uuid string,
session_key int64,
country string,
proxy_type string,
limit uint) (res *ZGetTunnelsResponse, reterr error) {
var tunnels ZGetTunnelsResponse
params := make(url.Values)
if proxy_type == "lum" {
params.Add("country", country+".pool_lum_"+country+"_shared")
} else if proxy_type == "virt" { // seems to be for brazil and japan only
params.Add("country", country+".pool_virt_pool_"+country)
} 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 { // direct or skip
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)
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)
if err != nil {
reterr = err
return
}
reterr = json.Unmarshal(data, &tunnels)
res = &tunnels
return
}
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[:])
initres, err := background_init(ctx, user_uuid)
if err != nil {
reterr = err
return
}
res, reterr = zgettunnels(ctx, user_uuid, initres.Key, country, proxy_type, limit)
return
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)
if err != nil {
reterr = err
return
}
res, reterr = zgettunnels(ctx, user_uuid, initres.Key, country, proxy_type, limit)
return
}

View File

@@ -3,252 +3,252 @@ package main
var ISO3166 map[string]string
func init() {
ISO3166 = map[string]string{
"AD": "Andorra",
"AE": "United Arab Emirates",
"AF": "Afghanistan",
"AG": "Antigua & Barbuda",
"AI": "Anguilla",
"AL": "Albania",
"AM": "Armenia",
"AN": "Netherlands Antilles",
"AO": "Angola",
"AQ": "Antarctica",
"AR": "Argentina",
"AS": "American Samoa",
"AT": "Austria",
"AU": "Australia",
"AW": "Aruba",
"AZ": "Azerbaijan",
"BA": "Bosnia and Herzegovina",
"BB": "Barbados",
"BD": "Bangladesh",
"BE": "Belgium",
"BF": "Burkina Faso",
"BG": "Bulgaria",
"BH": "Bahrain",
"BI": "Burundi",
"BJ": "Benin",
"BM": "Bermuda",
"BN": "Brunei Darussalam",
"BO": "Bolivia",
"BR": "Brazil",
"BS": "Bahama",
"BT": "Bhutan",
"BU": "Burma (no longer exists)",
"BV": "Bouvet Island",
"BW": "Botswana",
"BY": "Belarus",
"BZ": "Belize",
"CA": "Canada",
"CC": "Cocos (Keeling) Islands",
"CF": "Central African Republic",
"CG": "Congo",
"CH": "Switzerland",
"CI": "Cote D\"ivoire (Ivory Coast)",
"CK": "Cook Iislands",
"CL": "Chile",
"CM": "Cameroon",
"CN": "China",
"CO": "Colombia",
"CR": "Costa Rica",
"CS": "Czechoslovakia (no longer exists)",
"CU": "Cuba",
"CV": "Cape Verde",
"CX": "Christmas Island",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DD": "German Democratic Republic (no longer exists)",
"DE": "Germany",
"DJ": "Djibouti",
"DK": "Denmark",
"DM": "Dominica",
"DO": "Dominican Republic",
"DZ": "Algeria",
"EC": "Ecuador",
"EE": "Estonia",
"EG": "Egypt",
"EH": "Western Sahara",
"ER": "Eritrea",
"ES": "Spain",
"ET": "Ethiopia",
"FI": "Finland",
"FJ": "Fiji",
"FK": "Falkland Islands (Malvinas)",
"FM": "Micronesia",
"FO": "Faroe Islands",
"FR": "France",
"FX": "France, Metropolitan",
"GA": "Gabon",
"GB": "United Kingdom (Great Britain)",
"GD": "Grenada",
"GE": "Georgia",
"GF": "French Guiana",
"GH": "Ghana",
"GI": "Gibraltar",
"GL": "Greenland",
"GM": "Gambia",
"GN": "Guinea",
"GP": "Guadeloupe",
"GQ": "Equatorial Guinea",
"GR": "Greece",
"GS": "South Georgia and the South Sandwich Islands",
"GT": "Guatemala",
"GU": "Guam",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HK": "Hong Kong",
"HM": "Heard & McDonald Islands",
"HN": "Honduras",
"HR": "Croatia",
"HT": "Haiti",
"HU": "Hungary",
"ID": "Indonesia",
"IE": "Ireland",
"IL": "Israel",
"IN": "India",
"IO": "British Indian Ocean Territory",
"IQ": "Iraq",
"IR": "Islamic Republic of Iran",
"IS": "Iceland",
"IT": "Italy",
"JM": "Jamaica",
"JO": "Jordan",
"JP": "Japan",
"KE": "Kenya",
"KG": "Kyrgyzstan",
"KH": "Cambodia",
"KI": "Kiribati",
"KM": "Comoros",
"KN": "St. Kitts and Nevis",
"KP": "Korea, Democratic People\"s Republic of",
"KR": "Korea, Republic of",
"KW": "Kuwait",
"KY": "Cayman Islands",
"KZ": "Kazakhstan",
"LA": "Lao People\"s Democratic Republic",
"LB": "Lebanon",
"LC": "Saint Lucia",
"LI": "Liechtenstein",
"LK": "Sri Lanka",
"LR": "Liberia",
"LS": "Lesotho",
"LT": "Lithuania",
"LU": "Luxembourg",
"LV": "Latvia",
"LY": "Libyan Arab Jamahiriya",
"MA": "Morocco",
"MC": "Monaco",
"MD": "Moldova, Republic of",
"MG": "Madagascar",
"MH": "Marshall Islands",
"ML": "Mali",
"MN": "Mongolia",
"MM": "Myanmar",
"MO": "Macau",
"MP": "Northern Mariana Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MS": "Monserrat",
"MT": "Malta",
"MU": "Mauritius",
"MV": "Maldives",
"MW": "Malawi",
"MX": "Mexico",
"MY": "Malaysia",
"MZ": "Mozambique",
"NA": "Namibia",
"NC": "New Caledonia",
"NE": "Niger",
"NF": "Norfolk Island",
"NG": "Nigeria",
"NI": "Nicaragua",
"NL": "Netherlands",
"NO": "Norway",
"NP": "Nepal",
"NR": "Nauru",
"NT": "Neutral Zone (no longer exists)",
"NU": "Niue",
"NZ": "New Zealand",
"OM": "Oman",
"PA": "Panama",
"PE": "Peru",
"PF": "French Polynesia",
"PG": "Papua New Guinea",
"PH": "Philippines",
"PK": "Pakistan",
"PL": "Poland",
"PM": "St. Pierre & Miquelon",
"PN": "Pitcairn",
"PR": "Puerto Rico",
"PT": "Portugal",
"PW": "Palau",
"PY": "Paraguay",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"SA": "Saudi Arabia",
"SB": "Solomon Islands",
"SC": "Seychelles",
"SD": "Sudan",
"SE": "Sweden",
"SG": "Singapore",
"SH": "St. Helena",
"SI": "Slovenia",
"SJ": "Svalbard & Jan Mayen Islands",
"SK": "Slovakia",
"SL": "Sierra Leone",
"SM": "San Marino",
"SN": "Senegal",
"SO": "Somalia",
"SR": "Suriname",
"ST": "Sao Tome & Principe",
"SU": "Union of Soviet Socialist Republics (no longer exists)",
"SV": "El Salvador",
"SY": "Syrian Arab Republic",
"SZ": "Swaziland",
"TC": "Turks & Caicos Islands",
"TD": "Chad",
"TF": "French Southern Territories",
"TG": "Togo",
"TH": "Thailand",
"TJ": "Tajikistan",
"TK": "Tokelau",
"TM": "Turkmenistan",
"TN": "Tunisia",
"TO": "Tonga",
"TP": "East Timor",
"TR": "Turkey",
"TT": "Trinidad & Tobago",
"TV": "Tuvalu",
"TW": "Taiwan, Province of China",
"TZ": "Tanzania, United Republic of",
"UA": "Ukraine",
"UG": "Uganda",
"UK": "United Kingdom",
"UM": "United States Minor Outlying Islands",
"US": "United States of America",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VA": "Vatican City State (Holy See)",
"VC": "St. Vincent & the Grenadines",
"VE": "Venezuela",
"VG": "British Virgin Islands",
"VI": "United States Virgin Islands",
"VN": "Viet Nam",
"VU": "Vanuatu",
"WF": "Wallis & Futuna Islands",
"WS": "Samoa",
"YD": "Democratic Yemen (no longer exists)",
"YE": "Yemen",
"YT": "Mayotte",
"YU": "Yugoslavia",
"ZA": "South Africa",
"ZM": "Zambia",
"ZR": "Zaire",
"ZW": "Zimbabwe",
"ZZ": "Unknown or unspecified country",
}
ISO3166 = map[string]string{
"AD": "Andorra",
"AE": "United Arab Emirates",
"AF": "Afghanistan",
"AG": "Antigua & Barbuda",
"AI": "Anguilla",
"AL": "Albania",
"AM": "Armenia",
"AN": "Netherlands Antilles",
"AO": "Angola",
"AQ": "Antarctica",
"AR": "Argentina",
"AS": "American Samoa",
"AT": "Austria",
"AU": "Australia",
"AW": "Aruba",
"AZ": "Azerbaijan",
"BA": "Bosnia and Herzegovina",
"BB": "Barbados",
"BD": "Bangladesh",
"BE": "Belgium",
"BF": "Burkina Faso",
"BG": "Bulgaria",
"BH": "Bahrain",
"BI": "Burundi",
"BJ": "Benin",
"BM": "Bermuda",
"BN": "Brunei Darussalam",
"BO": "Bolivia",
"BR": "Brazil",
"BS": "Bahama",
"BT": "Bhutan",
"BU": "Burma (no longer exists)",
"BV": "Bouvet Island",
"BW": "Botswana",
"BY": "Belarus",
"BZ": "Belize",
"CA": "Canada",
"CC": "Cocos (Keeling) Islands",
"CF": "Central African Republic",
"CG": "Congo",
"CH": "Switzerland",
"CI": "Cote D\"ivoire (Ivory Coast)",
"CK": "Cook Iislands",
"CL": "Chile",
"CM": "Cameroon",
"CN": "China",
"CO": "Colombia",
"CR": "Costa Rica",
"CS": "Czechoslovakia (no longer exists)",
"CU": "Cuba",
"CV": "Cape Verde",
"CX": "Christmas Island",
"CY": "Cyprus",
"CZ": "Czech Republic",
"DD": "German Democratic Republic (no longer exists)",
"DE": "Germany",
"DJ": "Djibouti",
"DK": "Denmark",
"DM": "Dominica",
"DO": "Dominican Republic",
"DZ": "Algeria",
"EC": "Ecuador",
"EE": "Estonia",
"EG": "Egypt",
"EH": "Western Sahara",
"ER": "Eritrea",
"ES": "Spain",
"ET": "Ethiopia",
"FI": "Finland",
"FJ": "Fiji",
"FK": "Falkland Islands (Malvinas)",
"FM": "Micronesia",
"FO": "Faroe Islands",
"FR": "France",
"FX": "France, Metropolitan",
"GA": "Gabon",
"GB": "United Kingdom (Great Britain)",
"GD": "Grenada",
"GE": "Georgia",
"GF": "French Guiana",
"GH": "Ghana",
"GI": "Gibraltar",
"GL": "Greenland",
"GM": "Gambia",
"GN": "Guinea",
"GP": "Guadeloupe",
"GQ": "Equatorial Guinea",
"GR": "Greece",
"GS": "South Georgia and the South Sandwich Islands",
"GT": "Guatemala",
"GU": "Guam",
"GW": "Guinea-Bissau",
"GY": "Guyana",
"HK": "Hong Kong",
"HM": "Heard & McDonald Islands",
"HN": "Honduras",
"HR": "Croatia",
"HT": "Haiti",
"HU": "Hungary",
"ID": "Indonesia",
"IE": "Ireland",
"IL": "Israel",
"IN": "India",
"IO": "British Indian Ocean Territory",
"IQ": "Iraq",
"IR": "Islamic Republic of Iran",
"IS": "Iceland",
"IT": "Italy",
"JM": "Jamaica",
"JO": "Jordan",
"JP": "Japan",
"KE": "Kenya",
"KG": "Kyrgyzstan",
"KH": "Cambodia",
"KI": "Kiribati",
"KM": "Comoros",
"KN": "St. Kitts and Nevis",
"KP": "Korea, Democratic People\"s Republic of",
"KR": "Korea, Republic of",
"KW": "Kuwait",
"KY": "Cayman Islands",
"KZ": "Kazakhstan",
"LA": "Lao People\"s Democratic Republic",
"LB": "Lebanon",
"LC": "Saint Lucia",
"LI": "Liechtenstein",
"LK": "Sri Lanka",
"LR": "Liberia",
"LS": "Lesotho",
"LT": "Lithuania",
"LU": "Luxembourg",
"LV": "Latvia",
"LY": "Libyan Arab Jamahiriya",
"MA": "Morocco",
"MC": "Monaco",
"MD": "Moldova, Republic of",
"MG": "Madagascar",
"MH": "Marshall Islands",
"ML": "Mali",
"MN": "Mongolia",
"MM": "Myanmar",
"MO": "Macau",
"MP": "Northern Mariana Islands",
"MQ": "Martinique",
"MR": "Mauritania",
"MS": "Monserrat",
"MT": "Malta",
"MU": "Mauritius",
"MV": "Maldives",
"MW": "Malawi",
"MX": "Mexico",
"MY": "Malaysia",
"MZ": "Mozambique",
"NA": "Namibia",
"NC": "New Caledonia",
"NE": "Niger",
"NF": "Norfolk Island",
"NG": "Nigeria",
"NI": "Nicaragua",
"NL": "Netherlands",
"NO": "Norway",
"NP": "Nepal",
"NR": "Nauru",
"NT": "Neutral Zone (no longer exists)",
"NU": "Niue",
"NZ": "New Zealand",
"OM": "Oman",
"PA": "Panama",
"PE": "Peru",
"PF": "French Polynesia",
"PG": "Papua New Guinea",
"PH": "Philippines",
"PK": "Pakistan",
"PL": "Poland",
"PM": "St. Pierre & Miquelon",
"PN": "Pitcairn",
"PR": "Puerto Rico",
"PT": "Portugal",
"PW": "Palau",
"PY": "Paraguay",
"QA": "Qatar",
"RE": "Reunion",
"RO": "Romania",
"RU": "Russian Federation",
"RW": "Rwanda",
"SA": "Saudi Arabia",
"SB": "Solomon Islands",
"SC": "Seychelles",
"SD": "Sudan",
"SE": "Sweden",
"SG": "Singapore",
"SH": "St. Helena",
"SI": "Slovenia",
"SJ": "Svalbard & Jan Mayen Islands",
"SK": "Slovakia",
"SL": "Sierra Leone",
"SM": "San Marino",
"SN": "Senegal",
"SO": "Somalia",
"SR": "Suriname",
"ST": "Sao Tome & Principe",
"SU": "Union of Soviet Socialist Republics (no longer exists)",
"SV": "El Salvador",
"SY": "Syrian Arab Republic",
"SZ": "Swaziland",
"TC": "Turks & Caicos Islands",
"TD": "Chad",
"TF": "French Southern Territories",
"TG": "Togo",
"TH": "Thailand",
"TJ": "Tajikistan",
"TK": "Tokelau",
"TM": "Turkmenistan",
"TN": "Tunisia",
"TO": "Tonga",
"TP": "East Timor",
"TR": "Turkey",
"TT": "Trinidad & Tobago",
"TV": "Tuvalu",
"TW": "Taiwan, Province of China",
"TZ": "Tanzania, United Republic of",
"UA": "Ukraine",
"UG": "Uganda",
"UK": "United Kingdom",
"UM": "United States Minor Outlying Islands",
"US": "United States of America",
"UY": "Uruguay",
"UZ": "Uzbekistan",
"VA": "Vatican City State (Holy See)",
"VC": "St. Vincent & the Grenadines",
"VE": "Venezuela",
"VG": "British Virgin Islands",
"VI": "United States Virgin Islands",
"VN": "Viet Nam",
"VU": "Vanuatu",
"WF": "Wallis & Futuna Islands",
"WS": "Samoa",
"YD": "Democratic Yemen (no longer exists)",
"YE": "Yemen",
"YT": "Mayotte",
"YU": "Yugoslavia",
"ZA": "South Africa",
"ZM": "Zambia",
"ZR": "Zaire",
"ZW": "Zimbabwe",
"ZZ": "Unknown or unspecified country",
}
}

View File

@@ -1,57 +1,57 @@
package main
import (
"io"
"errors"
"time"
"errors"
"io"
"time"
)
const MAX_LOG_QLEN = 128
const QUEUE_SHUTDOWN_TIMEOUT = 500 * time.Millisecond
type LogWriter struct {
writer io.Writer
ch chan []byte
done chan struct{}
writer io.Writer
ch chan []byte
done chan struct{}
}
func (lw *LogWriter) Write(p []byte) (int, error) {
if p == nil {
return 0, errors.New("Can't write nil byte slice")
}
buf := make([]byte, len(p))
copy(buf, p)
select {
case lw.ch <- buf:
return len(p), nil
default:
return 0, errors.New("Writer queue overflow")
}
if p == nil {
return 0, errors.New("Can't write nil byte slice")
}
buf := make([]byte, len(p))
copy(buf, p)
select {
case lw.ch <- buf:
return len(p), nil
default:
return 0, errors.New("Writer queue overflow")
}
}
func NewLogWriter(writer io.Writer) *LogWriter {
lw := &LogWriter{writer,
make(chan []byte, MAX_LOG_QLEN),
make(chan struct{})}
go lw.loop()
return lw
lw := &LogWriter{writer,
make(chan []byte, MAX_LOG_QLEN),
make(chan struct{})}
go lw.loop()
return lw
}
func (lw *LogWriter) loop() {
for p := range lw.ch {
if p == nil {
break
}
lw.writer.Write(p)
}
lw.done <- struct{}{}
for p := range lw.ch {
if p == nil {
break
}
lw.writer.Write(p)
}
lw.done <- struct{}{}
}
func (lw *LogWriter) Close() {
lw.ch <- nil
timer := time.After(QUEUE_SHUTDOWN_TIMEOUT)
select {
case <-timer:
case <-lw.done:
}
lw.ch <- nil
timer := time.After(QUEUE_SHUTDOWN_TIMEOUT)
select {
case <-timer:
case <-lw.done:
}
}

200
main.go
View File

@@ -1,129 +1,131 @@
package main
import (
"log"
"os"
"fmt"
"flag"
// "os/signal"
// "syscall"
"time"
"net/http"
"flag"
"fmt"
"log"
"os"
// "os/signal"
// "syscall"
"net/http"
"time"
)
var (
PROTOCOL_WHITELIST map[string]bool
PROTOCOL_WHITELIST map[string]bool
)
func init() {
PROTOCOL_WHITELIST = map[string]bool{
"HTTP": true,
"http": true,
}
PROTOCOL_WHITELIST = map[string]bool{
"HTTP": true,
"http": true,
}
}
func perror(msg string) {
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, msg)
fmt.Fprintln(os.Stderr, "")
fmt.Fprintln(os.Stderr, msg)
}
func arg_fail(msg string) {
perror(msg)
perror("Usage:")
flag.PrintDefaults()
os.Exit(2)
perror(msg)
perror("Usage:")
flag.PrintDefaults()
os.Exit(2)
}
type CLIArgs struct {
country string
list_countries, list_proxies, use_trial bool
limit uint
bind_address string
verbosity int
timeout, rotate time.Duration
proxy_type string
resolver string
country string
list_countries, list_proxies, use_trial bool
limit uint
bind_address string
verbosity int
timeout, rotate time.Duration
proxy_type string
resolver string
force_port_field string
}
func parse_args() CLIArgs {
var args CLIArgs
flag.StringVar(&args.country, "country", "us", "desired proxy location")
flag.BoolVar(&args.list_countries, "list-countries", false, "list available countries and exit")
flag.BoolVar(&args.list_proxies, "list-proxies", false, "output proxy list and exit")
flag.UintVar(&args.limit, "limit", 3, "amount of proxies in retrieved list")
flag.StringVar(&args.bind_address, "bind-address", "127.0.0.1:8080", "HTTP proxy listen address")
flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity " +
"(10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical)")
flag.DurationVar(&args.timeout, "timeout", 10 * time.Second, "timeout for network operations")
flag.DurationVar(&args.rotate, "rotate", 1 * time.Hour, "rotate user ID once per given period")
flag.StringVar(&args.proxy_type, "proxy-type", "direct", "proxy type: direct or peer or lum")
flag.StringVar(&args.resolver, "resolver", "https://cloudflare-dns.com/dns-query",
"DNS/DoH/DoT resolver to workaround Hola blocked hosts. " +
"See https://github.com/ameshkov/dnslookup/ for upstream DNS URL format.")
flag.BoolVar(&args.use_trial, "use-trial", false, "use trial ports instead of regular ports")
flag.Parse()
if args.country == "" {
arg_fail("Country can't be empty string.")
}
if args.proxy_type == "" {
arg_fail("Proxy type can't be an empty string.")
}
if args.list_countries && args.list_proxies {
arg_fail("list-countries and list-proxies flags are mutually exclusive")
}
return args
var args CLIArgs
flag.StringVar(&args.force_port_field, "force-port-field", "", "force specific port field/num (example 24232 or lum)") // would be nice to not show in help page
flag.StringVar(&args.country, "country", "us", "desired proxy location")
flag.BoolVar(&args.list_countries, "list-countries", false, "list available countries and exit")
flag.BoolVar(&args.list_proxies, "list-proxies", false, "output proxy list and exit")
flag.UintVar(&args.limit, "limit", 3, "amount of proxies in retrieved list")
flag.StringVar(&args.bind_address, "bind-address", "127.0.0.1:8080", "HTTP proxy listen address")
flag.IntVar(&args.verbosity, "verbosity", 20, "logging verbosity "+
"(10 - debug, 20 - info, 30 - warning, 40 - error, 50 - critical)")
flag.DurationVar(&args.timeout, "timeout", 10*time.Second, "timeout for network operations")
flag.DurationVar(&args.rotate, "rotate", 1*time.Hour, "rotate user ID once per given period")
flag.StringVar(&args.proxy_type, "proxy-type", "direct", "proxy type: direct or peer or lum or virt or pool") // or skip but not mentioned
// skip would be used something like this: `./bin/hola-proxy -proxy-type skip -force-port-field 24232 -country ua.peer` for debugging
flag.StringVar(&args.resolver, "resolver", "https://cloudflare-dns.com/dns-query",
"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", true, "use regular ports instead of trial ports") // would be nice to not show in help page
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 run() int {
args := parse_args()
if args.list_countries {
return print_countries(args.timeout)
}
if args.list_proxies {
return print_proxies(args.country, args.proxy_type, args.limit, args.timeout)
}
args := parse_args()
if args.list_countries {
return print_countries(args.timeout)
}
if args.list_proxies {
return print_proxies(args.country, args.proxy_type, args.limit, args.timeout)
}
logWriter := NewLogWriter(os.Stderr)
defer logWriter.Close()
logWriter := NewLogWriter(os.Stderr)
defer logWriter.Close()
mainLogger := NewCondLogger(log.New(logWriter, "MAIN : ",
log.LstdFlags | log.Lshortfile),
args.verbosity)
credLogger := NewCondLogger(log.New(logWriter, "CRED : ",
log.LstdFlags | log.Lshortfile),
args.verbosity)
proxyLogger := NewCondLogger(log.New(logWriter, "PROXY : ",
log.LstdFlags | log.Lshortfile),
args.verbosity)
mainLogger.Info("Constructing fallback DNS upstream...")
resolver, err := NewResolver(args.resolver, args.timeout)
if err != nil {
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
return 6
}
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)
logWriter.Close()
return 5
}
mainLogger.Info("Endpoint: %s", endpoint)
mainLogger.Info("Starting proxy server...")
handler := NewProxyHandler(endpoint, auth, resolver, proxyLogger)
err = http.ListenAndServe(args.bind_address, handler)
mainLogger.Critical("Server terminated with a reason: %v", err)
mainLogger.Info("Shutting down...")
return 0
mainLogger := NewCondLogger(log.New(logWriter, "MAIN : ",
log.LstdFlags|log.Lshortfile),
args.verbosity)
credLogger := NewCondLogger(log.New(logWriter, "CRED : ",
log.LstdFlags|log.Lshortfile),
args.verbosity)
proxyLogger := NewCondLogger(log.New(logWriter, "PROXY : ",
log.LstdFlags|log.Lshortfile),
args.verbosity)
mainLogger.Info("Constructing fallback DNS upstream...")
resolver, err := NewResolver(args.resolver, args.timeout)
if err != nil {
mainLogger.Critical("Unable to instantiate DNS resolver: %v", err)
return 6
}
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, args.force_port_field)
if err != nil {
mainLogger.Critical("Unable to determine proxy endpoint: %v", err)
logWriter.Close()
return 5
}
mainLogger.Info("Endpoint: %s", endpoint)
mainLogger.Info("Starting proxy server...")
handler := NewProxyHandler(endpoint, auth, resolver, proxyLogger)
err = http.ListenAndServe(args.bind_address, handler)
mainLogger.Critical("Server terminated with a reason: %v", err)
mainLogger.Info("Shutting down...")
return 0
}
func main() {
os.Exit(run())
os.Exit(run())
}

View File

@@ -3,80 +3,80 @@ package main
import (
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
"time"
"time"
)
type Resolver struct {
upstream upstream.Upstream
upstream upstream.Upstream
}
const DOT = 0x2e
func NewResolver(address string, timeout time.Duration) (*Resolver, error) {
opts := upstream.Options{Timeout: timeout}
opts := upstream.Options{Timeout: timeout}
u, err := upstream.AddressToUpstream(address, opts)
if err != nil {
return nil, err
}
return &Resolver{upstream: u}, 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
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
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
res := r.ResolveA(domain)
if len(res) == 0 {
res = r.ResolveAAAA(domain)
}
return res
}

View File

@@ -1,5 +1,5 @@
name: hola-proxy
version: '1.1.2'
version: '1.4.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.

382
utils.go
View File

@@ -1,125 +1,137 @@
package main
import (
"fmt"
"context"
"net"
"sync"
"io"
"os"
"time"
"encoding/base64"
"encoding/csv"
"errors"
"strings"
"strconv"
"net/http"
"net/url"
"bufio"
"bufio"
"context"
"encoding/base64"
"encoding/csv"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"sync"
"time"
)
func basic_auth_header(login, password string) string {
return "basic " + base64.StdEncoding.EncodeToString(
[]byte(login + ":" + password))
return "basic " + base64.StdEncoding.EncodeToString(
[]byte(login+":"+password))
}
func proxy(ctx context.Context, left, right net.Conn) {
wg := sync.WaitGroup{}
cpy := func (dst, src net.Conn) {
defer wg.Done()
io.Copy(dst, src)
dst.Close()
}
wg.Add(2)
go cpy(left, right)
go cpy(right, left)
groupdone := make(chan struct{})
go func() {
wg.Wait()
groupdone <-struct{}{}
}()
select {
case <-ctx.Done():
left.Close()
right.Close()
case <-groupdone:
return
}
<-groupdone
return
wg := sync.WaitGroup{}
cpy := func(dst, src net.Conn) {
defer wg.Done()
io.Copy(dst, src)
dst.Close()
}
wg.Add(2)
go cpy(left, right)
go cpy(right, left)
groupdone := make(chan struct{})
go func() {
wg.Wait()
groupdone <- struct{}{}
}()
select {
case <-ctx.Done():
left.Close()
right.Close()
case <-groupdone:
return
}
<-groupdone
return
}
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)
return 3
}
for _, code := range countries {
fmt.Printf("%v - %v\n", code, ISO3166[strings.ToUpper(code)])
}
return 0
ctx, _ := context.WithTimeout(context.Background(), timeout)
countries, err := VPNCountries(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
return 3
}
for _, code := range countries {
fmt.Printf("%v - %v\n", code, ISO3166[strings.ToUpper(code)])
}
return 0
}
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)
return 3
}
wr := csv.NewWriter(os.Stdout)
login := LOGIN_PREFIX + user_uuid
password := tunnels.AgentKey
fmt.Println("Login:", login)
fmt.Println("Password:", password)
fmt.Println("Proxy-Authorization:",
basic_auth_header(login, password))
fmt.Println("")
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]})
}
}
wr.Flush()
return 0
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)
return 3
}
wr := csv.NewWriter(os.Stdout)
login := LOGIN_PREFIX + user_uuid
password := tunnels.AgentKey
fmt.Println("Login:", login)
fmt.Println("Password:", password)
fmt.Println("Proxy-Authorization:",
basic_auth_header(login, password))
fmt.Println("")
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]})
}
}
wr.Flush()
return 0
}
func get_endpoint(tunnels *ZGetTunnelsResponse, typ string, trial bool) (string, error) {
var hostname string
for k, _ := range tunnels.IPList {
hostname = k
break
}
if hostname == "" {
return "", errors.New("No tunnels found in API response")
}
var port uint16
if typ == "direct" {
if trial {
port = tunnels.Port.Trial
} else {
port = tunnels.Port.Direct
}
} else if typ == "peer" || typ == "lum" {
if trial {
port = tunnels.Port.TrialPeer
} else {
port = tunnels.Port.Peer
}
} else {
return "", errors.New("Unsupported port type")
}
return net.JoinHostPort(hostname, strconv.FormatUint(uint64(port), 10)), nil
func get_endpoint(tunnels *ZGetTunnelsResponse, typ string, trial bool, force_port_field string) (string, error) {
var hostname string
for k := range tunnels.IPList {
hostname = k
break
}
if hostname == "" {
return "", errors.New("No tunnels found in API response")
}
var port uint16
if force_port_field != "" {
port2, err := strconv.ParseUint(force_port_field, 0, 16)
if err == nil {
port = (uint16)(port2)
typ = "skip"
} else {
typ = force_port_field
}
}
if typ != "skip" {
if typ == "direct" || typ == "lum" || typ == "pool" || typ == "virt" {
if trial {
port = tunnels.Port.Trial
} else {
port = tunnels.Port.Direct
}
} else if typ == "peer" {
if trial {
port = tunnels.Port.TrialPeer
} else {
port = tunnels.Port.Peer
}
} else {
return "", errors.New("Unsupported port type")
}
}
return net.JoinHostPort(hostname, strconv.FormatUint(uint64(port), 10)), nil
}
// Hop-by-hop headers. These are removed when sent to the backend.
@@ -150,93 +162,93 @@ 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
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
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
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
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
}