add probing resistance support for HTTP2 proxy
This commit is contained in:
parent
02db99b747
commit
8ba90995ae
14
gost.go
14
gost.go
@ -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)
|
||||||
|
}
|
||||||
|
6
http.go
6
http.go
@ -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":
|
||||||
|
111
http2.go
111
http2.go
@ -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...) {
|
||||||
|
// 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 {
|
||||||
|
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)
|
log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target)
|
||||||
w.Header().Set("Proxy-Authenticate", "Basic realm=\"gost\"")
|
resp.StatusCode = http.StatusProxyAuthRequired
|
||||||
w.WriteHeader(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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user