add probing resistance support for HTTP proxy
This commit is contained in:
parent
8a1bfdb1cc
commit
1a0098cf83
@ -469,6 +469,7 @@ func (r *route) serve() error {
|
||||
gost.HostsHandlerOption(hosts),
|
||||
gost.RetryHandlerOption(node.GetInt("retry")),
|
||||
gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second),
|
||||
gost.ProbeResistHandlerOption(node.Get("probe_resist")),
|
||||
)
|
||||
|
||||
srv := &gost.Server{Listener: ln}
|
||||
|
@ -41,6 +41,9 @@ func TCPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
|
||||
if raddr == "" {
|
||||
raddr = ":0" // dummy address
|
||||
}
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
@ -136,6 +139,9 @@ func UDPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
||||
group: NewNodeGroup(),
|
||||
}
|
||||
|
||||
if raddr == "" {
|
||||
raddr = ":0" // dummy address
|
||||
}
|
||||
for i, addr := range strings.Split(raddr, ",") {
|
||||
if addr == "" {
|
||||
continue
|
||||
|
32
handler.go
32
handler.go
@ -20,18 +20,19 @@ type Handler interface {
|
||||
|
||||
// HandlerOptions describes the options for Handler.
|
||||
type HandlerOptions struct {
|
||||
Addr string
|
||||
Chain *Chain
|
||||
Users []*url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Whitelist *Permissions
|
||||
Blacklist *Permissions
|
||||
Strategy Strategy
|
||||
Bypass *Bypass
|
||||
Retries int
|
||||
Timeout time.Duration
|
||||
Resolver Resolver
|
||||
Hosts *Hosts
|
||||
Addr string
|
||||
Chain *Chain
|
||||
Users []*url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Whitelist *Permissions
|
||||
Blacklist *Permissions
|
||||
Strategy Strategy
|
||||
Bypass *Bypass
|
||||
Retries int
|
||||
Timeout time.Duration
|
||||
Resolver Resolver
|
||||
Hosts *Hosts
|
||||
ProbeResist string
|
||||
}
|
||||
|
||||
// HandlerOption allows a common way to set handler options.
|
||||
@ -121,6 +122,13 @@ func HostsHandlerOption(hosts *Hosts) HandlerOption {
|
||||
}
|
||||
}
|
||||
|
||||
// ProbeResistHandlerOption adds the probe resistance for HTTP proxy.
|
||||
func ProbeResistHandlerOption(pr string) HandlerOption {
|
||||
return func(opts *HandlerOptions) {
|
||||
opts.ProbeResist = pr
|
||||
}
|
||||
}
|
||||
|
||||
type autoHandler struct {
|
||||
options *HandlerOptions
|
||||
}
|
||||
|
125
http.go
125
http.go
@ -8,6 +8,8 @@ import (
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -111,16 +113,6 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
log.Logf("[http] %s -> %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||
}
|
||||
|
||||
if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||
resp := "HTTP/1.1 400 Bad Request\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
conn.Write([]byte(resp))
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, resp)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// try to get the actual host.
|
||||
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||
if host, err := decodeServerName(v); err == nil {
|
||||
@ -128,25 +120,37 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
resp := &http.Response{
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: http.Header{},
|
||||
}
|
||||
resp.Header.Add("Proxy-Agent", "gost/"+Version)
|
||||
|
||||
if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
|
||||
log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
|
||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
conn.Write(b)
|
||||
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
|
||||
conn.RemoteAddr(), req.Host, req.Host)
|
||||
resp.StatusCode = http.StatusForbidden
|
||||
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||
}
|
||||
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
|
||||
if h.options.Bypass.Contains(req.Host) {
|
||||
log.Logf("[http] [bypass] %s", req.Host)
|
||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
conn.Write(b)
|
||||
resp.StatusCode = http.StatusForbidden
|
||||
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||
}
|
||||
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
|
||||
@ -155,16 +159,78 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
|
||||
}
|
||||
if !authenticate(u, p, h.options.Users...) {
|
||||
log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
||||
conn.Write([]byte(resp))
|
||||
// probing resistance is enabled
|
||||
if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 {
|
||||
switch ss[0] {
|
||||
case "error":
|
||||
resp.StatusCode, _ = strconv.Atoi(ss[1])
|
||||
case "web":
|
||||
url := ss[1]
|
||||
if !strings.HasPrefix(url, "http") {
|
||||
url = "http://" + url
|
||||
}
|
||||
if r, err := http.Get(url); err == nil {
|
||||
resp = r
|
||||
}
|
||||
case "host":
|
||||
cc, err := net.Dial("tcp", ss[1])
|
||||
if err == nil {
|
||||
req.Write(cc)
|
||||
log.Logf("[http] %s <-> %s", conn.LocalAddr(), ss[1])
|
||||
transport(conn, cc)
|
||||
log.Logf("[http] %s >-< %s", conn.LocalAddr(), ss[1])
|
||||
return
|
||||
}
|
||||
case "file":
|
||||
f, _ := os.Open(ss[1])
|
||||
if f != nil {
|
||||
resp.StatusCode = http.StatusOK
|
||||
if finfo, _ := f.Stat(); finfo != nil {
|
||||
resp.ContentLength = finfo.Size()
|
||||
}
|
||||
resp.Body = f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode == 0 {
|
||||
log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
||||
resp.StatusCode = http.StatusProxyAuthRequired
|
||||
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||
} else {
|
||||
resp.Header = http.Header{}
|
||||
resp.Header.Set("Server", "nginx/1.14.1")
|
||||
resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
|
||||
if resp.ContentLength > 0 {
|
||||
resp.Header.Set("Content-Type", "text/html")
|
||||
}
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
resp.Header.Set("Connection", "keep-alive")
|
||||
}
|
||||
}
|
||||
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||
}
|
||||
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Method == "PRI" || (req.Method != http.MethodConnect && req.URL.Scheme != "http") {
|
||||
resp.StatusCode = http.StatusBadRequest
|
||||
|
||||
if Debug {
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||
}
|
||||
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
|
||||
req.Header.Del("Proxy-Authorization")
|
||||
// req.Header.Del("Proxy-Connection")
|
||||
|
||||
host := req.Host
|
||||
if _, port, _ := net.SplitHostPort(host); port == "" {
|
||||
@ -212,13 +278,14 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
||||
|
||||
if err != nil {
|
||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
||||
resp.StatusCode = http.StatusServiceUnavailable
|
||||
|
||||
b := []byte("HTTP/1.1 503 Service unavailable\r\n" +
|
||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
||||
if Debug {
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(b))
|
||||
dump, _ := httputil.DumpResponse(resp, false)
|
||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), host, string(dump))
|
||||
}
|
||||
conn.Write(b)
|
||||
|
||||
resp.Write(conn)
|
||||
return
|
||||
}
|
||||
defer cc.Close()
|
||||
|
6
sni.go
6
sni.go
@ -112,12 +112,12 @@ func (h *sniHandler) Handle(conn net.Conn) {
|
||||
defer cc.Close()
|
||||
|
||||
if _, err := cc.Write(b); err != nil {
|
||||
log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
||||
log.Logf("[sni] %s -> %s : %s", conn.RemoteAddr(), addr, err)
|
||||
}
|
||||
|
||||
log.Logf("[sni] %s <-> %s", cc.LocalAddr(), host)
|
||||
log.Logf("[sni] %s <-> %s", cc.LocalAddr(), addr)
|
||||
transport(conn, cc)
|
||||
log.Logf("[sni] %s >-< %s", cc.LocalAddr(), host)
|
||||
log.Logf("[sni] %s >-< %s", cc.LocalAddr(), addr)
|
||||
}
|
||||
|
||||
// sniSniffConn is a net.Conn that reads from r, fails on Writes,
|
||||
|
Loading…
Reference in New Issue
Block a user