add sni connector
This commit is contained in:
parent
5b97b878fb
commit
efbabd4052
@ -57,6 +57,9 @@ func buildService(cfg *config.Config) (services []*service.Service) {
|
|||||||
chainable.WithChain(chains[svc.Chain])
|
chainable.WithChain(chains[svc.Chain])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if svc.Listener.Metadata == nil {
|
||||||
|
svc.Listener.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
if err := ln.Init(metadata.MapMetadata(svc.Listener.Metadata)); err != nil {
|
if err := ln.Init(metadata.MapMetadata(svc.Listener.Metadata)); err != nil {
|
||||||
listenerLogger.Fatal("init: ", err)
|
listenerLogger.Fatal("init: ", err)
|
||||||
}
|
}
|
||||||
@ -78,6 +81,9 @@ func buildService(cfg *config.Config) (services []*service.Service) {
|
|||||||
forwarder.Forward(forwarderFromConfig(svc.Forwarder))
|
forwarder.Forward(forwarderFromConfig(svc.Forwarder))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if svc.Handler.Metadata == nil {
|
||||||
|
svc.Handler.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
if err := h.Init(metadata.MapMetadata(svc.Handler.Metadata)); err != nil {
|
if err := h.Init(metadata.MapMetadata(svc.Handler.Metadata)); err != nil {
|
||||||
handlerLogger.Fatal("init: ", err)
|
handlerLogger.Fatal("init: ", err)
|
||||||
}
|
}
|
||||||
@ -119,6 +125,10 @@ func chainFromConfig(cfg *config.ChainConfig) *chain.Chain {
|
|||||||
cr := registry.GetConnector(v.Connector.Type)(
|
cr := registry.GetConnector(v.Connector.Type)(
|
||||||
connector.LoggerOption(connectorLogger),
|
connector.LoggerOption(connectorLogger),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if v.Connector.Metadata == nil {
|
||||||
|
v.Connector.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
|
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
|
||||||
connectorLogger.Fatal("init: ", err)
|
connectorLogger.Fatal("init: ", err)
|
||||||
}
|
}
|
||||||
@ -133,6 +143,10 @@ func chainFromConfig(cfg *config.ChainConfig) *chain.Chain {
|
|||||||
d := registry.GetDialer(v.Dialer.Type)(
|
d := registry.GetDialer(v.Dialer.Type)(
|
||||||
dialer.LoggerOption(dialerLogger),
|
dialer.LoggerOption(dialerLogger),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if v.Dialer.Metadata == nil {
|
||||||
|
v.Dialer.Metadata = make(map[string]interface{})
|
||||||
|
}
|
||||||
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
|
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
|
||||||
dialerLogger.Fatal("init: ", err)
|
dialerLogger.Fatal("init: ", err)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
_ "github.com/go-gost/gost/pkg/connector/forward"
|
_ "github.com/go-gost/gost/pkg/connector/forward"
|
||||||
_ "github.com/go-gost/gost/pkg/connector/http"
|
_ "github.com/go-gost/gost/pkg/connector/http"
|
||||||
_ "github.com/go-gost/gost/pkg/connector/relay"
|
_ "github.com/go-gost/gost/pkg/connector/relay"
|
||||||
|
_ "github.com/go-gost/gost/pkg/connector/sni"
|
||||||
_ "github.com/go-gost/gost/pkg/connector/socks/v4"
|
_ "github.com/go-gost/gost/pkg/connector/socks/v4"
|
||||||
_ "github.com/go-gost/gost/pkg/connector/socks/v5"
|
_ "github.com/go-gost/gost/pkg/connector/socks/v5"
|
||||||
_ "github.com/go-gost/gost/pkg/connector/ss"
|
_ "github.com/go-gost/gost/pkg/connector/ss"
|
||||||
|
1
go.mod
1
go.mod
@ -4,6 +4,7 @@ go 1.17
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
|
||||||
github.com/coreos/go-iptables v0.5.0 // indirect
|
github.com/coreos/go-iptables v0.5.0 // indirect
|
||||||
github.com/ginuerzh/tls-dissector v0.0.2-0.20201202075250-98fa925912da
|
github.com/ginuerzh/tls-dissector v0.0.2-0.20201202075250-98fa925912da
|
||||||
github.com/go-gost/gosocks4 v0.0.1
|
github.com/go-gost/gosocks4 v0.0.1
|
||||||
|
4
go.sum
4
go.sum
@ -60,6 +60,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
|||||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
@ -115,8 +117,6 @@ github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgO
|
|||||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
|
||||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
|
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
|
||||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
||||||
github.com/go-gost/tls-dissector v0.0.1 h1:cySZTSa7o5aOg/bqZXVFzi3NMudsiLwkzArFoxjUWCY=
|
|
||||||
github.com/go-gost/tls-dissector v0.0.1/go.mod h1:8CmRTbp7v4Ebd/lewu/Y/4dEJOP9ke6nwumyJ9WlOec=
|
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e h1:73NGqAs22ey3wJkIYVD/ACEoovuIuOlEzQTEoqrO5+U=
|
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e h1:73NGqAs22ey3wJkIYVD/ACEoovuIuOlEzQTEoqrO5+U=
|
||||||
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||||
|
@ -82,15 +82,14 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get returns a buffer size range from (0, 65]KB,
|
// Get returns a buffer size.
|
||||||
// panic if size > 65KB.
|
|
||||||
func Get(size int) []byte {
|
func Get(size int) []byte {
|
||||||
for i := range pools {
|
for i := range pools {
|
||||||
if size <= pools[i].size {
|
if size <= pools[i].size {
|
||||||
return pools[i].pool.Get().([]byte)
|
return pools[i].pool.Get().([]byte)[:size]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
panic("size too large (max=65KB)")
|
return make([]byte, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Put(b []byte) {
|
func Put(b []byte) {
|
||||||
|
128
pkg/connector/sni/conn.go
Normal file
128
pkg/connector/sni/conn.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package sni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
|
"hash/crc32"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
dissector "github.com/go-gost/tls-dissector"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sniClientConn struct {
|
||||||
|
host string
|
||||||
|
obfuscated bool
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sniClientConn) Write(p []byte) (int, error) {
|
||||||
|
b, err := c.obfuscate(p)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if _, err = c.Conn.Write(b); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sniClientConn) obfuscate(p []byte) ([]byte, error) {
|
||||||
|
if c.host == "" {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.obfuscated {
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if p[0] == dissector.Handshake {
|
||||||
|
b, err := readClientHelloRecord(bytes.NewReader(p), c.host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.obfuscated = true
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
br := bufio.NewReader(bytes.NewReader(p))
|
||||||
|
for {
|
||||||
|
s, err := br.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
if err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s != "" {
|
||||||
|
buf.Write([]byte(s))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of HTTP header
|
||||||
|
if s == "\r\n" {
|
||||||
|
buf.Write([]byte(s))
|
||||||
|
// drain the remain bytes.
|
||||||
|
io.Copy(buf, br)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(s, "Host") {
|
||||||
|
s = strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(s, "Host:"), "\r\n"))
|
||||||
|
host := encodeServerName(s)
|
||||||
|
buf.WriteString("Host: " + c.host + "\r\n")
|
||||||
|
buf.WriteString("Gost-Target: " + host + "\r\n")
|
||||||
|
// drain the remain bytes.
|
||||||
|
io.Copy(buf, br)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf.Write([]byte(s))
|
||||||
|
}
|
||||||
|
c.obfuscated = true
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readClientHelloRecord(r io.Reader, host string) ([]byte, error) {
|
||||||
|
record, err := dissector.ReadRecord(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
clientHello := dissector.ClientHelloMsg{}
|
||||||
|
if err := clientHello.Decode(record.Opaque); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ext := range clientHello.Extensions {
|
||||||
|
if ext.Type() == dissector.ExtServerName {
|
||||||
|
snExtension := ext.(*dissector.ServerNameExtension)
|
||||||
|
if host != "" {
|
||||||
|
e, _ := dissector.NewExtension(0xFFFE, []byte(encodeServerName(snExtension.Name)))
|
||||||
|
clientHello.Extensions = append(clientHello.Extensions, e)
|
||||||
|
snExtension.Name = host
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
record.Opaque, err = clientHello.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if _, err := record.WriteTo(buf); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeServerName(name string) string {
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
binary.Write(buf, binary.BigEndian, crc32.ChecksumIEEE([]byte(name)))
|
||||||
|
buf.WriteString(base64.RawURLEncoding.EncodeToString([]byte(name)))
|
||||||
|
return base64.RawURLEncoding.EncodeToString(buf.Bytes())
|
||||||
|
}
|
47
pkg/connector/sni/connector.go
Normal file
47
pkg/connector/sni/connector.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package sni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/go-gost/gost/pkg/connector"
|
||||||
|
"github.com/go-gost/gost/pkg/logger"
|
||||||
|
md "github.com/go-gost/gost/pkg/metadata"
|
||||||
|
"github.com/go-gost/gost/pkg/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registry.RegiserConnector("sni", NewConnector)
|
||||||
|
}
|
||||||
|
|
||||||
|
type sniConnector struct {
|
||||||
|
md metadata
|
||||||
|
logger logger.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnector(opts ...connector.Option) connector.Connector {
|
||||||
|
options := &connector.Options{}
|
||||||
|
for _, opt := range opts {
|
||||||
|
opt(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sniConnector{
|
||||||
|
logger: options.Logger,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sniConnector) Init(md md.Metadata) (err error) {
|
||||||
|
return c.parseMetadata(md)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sniConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
|
||||||
|
c.logger = c.logger.WithFields(map[string]interface{}{
|
||||||
|
"remote": conn.RemoteAddr().String(),
|
||||||
|
"local": conn.LocalAddr().String(),
|
||||||
|
"network": network,
|
||||||
|
"address": address,
|
||||||
|
})
|
||||||
|
c.logger.Infof("connect %s/%s", address, network)
|
||||||
|
|
||||||
|
return &sniClientConn{Conn: conn, host: c.md.host}, nil
|
||||||
|
}
|
24
pkg/connector/sni/metadata.go
Normal file
24
pkg/connector/sni/metadata.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package sni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
md "github.com/go-gost/gost/pkg/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
type metadata struct {
|
||||||
|
host string
|
||||||
|
connectTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sniConnector) parseMetadata(md md.Metadata) (err error) {
|
||||||
|
const (
|
||||||
|
host = "host"
|
||||||
|
connectTimeout = "timeout"
|
||||||
|
)
|
||||||
|
|
||||||
|
c.md.host = md.GetString(host)
|
||||||
|
c.md.connectTimeout = md.GetDuration(connectTimeout)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -9,10 +9,6 @@ import (
|
|||||||
"github.com/go-gost/gosocks4"
|
"github.com/go-gost/gosocks4"
|
||||||
"github.com/go-gost/gosocks5"
|
"github.com/go-gost/gosocks5"
|
||||||
"github.com/go-gost/gost/pkg/handler"
|
"github.com/go-gost/gost/pkg/handler"
|
||||||
http_handler "github.com/go-gost/gost/pkg/handler/http"
|
|
||||||
relay_handler "github.com/go-gost/gost/pkg/handler/relay"
|
|
||||||
socks4_handler "github.com/go-gost/gost/pkg/handler/socks/v4"
|
|
||||||
socks5_handler "github.com/go-gost/gost/pkg/handler/socks/v5"
|
|
||||||
"github.com/go-gost/gost/pkg/logger"
|
"github.com/go-gost/gost/pkg/logger"
|
||||||
md "github.com/go-gost/gost/pkg/metadata"
|
md "github.com/go-gost/gost/pkg/metadata"
|
||||||
"github.com/go-gost/gost/pkg/registry"
|
"github.com/go-gost/gost/pkg/registry"
|
||||||
@ -46,37 +42,52 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
log: log,
|
log: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
v := append(opts,
|
if f := registry.GetHandler("http"); f != nil {
|
||||||
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "http"})))
|
v := append(opts,
|
||||||
h.httpHandler = http_handler.NewHandler(v...)
|
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "http"})))
|
||||||
|
h.httpHandler = f(v...)
|
||||||
|
}
|
||||||
|
if f := registry.GetHandler("socks4"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "socks4"})))
|
||||||
|
h.socks4Handler = f(v...)
|
||||||
|
}
|
||||||
|
if f := registry.GetHandler("socks5"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "socks5"})))
|
||||||
|
h.socks5Handler = f(v...)
|
||||||
|
}
|
||||||
|
if f := registry.GetHandler("relay"); f != nil {
|
||||||
|
v := append(opts,
|
||||||
|
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "relay"})))
|
||||||
|
h.relayHandler = f(v...)
|
||||||
|
}
|
||||||
|
|
||||||
v = append(opts,
|
|
||||||
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "socks4"})))
|
|
||||||
h.socks4Handler = socks4_handler.NewHandler(v...)
|
|
||||||
|
|
||||||
v = append(opts,
|
|
||||||
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "socks5"})))
|
|
||||||
h.socks5Handler = socks5_handler.NewHandler(v...)
|
|
||||||
|
|
||||||
v = append(opts,
|
|
||||||
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "relay"})))
|
|
||||||
h.relayHandler = relay_handler.NewHandler(v...)
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *autoHandler) Init(md md.Metadata) error {
|
func (h *autoHandler) Init(md md.Metadata) error {
|
||||||
if err := h.httpHandler.Init(md); err != nil {
|
if h.httpHandler != nil {
|
||||||
return err
|
if err := h.httpHandler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := h.socks4Handler.Init(md); err != nil {
|
if h.socks4Handler != nil {
|
||||||
return err
|
if err := h.socks4Handler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := h.socks5Handler.Init(md); err != nil {
|
if h.socks5Handler != nil {
|
||||||
return err
|
if err := h.socks5Handler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := h.relayHandler.Init(md); err != nil {
|
if h.relayHandler != nil {
|
||||||
return err
|
if err := h.relayHandler.Init(md); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,13 +116,21 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn) {
|
|||||||
conn = handler.NewBufferReaderConn(conn, br)
|
conn = handler.NewBufferReaderConn(conn, br)
|
||||||
switch b[0] {
|
switch b[0] {
|
||||||
case gosocks4.Ver4: // socks4
|
case gosocks4.Ver4: // socks4
|
||||||
h.socks4Handler.Handle(ctx, conn)
|
if h.socks4Handler != nil {
|
||||||
|
h.socks4Handler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
case gosocks5.Ver5: // socks5
|
case gosocks5.Ver5: // socks5
|
||||||
h.socks5Handler.Handle(ctx, conn)
|
if h.socks5Handler != nil {
|
||||||
|
h.socks5Handler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
case relay.Version1: // relay
|
case relay.Version1: // relay
|
||||||
h.relayHandler.Handle(ctx, conn)
|
if h.relayHandler != nil {
|
||||||
|
h.relayHandler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
default: // http
|
default: // http
|
||||||
h.httpHandler.Handle(ctx, conn)
|
if h.httpHandler != nil {
|
||||||
|
h.httpHandler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/asaskevich/govalidator"
|
||||||
"github.com/go-gost/gost/pkg/bypass"
|
"github.com/go-gost/gost/pkg/bypass"
|
||||||
"github.com/go-gost/gost/pkg/chain"
|
"github.com/go-gost/gost/pkg/chain"
|
||||||
"github.com/go-gost/gost/pkg/handler"
|
"github.com/go-gost/gost/pkg/handler"
|
||||||
@ -59,12 +60,10 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn) {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
h.logger = h.logger.WithFields(map[string]interface{}{
|
h.logger = h.logger.WithFields(map[string]interface{}{
|
||||||
"remote": conn.RemoteAddr().String(),
|
"remote": conn.RemoteAddr().String(),
|
||||||
"local": conn.LocalAddr().String(),
|
"local": conn.LocalAddr().String(),
|
||||||
})
|
})
|
||||||
|
|
||||||
h.logger.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
h.logger.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
defer func() {
|
defer func() {
|
||||||
h.logger.WithFields(map[string]interface{}{
|
h.logger.WithFields(map[string]interface{}{
|
||||||
@ -87,6 +86,10 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.md.sni && !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
|
||||||
|
req.URL.Scheme = "http"
|
||||||
|
}
|
||||||
|
|
||||||
// Try to get the actual host.
|
// Try to get the actual host.
|
||||||
// Compatible with GOST 2.x.
|
// Compatible with GOST 2.x.
|
||||||
if v := req.Header.Get("Gost-Target"); v != "" {
|
if v := req.Header.Get("Gost-Target"); v != "" {
|
||||||
|
@ -12,6 +12,7 @@ type metadata struct {
|
|||||||
proxyAgent string
|
proxyAgent string
|
||||||
retryCount int
|
retryCount int
|
||||||
probeResist *probeResist
|
probeResist *probeResist
|
||||||
|
sni bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *httpHandler) parseMetadata(md md.Metadata) error {
|
func (h *httpHandler) parseMetadata(md md.Metadata) error {
|
||||||
@ -21,6 +22,7 @@ func (h *httpHandler) parseMetadata(md md.Metadata) error {
|
|||||||
probeResistKey = "probeResist"
|
probeResistKey = "probeResist"
|
||||||
knock = "knock"
|
knock = "knock"
|
||||||
retryCount = "retry"
|
retryCount = "retry"
|
||||||
|
sni = "sni"
|
||||||
)
|
)
|
||||||
|
|
||||||
h.md.proxyAgent = md.GetString(proxyAgent)
|
h.md.proxyAgent = md.GetString(proxyAgent)
|
||||||
@ -50,6 +52,7 @@ func (h *httpHandler) parseMetadata(md md.Metadata) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
h.md.retryCount = md.GetInt(retryCount)
|
h.md.retryCount = md.GetInt(retryCount)
|
||||||
|
h.md.sni = md.GetBool(sni)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
20
pkg/handler/sni/conn.go
Normal file
20
pkg/handler/sni/conn.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package sni
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cacheConn struct {
|
||||||
|
net.Conn
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cacheConn) Read(b []byte) (n int, err error) {
|
||||||
|
if len(c.buf) > 0 {
|
||||||
|
n = copy(b, c.buf)
|
||||||
|
c.buf = c.buf[n:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Conn.Read(b)
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package sni
|
package sni
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
@ -13,8 +13,8 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/gost/pkg/bypass"
|
"github.com/go-gost/gost/pkg/bypass"
|
||||||
"github.com/go-gost/gost/pkg/chain"
|
"github.com/go-gost/gost/pkg/chain"
|
||||||
|
"github.com/go-gost/gost/pkg/common/bufpool"
|
||||||
"github.com/go-gost/gost/pkg/handler"
|
"github.com/go-gost/gost/pkg/handler"
|
||||||
http_handler "github.com/go-gost/gost/pkg/handler/http"
|
|
||||||
"github.com/go-gost/gost/pkg/logger"
|
"github.com/go-gost/gost/pkg/logger"
|
||||||
md "github.com/go-gost/gost/pkg/metadata"
|
md "github.com/go-gost/gost/pkg/metadata"
|
||||||
"github.com/go-gost/gost/pkg/registry"
|
"github.com/go-gost/gost/pkg/registry"
|
||||||
@ -49,9 +49,11 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
logger: log,
|
logger: log,
|
||||||
}
|
}
|
||||||
|
|
||||||
v := append(opts,
|
if f := registry.GetHandler("http"); f != nil {
|
||||||
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "http"})))
|
v := append(opts,
|
||||||
h.httpHandler = http_handler.NewHandler(v...)
|
handler.LoggerOption(log.WithFields(map[string]interface{}{"type": "http"})))
|
||||||
|
h.httpHandler = f(v...)
|
||||||
|
}
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
@ -60,8 +62,13 @@ func (h *sniHandler) Init(md md.Metadata) (err error) {
|
|||||||
if err = h.parseMetadata(md); err != nil {
|
if err = h.parseMetadata(md); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = h.httpHandler.Init(md); err != nil {
|
if h.httpHandler != nil {
|
||||||
return
|
if md != nil {
|
||||||
|
md.Set("sni", true)
|
||||||
|
}
|
||||||
|
if err = h.httpHandler.Init(md); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -88,22 +95,36 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn) {
|
|||||||
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
br := bufio.NewReader(conn)
|
var hdr [dissector.RecordHeaderLen]byte
|
||||||
hdr, err := br.Peek(dissector.RecordHeaderLen)
|
if _, err := io.ReadFull(conn, hdr[:]); err != nil {
|
||||||
if err != nil {
|
|
||||||
h.logger.Error(err)
|
h.logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
conn = handler.NewBufferReaderConn(conn, br)
|
|
||||||
|
|
||||||
if hdr[0] != dissector.Handshake {
|
if hdr[0] != dissector.Handshake {
|
||||||
// We assume it is an HTTP request
|
// We assume it is an HTTP request
|
||||||
h.httpHandler.Handle(ctx, conn)
|
conn = &cacheConn{
|
||||||
|
Conn: conn,
|
||||||
|
buf: hdr[:],
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.httpHandler != nil {
|
||||||
|
h.httpHandler.Handle(ctx, conn)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
host, err := h.decodeHost(conn)
|
length := binary.BigEndian.Uint16(hdr[3:5])
|
||||||
|
|
||||||
|
buf := bufpool.Get(int(length) + dissector.RecordHeaderLen)
|
||||||
|
defer bufpool.Put(buf)
|
||||||
|
if _, err := io.ReadFull(conn, buf[dissector.RecordHeaderLen:]); err != nil {
|
||||||
|
h.logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(buf, hdr[:])
|
||||||
|
|
||||||
|
buf, host, err := h.decodeHost(bytes.NewReader(buf))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.logger.Error(err)
|
h.logger.Error(err)
|
||||||
return
|
return
|
||||||
@ -130,6 +151,11 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn) {
|
|||||||
}
|
}
|
||||||
defer cc.Close()
|
defer cc.Close()
|
||||||
|
|
||||||
|
if _, err := cc.Write(buf); err != nil {
|
||||||
|
h.logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), target)
|
h.logger.Infof("%s <-> %s", conn.RemoteAddr(), target)
|
||||||
handler.Transport(conn, cc)
|
handler.Transport(conn, cc)
|
||||||
@ -140,27 +166,51 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn) {
|
|||||||
Infof("%s >-< %s", conn.RemoteAddr(), target)
|
Infof("%s >-< %s", conn.RemoteAddr(), target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *sniHandler) decodeHost(r io.Reader) (host string, err error) {
|
func (h *sniHandler) decodeHost(r io.Reader) (opaque []byte, host string, err error) {
|
||||||
record, err := dissector.ReadRecord(r)
|
record, err := dissector.ReadRecord(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
clientHello := &dissector.ClientHelloMsg{}
|
clientHello := dissector.ClientHelloMsg{}
|
||||||
if err = clientHello.Decode(record.Opaque); err != nil {
|
if err = clientHello.Decode(record.Opaque); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var extensions []dissector.Extension
|
||||||
for _, ext := range clientHello.Extensions {
|
for _, ext := range clientHello.Extensions {
|
||||||
if ext.Type() == 0xFFFE {
|
if ext.Type() == 0xFFFE {
|
||||||
b, _ := ext.Encode()
|
b, _ := ext.Encode()
|
||||||
return h.decodeServerName(string(b))
|
if v, err := h.decodeServerName(string(b)); err == nil {
|
||||||
|
host = v
|
||||||
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
extensions = append(extensions, ext)
|
||||||
|
}
|
||||||
|
clientHello.Extensions = extensions
|
||||||
|
|
||||||
|
for _, ext := range clientHello.Extensions {
|
||||||
if ext.Type() == dissector.ExtServerName {
|
if ext.Type() == dissector.ExtServerName {
|
||||||
snExtension := ext.(*dissector.ServerNameExtension)
|
snExtension := ext.(*dissector.ServerNameExtension)
|
||||||
host = snExtension.Name
|
if host == "" {
|
||||||
|
host = snExtension.Name
|
||||||
|
} else {
|
||||||
|
snExtension.Name = host
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record.Opaque, err = clientHello.Encode()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
if _, err = record.WriteTo(buf); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
opaque = buf.Bytes()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
type Metadata interface {
|
type Metadata interface {
|
||||||
IsExists(key string) bool
|
IsExists(key string) bool
|
||||||
|
Set(key string, value interface{})
|
||||||
Get(key string) interface{}
|
Get(key string) interface{}
|
||||||
GetBool(key string) bool
|
GetBool(key string) bool
|
||||||
GetInt(key string) int
|
GetInt(key string) int
|
||||||
@ -22,6 +23,10 @@ func (m MapMetadata) IsExists(key string) bool {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MapMetadata) Set(key string, value interface{}) {
|
||||||
|
m[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
func (m MapMetadata) Get(key string) interface{} {
|
func (m MapMetadata) Get(key string) interface{} {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m[key]
|
return m[key]
|
||||||
|
Loading…
Reference in New Issue
Block a user