add HTTP2 proxy support
This commit is contained in:
parent
1c76ca4b26
commit
346c2c27e5
@ -2,9 +2,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"crypto/tls"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/ginuerzh/gost/gost"
|
"github.com/ginuerzh/gost/gost"
|
||||||
)
|
)
|
||||||
@ -85,36 +89,55 @@ func main() {
|
|||||||
},
|
},
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// http+kcp
|
// http2
|
||||||
gost.Node{
|
gost.Node{
|
||||||
Addr: "127.0.0.1:8388",
|
Addr: "127.0.0.1:1443",
|
||||||
Client: gost.NewClient(
|
Client: gost.NewClient(
|
||||||
gost.HTTPConnector(nil),
|
gost.HTTP2Connector(url.UserPassword("admin", "123456")),
|
||||||
gost.KCPTransporter(nil),
|
gost.HTTP2Transporter(
|
||||||
|
nil,
|
||||||
|
&tls.Config{InsecureSkipVerify: true},
|
||||||
|
time.Second*60,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
// http+kcp
|
||||||
|
gost.Node{
|
||||||
|
Addr: "127.0.0.1:8388",
|
||||||
|
Client: gost.NewClient(
|
||||||
|
gost.HTTPConnector(nil),
|
||||||
|
gost.KCPTransporter(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
*/
|
||||||
)
|
)
|
||||||
|
|
||||||
conn, err := chain.Dial("localhost:10000")
|
for i := 0; i < 10; i++ {
|
||||||
if err != nil {
|
conn, err := chain.Dial("localhost:10000")
|
||||||
log.Fatal(err)
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
}
|
||||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:10000/pkg", nil)
|
//conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
|
||||||
if err != nil {
|
req, err := http.NewRequest(http.MethodGet, "http://localhost:10000/pkg", nil)
|
||||||
log.Fatal(err)
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
if err := req.Write(conn); err != nil {
|
}
|
||||||
log.Fatal(err)
|
if err := req.Write(conn); err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
}
|
||||||
if err != nil {
|
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
|
||||||
log.Fatal(err)
|
if err != nil {
|
||||||
}
|
log.Fatal(err)
|
||||||
defer resp.Body.Close()
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
rb, _ := httputil.DumpRequest(req, true)
|
rb, _ := httputil.DumpRequest(req, true)
|
||||||
log.Println(string(rb))
|
log.Println(string(rb))
|
||||||
rb, _ = httputil.DumpResponse(resp, true)
|
rb, _ = httputil.DumpResponse(resp, true)
|
||||||
log.Println(string(rb))
|
log.Println(string(rb))
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ type Connector interface {
|
|||||||
type Transporter interface {
|
type Transporter interface {
|
||||||
Dial(addr string) (net.Conn, error)
|
Dial(addr string) (net.Conn, error)
|
||||||
Handshake(conn net.Conn) (net.Conn, error)
|
Handshake(conn net.Conn) (net.Conn, error)
|
||||||
|
// Indicate that the Transporter supports multiplex
|
||||||
|
Multiplex() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpTransporter struct {
|
type tcpTransporter struct {
|
||||||
@ -80,3 +82,7 @@ func (tr *tcpTransporter) Dial(addr string) (net.Conn, error) {
|
|||||||
func (tr *tcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
func (tr *tcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tr *tcpTransporter) Multiplex() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -28,6 +28,10 @@ var (
|
|||||||
ReadTimeout = 30 * time.Second
|
ReadTimeout = 30 * time.Second
|
||||||
// WriteTimeout is the timeout for writing.
|
// WriteTimeout is the timeout for writing.
|
||||||
WriteTimeout = 60 * time.Second
|
WriteTimeout = 60 * time.Second
|
||||||
|
// PingTimeout is the timeout for pinging.
|
||||||
|
PingTimeout = 30 * time.Second
|
||||||
|
// PingRetries is the reties of ping.
|
||||||
|
PingRetries = 3
|
||||||
// default udp node TTL in second for udp port forwarding.
|
// default udp node TTL in second for udp port forwarding.
|
||||||
defaultTTL = 60
|
defaultTTL = 60
|
||||||
)
|
)
|
||||||
|
36
gost/http.go
36
gost/http.go
@ -110,21 +110,8 @@ func (h *httpHandler) Handle(conn net.Conn) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
valid := false
|
u, p, _ := basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
||||||
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"))
|
if !authenticate(u, p, h.options.Users...) {
|
||||||
users := h.options.Users
|
|
||||||
for _, user := range users {
|
|
||||||
username := user.Username()
|
|
||||||
password, _ := user.Password()
|
|
||||||
if (u == username && p == password) ||
|
|
||||||
(u == username && password == "") ||
|
|
||||||
(username == "" && p == password) {
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(users) > 0 && !valid {
|
|
||||||
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 := "HTTP/1.1 407 Proxy Authentication Required\r\n" +
|
||||||
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
"Proxy-Authenticate: Basic realm=\"gost\"\r\n" +
|
||||||
@ -182,7 +169,7 @@ func (h *httpHandler) Handle(conn net.Conn) {
|
|||||||
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
log.Logf("[http] %s >-< %s", conn.RemoteAddr(), req.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
|
||||||
if proxyAuth == "" {
|
if proxyAuth == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -202,3 +189,20 @@ func (h *httpHandler) basicProxyAuth(proxyAuth string) (username, password strin
|
|||||||
|
|
||||||
return cs[:s], cs[s+1:], true
|
return cs[:s], cs[s+1:], true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authenticate(username, password string, users ...*url.Userinfo) bool {
|
||||||
|
if len(users) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, user := range users {
|
||||||
|
u := user.Username()
|
||||||
|
p, _ := user.Password()
|
||||||
|
if (u == username && p == password) ||
|
||||||
|
(u == username && p == "") ||
|
||||||
|
(u == "" && p == password) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
444
gost/http2.go
Normal file
444
gost/http2.go
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
package gost
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"crypto/tls"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httputil"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-log/log"
|
||||||
|
"golang.org/x/net/http2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type http2Connector struct {
|
||||||
|
User *url.Userinfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2Connector creates a Connector for HTTP2 proxy client.
|
||||||
|
// It accepts an optional auth info for HTTP Basic Authentication.
|
||||||
|
func HTTP2Connector(user *url.Userinfo) Connector {
|
||||||
|
return &http2Connector{User: user}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Connector) Connect(conn net.Conn, addr string) (net.Conn, error) {
|
||||||
|
cc, ok := conn.(*http2DummyConn)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("conn must be a conn wrapper")
|
||||||
|
}
|
||||||
|
|
||||||
|
pr, pw := io.Pipe()
|
||||||
|
u := &url.URL{
|
||||||
|
Host: addr,
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("CONNECT", u.String(), ioutil.NopCloser(pr))
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http2] %s - %s : %s", cc.raddr, addr, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if c.User != nil {
|
||||||
|
req.Header.Set("Proxy-Authorization",
|
||||||
|
"Basic "+base64.StdEncoding.EncodeToString([]byte(c.User.String())))
|
||||||
|
}
|
||||||
|
req.ProtoMajor = 2
|
||||||
|
req.ProtoMinor = 0
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
dump, _ := httputil.DumpRequest(req, false)
|
||||||
|
log.Log("[http2]", string(dump))
|
||||||
|
}
|
||||||
|
resp, err := cc.conn.RoundTrip(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http2] %s - %s : %s", cc.raddr, addr, err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
dump, _ := httputil.DumpResponse(resp, false)
|
||||||
|
log.Log("[http2]", string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
resp.Body.Close()
|
||||||
|
return nil, errors.New(resp.Status)
|
||||||
|
}
|
||||||
|
hc := &http2Conn{r: resp.Body, w: pw}
|
||||||
|
hc.remoteAddr, _ = net.ResolveTCPAddr("tcp", cc.raddr)
|
||||||
|
return hc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type http2Transporter struct {
|
||||||
|
tlsConfig *tls.Config
|
||||||
|
tr *http2.Transport
|
||||||
|
chain *Chain
|
||||||
|
conns map[string]*http2.ClientConn
|
||||||
|
connMutex sync.Mutex
|
||||||
|
pingInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2Transporter creates a Transporter that is used by HTTP2 proxy client.
|
||||||
|
//
|
||||||
|
// Optional chain is a proxy chain that can be used to establish a connection with the HTTP2 server.
|
||||||
|
//
|
||||||
|
// Optional config is a TLS config for TLS handshake, if is nil, will use h2c mode.
|
||||||
|
//
|
||||||
|
// Optional ping is the ping interval, if is zero, ping will not be enabled.
|
||||||
|
func HTTP2Transporter(chain *Chain, config *tls.Config, ping time.Duration) Transporter {
|
||||||
|
return &http2Transporter{
|
||||||
|
tlsConfig: config,
|
||||||
|
tr: new(http2.Transport),
|
||||||
|
chain: chain,
|
||||||
|
pingInterval: ping,
|
||||||
|
conns: make(map[string]*http2.ClientConn),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *http2Transporter) Dial(addr string) (net.Conn, error) {
|
||||||
|
tr.connMutex.Lock()
|
||||||
|
conn, ok := tr.conns[addr]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
cc, err := tr.chain.Dial(addr)
|
||||||
|
if err != nil {
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if tr.tlsConfig != nil {
|
||||||
|
tc := tls.Client(cc, tr.tlsConfig)
|
||||||
|
if err := tc.Handshake(); err != nil {
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
cc = tc
|
||||||
|
}
|
||||||
|
conn, err = tr.tr.NewClientConn(cc)
|
||||||
|
if err != nil {
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tr.conns[addr] = conn
|
||||||
|
go tr.ping(tr.pingInterval, addr, conn)
|
||||||
|
}
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
|
||||||
|
if !conn.CanTakeNewRequest() {
|
||||||
|
tr.connMutex.Lock()
|
||||||
|
delete(tr.conns, addr) // TODO: we could re-connect to the addr automatically.
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
return nil, errors.New("connection is dead")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http2DummyConn{
|
||||||
|
raddr: addr,
|
||||||
|
conn: conn,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *http2Transporter) ping(interval time.Duration, addr string, conn *http2.ClientConn) {
|
||||||
|
if interval <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Log("[http2] ping is enabled, interval:", interval)
|
||||||
|
|
||||||
|
baseCtx := context.Background()
|
||||||
|
t := time.NewTicker(interval)
|
||||||
|
retries := PingRetries
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-t.C:
|
||||||
|
if !conn.CanTakeNewRequest() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithTimeout(baseCtx, PingTimeout)
|
||||||
|
if err := conn.Ping(ctx); err != nil {
|
||||||
|
log.Logf("[http2] ping: %s", err)
|
||||||
|
if retries > 0 {
|
||||||
|
retries--
|
||||||
|
log.Log("[http2] retry ping")
|
||||||
|
cancel()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// connection is dead, remove it.
|
||||||
|
tr.connMutex.Lock()
|
||||||
|
delete(tr.conns, addr)
|
||||||
|
tr.connMutex.Unlock()
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
retries = PingRetries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *http2Transporter) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *http2Transporter) Multiplex() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type http2Handler struct {
|
||||||
|
server *http2.Server
|
||||||
|
options *HandlerOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2Handler creates a server Handler for HTTP2 proxy server.
|
||||||
|
func HTTP2Handler(opts ...HandlerOption) Handler {
|
||||||
|
h := &http2Handler{
|
||||||
|
server: new(http2.Server),
|
||||||
|
options: &HandlerOptions{
|
||||||
|
Chain: new(Chain),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(h.options)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *http2Handler) Handle(conn net.Conn) {
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if tc, ok := conn.(*tls.Conn); ok {
|
||||||
|
// NOTE: HTTP2 server will check the TLS version,
|
||||||
|
// so we must ensure that the TLS connection is handshake completed.
|
||||||
|
if err := tc.Handshake(); err != nil {
|
||||||
|
log.Logf("[http2] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
opt := http2.ServeConnOpts{
|
||||||
|
Handler: http.HandlerFunc(h.handleFunc),
|
||||||
|
}
|
||||||
|
h.server.ServeConn(conn, &opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *http2Handler) handleFunc(w http.ResponseWriter, r *http.Request) {
|
||||||
|
target := r.Header.Get("Gost-Target") // compitable with old version
|
||||||
|
if target == "" {
|
||||||
|
target = r.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Logf("[http2] %s %s - %s %s", r.Method, r.RemoteAddr, target, r.Proto)
|
||||||
|
if Debug {
|
||||||
|
dump, _ := httputil.DumpRequest(r, false)
|
||||||
|
log.Log("[http2]", string(dump))
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Proxy-Agent", "gost/"+Version)
|
||||||
|
|
||||||
|
//! if !s.Base.Node.Can("tcp", target) {
|
||||||
|
//! glog.Errorf("Unauthorized to tcp connect to %s", target)
|
||||||
|
//! return
|
||||||
|
//! }
|
||||||
|
|
||||||
|
u, p, _ := basicProxyAuth(r.Header.Get("Proxy-Authorization"))
|
||||||
|
if !authenticate(u, p, h.options.Users...) {
|
||||||
|
log.Logf("[http2] %s <- %s : proxy authentication required", r.RemoteAddr, target)
|
||||||
|
w.Header().Set("Proxy-Authenticate", "Basic realm=\"gost\"")
|
||||||
|
w.WriteHeader(http.StatusProxyAuthRequired)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Header.Del("Proxy-Authorization")
|
||||||
|
r.Header.Del("Proxy-Connection")
|
||||||
|
|
||||||
|
cc, err := h.options.Chain.Dial(target)
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||||
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer cc.Close()
|
||||||
|
|
||||||
|
log.Logf("[http2] %s <-> %s", r.RemoteAddr, target)
|
||||||
|
|
||||||
|
if r.Method == http.MethodConnect {
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
if fw, ok := w.(http.Flusher); ok {
|
||||||
|
fw.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// compatible with HTTP1.x
|
||||||
|
if hj, ok := w.(http.Hijacker); ok && r.ProtoMajor == 1 {
|
||||||
|
// we take over the underly connection
|
||||||
|
conn, _, err := hj.Hijack()
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
log.Logf("[http2] %s -> %s : downgrade to HTTP/1.1", r.RemoteAddr, target)
|
||||||
|
transport(conn, cc)
|
||||||
|
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errc := make(chan error, 2)
|
||||||
|
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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = r.Write(cc); err != nil {
|
||||||
|
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(cc), r)
|
||||||
|
if err != nil {
|
||||||
|
log.Logf("[http2] %s -> %s : %s", r.RemoteAddr, target, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
for k, v := range resp.Header {
|
||||||
|
for _, vv := range v {
|
||||||
|
w.Header().Add(k, vv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.WriteHeader(resp.StatusCode)
|
||||||
|
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
|
||||||
|
log.Logf("[http2] %s <- %s : %s", r.RemoteAddr, target, err)
|
||||||
|
}
|
||||||
|
log.Logf("[http2] %s >-< %s", r.RemoteAddr, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP2 connection, wrapped up just like a net.Conn
|
||||||
|
type http2Conn struct {
|
||||||
|
r io.Reader
|
||||||
|
w io.Writer
|
||||||
|
remoteAddr net.Addr
|
||||||
|
localAddr net.Addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) Read(b []byte) (n int, err error) {
|
||||||
|
return c.r.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) Write(b []byte) (n int, err error) {
|
||||||
|
return c.w.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) Close() (err error) {
|
||||||
|
if rc, ok := c.r.(io.Closer); ok {
|
||||||
|
err = rc.Close()
|
||||||
|
}
|
||||||
|
if w, ok := c.w.(io.Closer); ok {
|
||||||
|
err = w.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) LocalAddr() net.Addr {
|
||||||
|
return c.localAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) RemoteAddr() net.Addr {
|
||||||
|
return c.remoteAddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) SetDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) SetReadDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2Conn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy HTTP2 connection.
|
||||||
|
type http2DummyConn struct {
|
||||||
|
raddr string
|
||||||
|
conn *http2.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) Read(b []byte) (n int, err error) {
|
||||||
|
return 0, &net.OpError{Op: "read", Net: "http2", Source: nil, Addr: nil, Err: errors.New("read not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) Write(b []byte) (n int, err error) {
|
||||||
|
return 0, &net.OpError{Op: "write", Net: "http2", Source: nil, Addr: nil, Err: errors.New("write not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) LocalAddr() net.Addr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) RemoteAddr() net.Addr {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) SetDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *http2DummyConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
|
||||||
|
}
|
||||||
|
|
||||||
|
type flushWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fw flushWriter) Write(p []byte) (n int, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if s, ok := r.(string); ok {
|
||||||
|
err = errors.New(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = r.(error)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
n, err = fw.w.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
// log.Log("flush writer:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if f, ok := fw.w.(http.Flusher); ok {
|
||||||
|
f.Flush()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -196,7 +196,7 @@ func (tr *kcpTransporter) Dial(addr string) (conn net.Conn, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
tr.sessionMutex.Lock()
|
tr.sessionMutex.Lock()
|
||||||
session.Close()
|
session.Close()
|
||||||
delete(tr.sessions, addr)
|
delete(tr.sessions, addr) // TODO: we could obtain a new session automatically.
|
||||||
tr.sessionMutex.Unlock()
|
tr.sessionMutex.Unlock()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -245,6 +245,10 @@ func (tr *kcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tr *kcpTransporter) Multiplex() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
type kcpListener struct {
|
type kcpListener struct {
|
||||||
config *KCPConfig
|
config *KCPConfig
|
||||||
ln *kcp.Listener
|
ln *kcp.Listener
|
||||||
|
@ -15,17 +15,18 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go httpServer()
|
// go httpServer()
|
||||||
go socks5Server()
|
// go socks5Server()
|
||||||
go tlsServer()
|
// go tlsServer()
|
||||||
go shadowServer()
|
// go shadowServer()
|
||||||
go wsServer()
|
// go wsServer()
|
||||||
go wssServer()
|
// go wssServer()
|
||||||
go kcpServer()
|
// go kcpServer()
|
||||||
go tcpForwardServer()
|
// go tcpForwardServer()
|
||||||
go rtcpForwardServer()
|
// go rtcpForwardServer()
|
||||||
// go rudpForwardServer()
|
// go rudpForwardServer()
|
||||||
go tcpRedirectServer()
|
// go tcpRedirectServer()
|
||||||
|
go http2Server()
|
||||||
|
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
@ -43,15 +44,10 @@ func httpServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func socks5Server() {
|
func socks5Server() {
|
||||||
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &gost.Server{}
|
s := &gost.Server{}
|
||||||
s.Handle(gost.SOCKS5Handler(
|
s.Handle(gost.SOCKS5Handler(
|
||||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||||
gost.TLSConfigHandlerOption(&tls.Config{Certificates: []tls.Certificate{cert}}),
|
gost.TLSConfigHandlerOption(tlsConfig()),
|
||||||
))
|
))
|
||||||
ln, err := gost.TCPListener(":1080")
|
ln, err := gost.TCPListener(":1080")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -77,11 +73,7 @@ func tlsServer() {
|
|||||||
s.Handle(gost.HTTPHandler(
|
s.Handle(gost.HTTPHandler(
|
||||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||||
))
|
))
|
||||||
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
|
ln, err := gost.TLSListener(":1443", tlsConfig())
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
ln, err := gost.TLSListener(":1443", &tls.Config{Certificates: []tls.Certificate{cert}})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -105,12 +97,7 @@ func wssServer() {
|
|||||||
s.Handle(gost.HTTPHandler(
|
s.Handle(gost.HTTPHandler(
|
||||||
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||||
))
|
))
|
||||||
|
ln, err := gost.WSSListener(":8443", &gost.WSOptions{TLSConfig: tlsConfig()})
|
||||||
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
ln, err := gost.WSSListener(":8443", &gost.WSOptions{TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -194,3 +181,26 @@ func tcpRedirectServer() {
|
|||||||
}
|
}
|
||||||
log.Fatal(s.Serve(ln))
|
log.Fatal(s.Serve(ln))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func http2Server() {
|
||||||
|
// http2.VerboseLogs = true
|
||||||
|
|
||||||
|
s := &gost.Server{}
|
||||||
|
s.Handle(gost.HTTP2Handler(
|
||||||
|
gost.UsersHandlerOption(url.UserPassword("admin", "123456")),
|
||||||
|
))
|
||||||
|
ln, err := gost.TLSListener(":1443", tlsConfig())
|
||||||
|
// ln, err := gost.TCPListener(":1443")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.Fatal(s.Serve(ln))
|
||||||
|
}
|
||||||
|
|
||||||
|
func tlsConfig() *tls.Config {
|
||||||
|
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &tls.Config{Certificates: []tls.Certificate{cert}}
|
||||||
|
}
|
||||||
|
1
gost/ssh.go
Normal file
1
gost/ssh.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package gost
|
12
gost/tls.go
12
gost/tls.go
@ -6,13 +6,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type tlsTransporter struct {
|
type tlsTransporter struct {
|
||||||
TLSClientConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
// TLSTransporter creates a Transporter that is used by TLS proxy client.
|
// TLSTransporter creates a Transporter that is used by TLS proxy client.
|
||||||
// It accepts a TLS config for TLS handshake.
|
// It accepts a TLS config for TLS handshake.
|
||||||
func TLSTransporter(cfg *tls.Config) Transporter {
|
func TLSTransporter(cfg *tls.Config) Transporter {
|
||||||
return &tlsTransporter{TLSClientConfig: cfg}
|
return &tlsTransporter{tlsConfig: cfg}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *tlsTransporter) Dial(addr string) (net.Conn, error) {
|
func (tr *tlsTransporter) Dial(addr string) (net.Conn, error) {
|
||||||
@ -20,7 +20,11 @@ func (tr *tlsTransporter) Dial(addr string) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tr *tlsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
func (tr *tlsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
||||||
return tls.Client(conn, tr.TLSClientConfig), nil
|
return tls.Client(conn, tr.tlsConfig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tlsTransporter) Multiplex() bool {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type tlsListener struct {
|
type tlsListener struct {
|
||||||
@ -33,5 +37,5 @@ func TLSListener(addr string, config *tls.Config) (Listener, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &tlsListener{Listener: ln}, nil
|
return &tlsListener{ln}, nil
|
||||||
}
|
}
|
||||||
|
@ -120,6 +120,10 @@ func (tr *wsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
return websocketClientConn(url.String(), conn, tr.options)
|
return websocketClientConn(url.String(), conn, tr.options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tr *wsTransporter) Multiplex() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type wssTransporter struct {
|
type wssTransporter struct {
|
||||||
addr string
|
addr string
|
||||||
options *WSOptions
|
options *WSOptions
|
||||||
@ -142,6 +146,10 @@ func (tr *wssTransporter) Handshake(conn net.Conn) (net.Conn, error) {
|
|||||||
return websocketClientConn(url.String(), conn, tr.options)
|
return websocketClientConn(url.String(), conn, tr.options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tr *wssTransporter) Multiplex() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type wsListener struct {
|
type wsListener struct {
|
||||||
addr net.Addr
|
addr net.Addr
|
||||||
upgrader *websocket.Upgrader
|
upgrader *websocket.Upgrader
|
||||||
|
Loading…
Reference in New Issue
Block a user