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.HostsHandlerOption(hosts),
|
||||||
gost.RetryHandlerOption(node.GetInt("retry")),
|
gost.RetryHandlerOption(node.GetInt("retry")),
|
||||||
gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second),
|
gost.TimeoutHandlerOption(time.Duration(node.GetInt("timeout"))*time.Second),
|
||||||
|
gost.ProbeResistHandlerOption(node.Get("probe_resist")),
|
||||||
)
|
)
|
||||||
|
|
||||||
srv := &gost.Server{Listener: ln}
|
srv := &gost.Server{Listener: ln}
|
||||||
|
@ -41,6 +41,9 @@ func TCPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
|||||||
group: NewNodeGroup(),
|
group: NewNodeGroup(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if raddr == "" {
|
||||||
|
raddr = ":0" // dummy address
|
||||||
|
}
|
||||||
for i, addr := range strings.Split(raddr, ",") {
|
for i, addr := range strings.Split(raddr, ",") {
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
continue
|
continue
|
||||||
@ -136,6 +139,9 @@ func UDPDirectForwardHandler(raddr string, opts ...HandlerOption) Handler {
|
|||||||
group: NewNodeGroup(),
|
group: NewNodeGroup(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if raddr == "" {
|
||||||
|
raddr = ":0" // dummy address
|
||||||
|
}
|
||||||
for i, addr := range strings.Split(raddr, ",") {
|
for i, addr := range strings.Split(raddr, ",") {
|
||||||
if addr == "" {
|
if addr == "" {
|
||||||
continue
|
continue
|
||||||
|
@ -32,6 +32,7 @@ type HandlerOptions struct {
|
|||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
Resolver Resolver
|
Resolver Resolver
|
||||||
Hosts *Hosts
|
Hosts *Hosts
|
||||||
|
ProbeResist string
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandlerOption allows a common way to set handler options.
|
// 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 {
|
type autoHandler struct {
|
||||||
options *HandlerOptions
|
options *HandlerOptions
|
||||||
}
|
}
|
||||||
|
127
http.go
127
http.go
@ -8,6 +8,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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))
|
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.
|
// try to get the actual host.
|
||||||
if v := req.Header.Get("Gost-Target"); v != "" {
|
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||||
if host, err := decodeServerName(v); err == nil {
|
if host, err := decodeServerName(v); err == nil {
|
||||||
@ -128,25 +120,37 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
|
resp := &http.Response{
|
||||||
log.Logf("[http] Unauthorized to tcp connect to %s", req.Host)
|
ProtoMajor: 1,
|
||||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
ProtoMinor: 1,
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
Header: http.Header{},
|
||||||
conn.Write(b)
|
|
||||||
if Debug {
|
|
||||||
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(b))
|
|
||||||
}
|
}
|
||||||
|
resp.Header.Add("Proxy-Agent", "gost/"+Version)
|
||||||
|
|
||||||
|
if !Can("tcp", req.Host, h.options.Whitelist, h.options.Blacklist) {
|
||||||
|
log.Logf("[http] %s - %s : Unauthorized to tcp connect to %s",
|
||||||
|
conn.RemoteAddr(), req.Host, req.Host)
|
||||||
|
resp.StatusCode = http.StatusForbidden
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Logf("[http] %s <- %s\n%s", conn.RemoteAddr(), req.Host, string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
resp.Write(conn)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.options.Bypass.Contains(req.Host) {
|
if h.options.Bypass.Contains(req.Host) {
|
||||||
log.Logf("[http] [bypass] %s", req.Host)
|
log.Logf("[http] [bypass] %s", req.Host)
|
||||||
b := []byte("HTTP/1.1 403 Forbidden\r\n" +
|
resp.StatusCode = http.StatusForbidden
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n")
|
|
||||||
conn.Write(b)
|
|
||||||
if Debug {
|
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
|
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)
|
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", conn.RemoteAddr(), req.Host, u, p)
|
||||||
}
|
}
|
||||||
if !authenticate(u, p, h.options.Users...) {
|
if !authenticate(u, p, h.options.Users...) {
|
||||||
|
// 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)
|
log.Logf("[http] %s <- %s : proxy authentication required", conn.RemoteAddr(), req.Host)
|
||||||
resp := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
resp.StatusCode = http.StatusProxyAuthRequired
|
||||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||||
"Proxy-Agent: gost/" + Version + "\r\n\r\n"
|
} else {
|
||||||
conn.Write([]byte(resp))
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Del("Proxy-Authorization")
|
req.Header.Del("Proxy-Authorization")
|
||||||
// req.Header.Del("Proxy-Connection")
|
|
||||||
|
|
||||||
host := req.Host
|
host := req.Host
|
||||||
if _, port, _ := net.SplitHostPort(host); port == "" {
|
if _, port, _ := net.SplitHostPort(host); port == "" {
|
||||||
@ -212,13 +278,14 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Logf("[http] %s -> %s : %s", conn.RemoteAddr(), host, err)
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
6
sni.go
6
sni.go
@ -112,12 +112,12 @@ func (h *sniHandler) Handle(conn net.Conn) {
|
|||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
if _, err := cc.Write(b); err != nil {
|
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)
|
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,
|
// sniSniffConn is a net.Conn that reads from r, fails on Writes,
|
||||||
|
Loading…
Reference in New Issue
Block a user