mirror of
https://github.com/Snawoot/hola-proxy.git
synced 2026-04-02 08:58:13 +00:00
Add new virt proxy type (unknown use but used by extension for some sites) + gofmt everything
This commit is contained in:
@@ -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)
|
||||
```
|
||||
|
||||
48
condlog.go
48
condlog.go
@@ -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}
|
||||
}
|
||||
|
||||
120
credservice.go
120
credservice.go
@@ -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
|
||||
}
|
||||
|
||||
384
handler.go
384
handler.go
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
270
holaapi.go
270
holaapi.go
@@ -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,148 +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")
|
||||
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)
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
496
iso3166.go
496
iso3166.go
@@ -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",
|
||||
}
|
||||
}
|
||||
|
||||
70
logwriter.go
70
logwriter.go
@@ -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:
|
||||
}
|
||||
}
|
||||
|
||||
197
main.go
197
main.go
@@ -1,129 +1,128 @@
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
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 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
|
||||
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 or virt 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, "dont-use-trial", true, "use regular ports instead of trial 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 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)
|
||||
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())
|
||||
}
|
||||
|
||||
114
resolver.go
114
resolver.go
@@ -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
|
||||
}
|
||||
|
||||
368
utils.go
368
utils.go
@@ -1,125 +1,125 @@
|
||||
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" || typ == "lum" || typ == "pool" {
|
||||
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
|
||||
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" || 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 +150,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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user