add probing resistance support for HTTP2 proxy

This commit is contained in:
ginuerzh 2018-11-17 22:11:21 +08:00
parent 02db99b747
commit 8ba90995ae
3 changed files with 106 additions and 27 deletions

14
gost.go
View File

@ -7,6 +7,7 @@ import (
"crypto/x509" "crypto/x509"
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/pem" "encoding/pem"
"io"
"math/big" "math/big"
"time" "time"
@ -100,3 +101,16 @@ func generateKeyPair() (rawCert, rawKey []byte, err error) {
return return
} }
type readWriter struct {
r io.Reader
w io.Writer
}
func (rw *readWriter) Read(p []byte) (n int, err error) {
return rw.r.Read(p)
}
func (rw *readWriter) Write(p []byte) (n int, err error) {
return rw.w.Write(p)
}

View File

@ -175,10 +175,12 @@ func (h *httpHandler) handleRequest(conn net.Conn, req *http.Request) {
case "host": case "host":
cc, err := net.Dial("tcp", ss[1]) cc, err := net.Dial("tcp", ss[1])
if err == nil { if err == nil {
defer cc.Close()
req.Write(cc) req.Write(cc)
log.Logf("[http] %s <-> %s", conn.LocalAddr(), ss[1]) log.Logf("[http] %s <-> %s : forward to %s", conn.LocalAddr(), req.Host, ss[1])
transport(conn, cc) transport(conn, cc)
log.Logf("[http] %s >-< %s", conn.LocalAddr(), ss[1]) log.Logf("[http] %s >-< %s : forward to %s", conn.LocalAddr(), req.Host, ss[1])
return return
} }
case "file": case "file":

113
http2.go
View File

@ -2,14 +2,18 @@ package gost
import ( import (
"bufio" "bufio"
"bytes"
"crypto/tls" "crypto/tls"
"encoding/base64" "encoding/base64"
"errors" "errors"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url" "net/url"
"os"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -308,14 +312,79 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
return return
} }
resp := &http.Response{
ProtoMajor: 2,
ProtoMinor: 0,
Header: http.Header{},
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization")) u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization"))
if Debug && (u != "" || p != "") { if Debug && (u != "" || p != "") {
log.Logf("[http] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p) log.Logf("[http] %s - %s : Authorization: '%s' '%s'", r.RemoteAddr, target, u, p)
} }
if !authenticate(u, p, h.options.Users...) { if !authenticate(u, p, h.options.Users...) {
log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target) // probing resistance is enabled
w.Header().Set("Proxy-Authenticate", "Basic realm=\"gost\"") if ss := strings.SplitN(h.options.ProbeResist, ":", 2); len(ss) == 2 {
w.WriteHeader(http.StatusProxyAuthRequired) 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 {
defer cc.Close()
log.Logf("[http2] %s <-> %s : forward to %s", r.RemoteAddr, target, ss[1])
if err := h.forwardRequest(w, r, cc); err != nil {
log.Logf("[http2] %s - %s : %s", r.RemoteAddr, target, err)
}
log.Logf("[http2] %s >-< %s : forward to %s", r.RemoteAddr, target, 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("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target)
resp.StatusCode = http.StatusProxyAuthRequired
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
} else {
w.Header().Del("Proxy-Agent")
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("[http2] %s <- %s\n%s", r.RemoteAddr, target, string(dump))
}
h.writeResponse(w, resp)
resp.Body.Close()
return return
} }
@ -359,47 +428,41 @@ func (h *http2Handler) roundTrip(w http.ResponseWriter, r *http.Request) {
} }
log.Logf("[http2] %s <-> %s", r.RemoteAddr, target) log.Logf("[http2] %s <-> %s", r.RemoteAddr, target)
errc := make(chan error, 2) transport(&readWriter{r: r.Body, w: flushWriter{w}}, cc)
go func() {
_, err := io.Copy(cc, r.Body)
errc <- err
}()
go func() {
_, err := io.Copy(flushWriter{w}, cc)
errc <- err
}()
select {
case <-errc:
// glog.V(LWARNING).Infoln("exit", err)
}
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target) log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
return return
} }
log.Logf("[http2] %s <-> %s", r.RemoteAddr, target) log.Logf("[http2] %s <-> %s", r.RemoteAddr, target)
if err = r.Write(cc); err != nil { if err := h.forwardRequest(w, r, cc); err != nil {
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err) log.Logf("[http2] %s - %s : %s", r.RemoteAddr, target, err)
}
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
}
func (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) {
if err = r.Write(rw); err != nil {
return return
} }
resp, err := http.ReadResponse(bufio.NewReader(cc), r) resp, err := http.ReadResponse(bufio.NewReader(rw), r)
if err != nil { if err != nil {
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
return return
} }
defer resp.Body.Close() defer resp.Body.Close()
return h.writeResponse(w, resp)
}
func (h *http2Handler) writeResponse(w http.ResponseWriter, resp *http.Response) error {
for k, v := range resp.Header { for k, v := range resp.Header {
for _, vv := range v { for _, vv := range v {
w.Header().Add(k, vv) w.Header().Add(k, vv)
} }
} }
w.WriteHeader(resp.StatusCode) w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil { _, err := io.Copy(flushWriter{w}, resp.Body)
log.Logf("[http2] %s <- %s : %s", r.RemoteAddr, target, err) return err
}
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
} }
type http2Listener struct { type http2Listener struct {