add KCP support

This commit is contained in:
rui.zheng 2017-07-22 23:21:04 +08:00
parent 684ccca252
commit 9b2bd7a88c
16 changed files with 667 additions and 101 deletions

View File

@ -6,7 +6,7 @@ import (
) )
var ( var (
// ErrEmptyChain is an error that implies the chain is empty // ErrEmptyChain is an error that implies the chain is empty.
ErrEmptyChain = errors.New("empty chain") ErrEmptyChain = errors.New("empty chain")
) )

View File

@ -5,7 +5,6 @@ import (
"log" "log"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"net/url"
"github.com/ginuerzh/gost/gost" "github.com/ginuerzh/gost/gost"
) )
@ -28,61 +27,72 @@ func main() {
}, },
*/ */
// socks5+tcp /*
gost.Node{ // socks5+tcp
Addr: "127.0.0.1:1080", gost.Node{
Client: gost.NewClient( Addr: "127.0.0.1:1080",
gost.SOCKS5Connector(url.UserPassword("admin", "123456")), Client: gost.NewClient(
gost.TCPTransporter(), gost.SOCKS5Connector(url.UserPassword("admin", "123456")),
), gost.TCPTransporter(),
},
/*
// ss+tcp
gost.Node{
Addr: "127.0.0.1:8338",
Client: gost.NewClient(
gost.ShadowConnector(url.UserPassword("chacha20", "123456")),
gost.TCPTransporter(),
),
},
*/
/*
// http+ws
gost.Node{
Addr: "127.0.0.1:8000",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSTransporter("127.0.0.1:8000", nil),
),
},
*/
/*
// http+wss
gost.Node{
Addr: "127.0.0.1:8443",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSSTransporter(
"127.0.0.1:8443",
&gost.WSOptions{TLSConfig: &tls.Config{InsecureSkipVerify: true}},
), ),
), },
}, */
*/
/* /*
// http+tls // ss+tcp
gost.Node{
Addr: "127.0.0.1:8338",
Client: gost.NewClient(
gost.ShadowConnector(url.UserPassword("chacha20", "123456")),
gost.TCPTransporter(),
),
},
*/
/*
// http+ws
gost.Node{
Addr: "127.0.0.1:8000",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSTransporter("127.0.0.1:8000", nil),
),
},
*/
/*
// http+wss
gost.Node{
Addr: "127.0.0.1:8443",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.WSSTransporter(
"127.0.0.1:8443",
&gost.WSOptions{TLSConfig: &tls.Config{InsecureSkipVerify: true}},
),
),
},
*/
/*
// http+tls
gost.Node{
Addr: "127.0.0.1:1443",
Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")),
gost.TLSTransporter(&tls.Config{InsecureSkipVerify: true}),
),
},
*/
// http+kcp
gost.Node{ gost.Node{
Addr: "127.0.0.1:1443", Addr: "127.0.0.1:8388",
Client: gost.NewClient( Client: gost.NewClient(
gost.HTTPConnector(url.UserPassword("admin", "123456")), gost.HTTPConnector(nil),
gost.TLSTransporter(&tls.Config{InsecureSkipVerify: true}), gost.KCPTransporter(nil),
), ),
}, },
*/
) )
conn, err := chain.Dial("localhost:10000") conn, err := chain.Dial("localhost:10000")

View File

@ -5,15 +5,15 @@ import (
) )
// Client is a proxy client. // Client is a proxy client.
// A client is divided into two layers: connector and transporter.
// Connector is responsible for connecting to the destination address through this proxy.
// Transporter performs a handshake with this proxy.
type Client struct { type Client struct {
Connector Connector Connector Connector
Transporter Transporter Transporter Transporter
} }
// NewClient creates a proxy client. // NewClient creates a proxy client.
// A client is divided into two layers: connector and transporter.
// Connector is responsible for connecting to the destination address through this proxy.
// Transporter performs a handshake with this proxy.
func NewClient(c Connector, tr Transporter) *Client { func NewClient(c Connector, tr Transporter) *Client {
return &Client{ return &Client{
Connector: c, Connector: c,
@ -21,9 +21,9 @@ func NewClient(c Connector, tr Transporter) *Client {
} }
} }
// Dial connects to the target address // Dial connects to the target address.
func (c *Client) Dial(addr string) (net.Conn, error) { func (c *Client) Dial(addr string) (net.Conn, error) {
return net.Dial(c.Transporter.Network(), addr) return c.Transporter.Dial(addr)
} }
// Handshake performs a handshake with the proxy. // Handshake performs a handshake with the proxy.
@ -38,7 +38,7 @@ func (c *Client) Connect(conn net.Conn, addr string) (net.Conn, error) {
return c.Connector.Connect(conn, addr) return c.Connector.Connect(conn, addr)
} }
// DefaultClient is a standard HTTP proxy client // DefaultClient is a standard HTTP proxy client.
var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter()) var DefaultClient = NewClient(HTTPConnector(nil), TCPTransporter())
// Dial connects to the address addr via the DefaultClient. // Dial connects to the address addr via the DefaultClient.
@ -46,7 +46,7 @@ func Dial(addr string) (net.Conn, error) {
return DefaultClient.Dial(addr) return DefaultClient.Dial(addr)
} }
// Handshake performs a handshake via the DefaultClient // Handshake performs a handshake via the DefaultClient.
func Handshake(conn net.Conn) (net.Conn, error) { func Handshake(conn net.Conn) (net.Conn, error) {
return DefaultClient.Handshake(conn) return DefaultClient.Handshake(conn)
} }
@ -56,26 +56,27 @@ func Connect(conn net.Conn, addr string) (net.Conn, error) {
return DefaultClient.Connect(conn, addr) return DefaultClient.Connect(conn, addr)
} }
// Connector is responsible for connecting to the destination address // Connector is responsible for connecting to the destination address.
type Connector interface { type Connector interface {
Connect(conn net.Conn, addr string) (net.Conn, error) Connect(conn net.Conn, addr string) (net.Conn, error)
} }
// Transporter is responsible for handshaking with the proxy server. // Transporter is responsible for handshaking with the proxy server.
type Transporter interface { type Transporter interface {
Network() string Dial(addr string) (net.Conn, error)
Handshake(conn net.Conn) (net.Conn, error) Handshake(conn net.Conn) (net.Conn, error)
} }
type tcpTransporter struct { type tcpTransporter struct {
} }
// TCPTransporter creates a transporter for TCP proxy client.
func TCPTransporter() Transporter { func TCPTransporter() Transporter {
return &tcpTransporter{} return &tcpTransporter{}
} }
func (tr *tcpTransporter) Network() string { func (tr *tcpTransporter) Dial(addr string) (net.Conn, error) {
return "tcp" return net.Dial("tcp", addr)
} }
func (tr *tcpTransporter) Handshake(conn net.Conn) (net.Conn, error) { func (tr *tcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {

View File

@ -6,24 +6,30 @@ import (
"github.com/go-log/log" "github.com/go-log/log"
) )
// Version is the gost version.
const Version = "2.4-dev20170722" const Version = "2.4-dev20170722"
// Debug is a flag that enables the debug log.
var Debug bool var Debug bool
var ( var (
TinyBufferSize = 128 tinyBufferSize = 128
SmallBufferSize = 1 * 1024 // 1KB small buffer smallBufferSize = 1 * 1024 // 1KB small buffer
MediumBufferSize = 8 * 1024 // 8KB medium buffer mediumBufferSize = 8 * 1024 // 8KB medium buffer
LargeBufferSize = 32 * 1024 // 32KB large buffer largeBufferSize = 32 * 1024 // 32KB large buffer
) )
var ( var (
// KeepAliveTime is the keep alive time period for TCP connection.
KeepAliveTime = 180 * time.Second KeepAliveTime = 180 * time.Second
DialTimeout = 30 * time.Second // DialTimeout is the timeout of dial.
ReadTimeout = 90 * time.Second DialTimeout = 30 * time.Second
WriteTimeout = 90 * time.Second // ReadTimeout is the timeout for reading.
ReadTimeout = 90 * time.Second
DefaultTTL = 60 // default udp node TTL in second for udp port forwarding // WriteTimeout is the timeout for writing.
WriteTimeout = 90 * time.Second
// default udp node TTL in second for udp port forwarding.
defaultTTL = 60
) )
func init() { func init() {

View File

@ -6,30 +6,36 @@ import (
"net/url" "net/url"
) )
// Handler is a proxy server handler
type Handler interface { type Handler interface {
Handle(net.Conn) Handle(net.Conn)
} }
// HandlerOptions describes the options for Handler.
type HandlerOptions struct { type HandlerOptions struct {
Chain *Chain Chain *Chain
Users []*url.Userinfo Users []*url.Userinfo
TLSConfig *tls.Config TLSConfig *tls.Config
} }
// HandlerOption allows a common way to set handler options.
type HandlerOption func(opts *HandlerOptions) type HandlerOption func(opts *HandlerOptions)
// ChainHandlerOption sets the Chain option of HandlerOptions.
func ChainHandlerOption(chain *Chain) HandlerOption { func ChainHandlerOption(chain *Chain) HandlerOption {
return func(opts *HandlerOptions) { return func(opts *HandlerOptions) {
opts.Chain = chain opts.Chain = chain
} }
} }
// UsersHandlerOption sets the Users option of HandlerOptions.
func UsersHandlerOption(users ...*url.Userinfo) HandlerOption { func UsersHandlerOption(users ...*url.Userinfo) HandlerOption {
return func(opts *HandlerOptions) { return func(opts *HandlerOptions) {
opts.Users = users opts.Users = users
} }
} }
// TLSConfigHandlerOption sets the TLSConfig option of HandlerOptions.
func TLSConfigHandlerOption(config *tls.Config) HandlerOption { func TLSConfigHandlerOption(config *tls.Config) HandlerOption {
return func(opts *HandlerOptions) { return func(opts *HandlerOptions) {
opts.TLSConfig = config opts.TLSConfig = config

View File

@ -17,6 +17,8 @@ type httpConnector struct {
User *url.Userinfo User *url.Userinfo
} }
// HTTPConnector creates a Connector for HTTP proxy client.
// It accepts an optional auth info for HTTP Basic Authentication.
func HTTPConnector(user *url.Userinfo) Connector { func HTTPConnector(user *url.Userinfo) Connector {
return &httpConnector{User: user} return &httpConnector{User: user}
} }
@ -71,6 +73,7 @@ type httpHandler struct {
options *HandlerOptions options *HandlerOptions
} }
// HTTPHandler creates a server Handler for HTTP proxy server.
func HTTPHandler(opts ...HandlerOption) Handler { func HTTPHandler(opts ...HandlerOption) Handler {
h := &httpHandler{ h := &httpHandler{
options: &HandlerOptions{ options: &HandlerOptions{

472
gost/kcp.go Normal file
View File

@ -0,0 +1,472 @@
package gost
import (
"crypto/sha1"
"encoding/csv"
"fmt"
"net"
"os"
"time"
"golang.org/x/crypto/pbkdf2"
"sync"
"github.com/go-log/log"
"github.com/klauspost/compress/snappy"
"gopkg.in/xtaci/kcp-go.v2"
"gopkg.in/xtaci/smux.v1"
)
var (
// SALT is the default salt for KCP cipher.
SALT = "kcp-go"
)
// KCPConfig describes the config for KCP.
type KCPConfig struct {
Key string `json:"key"`
Crypt string `json:"crypt"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
SndWnd int `json:"sndwnd"`
RcvWnd int `json:"rcvwnd"`
DataShard int `json:"datashard"`
ParityShard int `json:"parityshard"`
DSCP int `json:"dscp"`
NoComp bool `json:"nocomp"`
AckNodelay bool `json:"acknodelay"`
NoDelay int `json:"nodelay"`
Interval int `json:"interval"`
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
KeepAlive int `json:"keepalive"`
SnmpLog string `json:"snmplog"`
SnmpPeriod int `json:"snmpperiod"`
Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature.
}
// Init initializes the KCP config.
func (c *KCPConfig) Init() {
switch c.Mode {
case "normal":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 50, 2, 1
case "fast2":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 30, 2, 1
case "fast3":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
case "fast":
fallthrough
default:
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 40, 2, 1
}
}
var (
// DefaultKCPConfig is the default KCP config.
DefaultKCPConfig = &KCPConfig{
Key: "it's a secrect",
Crypt: "aes",
Mode: "fast",
MTU: 1350,
SndWnd: 1024,
RcvWnd: 1024,
DataShard: 10,
ParityShard: 3,
DSCP: 0,
NoComp: false,
AckNodelay: false,
NoDelay: 0,
Interval: 50,
Resend: 0,
NoCongestion: 0,
SockBuf: 4194304,
KeepAlive: 10,
SnmpLog: "",
SnmpPeriod: 60,
Signal: false,
}
)
type kcpConn struct {
conn net.Conn
stream *smux.Stream
}
func newKCPConn(conn net.Conn, stream *smux.Stream) *kcpConn {
return &kcpConn{conn: conn, stream: stream}
}
func (c *kcpConn) Read(b []byte) (n int, err error) {
return c.stream.Read(b)
}
func (c *kcpConn) Write(b []byte) (n int, err error) {
return c.stream.Write(b)
}
func (c *kcpConn) Close() error {
return c.stream.Close()
}
func (c *kcpConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *kcpConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *kcpConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *kcpConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *kcpConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
type kcpSession struct {
conn net.Conn
session *smux.Session
}
func (session *kcpSession) GetConn() (*kcpConn, error) {
stream, err := session.session.OpenStream()
if err != nil {
return nil, err
}
return newKCPConn(session.conn, stream), nil
}
func (session *kcpSession) Close() error {
return session.session.Close()
}
func (session *kcpSession) IsClosed() bool {
return session.session.IsClosed()
}
func (session *kcpSession) NumStreams() int {
return session.session.NumStreams()
}
type kcpTransporter struct {
sessions map[string]*kcpSession
sessionMutex sync.Mutex
config *KCPConfig
}
// KCPTransporter creates a Transporter that is used by KCP proxy client.
func KCPTransporter(config *KCPConfig) Transporter {
if config == nil {
config = DefaultKCPConfig
}
config.Init()
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
if config.Signal {
go kcpSigHandler()
}
return &kcpTransporter{
config: config,
sessions: make(map[string]*kcpSession),
}
}
func (tr *kcpTransporter) Dial(addr string) (conn net.Conn, err error) {
tr.sessionMutex.Lock()
session, ok := tr.sessions[addr]
if !ok {
session, err = tr.dial(addr, tr.config)
if err != nil {
tr.sessionMutex.Unlock()
return
}
tr.sessions[addr] = session
}
tr.sessionMutex.Unlock()
conn, err = session.GetConn()
if err != nil {
tr.sessionMutex.Lock()
session.Close()
delete(tr.sessions, addr)
tr.sessionMutex.Unlock()
}
return
}
func (tr *kcpTransporter) dial(addr string, config *KCPConfig) (*kcpSession, error) {
kcpconn, err := kcp.DialWithOptions(addr,
blockCrypt(config.Key, config.Crypt, SALT), config.DataShard, config.ParityShard)
if err != nil {
return nil, err
}
kcpconn.SetStreamMode(true)
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
kcpconn.SetMtu(config.MTU)
kcpconn.SetACKNoDelay(config.AckNodelay)
kcpconn.SetKeepAlive(config.KeepAlive)
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
log.Log("[kcp]", err)
}
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err)
}
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err)
}
// stream multiplex
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = config.SockBuf
var conn net.Conn = kcpconn
if !config.NoComp {
conn = newCompStreamConn(kcpconn)
}
session, err := smux.Client(conn, smuxConfig)
if err != nil {
conn.Close()
return nil, err
}
return &kcpSession{conn: conn, session: session}, nil
}
func (tr *kcpTransporter) Handshake(conn net.Conn) (net.Conn, error) {
return conn, nil
}
type kcpListener struct {
config *KCPConfig
ln *kcp.Listener
connChan chan net.Conn
errChan chan error
}
// KCPListener creates a Listener for KCP proxy server.
func KCPListener(addr string, config *KCPConfig) (Listener, error) {
if config == nil {
config = DefaultKCPConfig
}
config.Init()
ln, err := kcp.ListenWithOptions(addr,
blockCrypt(config.Key, config.Crypt, SALT), config.DataShard, config.ParityShard)
if err != nil {
return nil, err
}
if err = ln.SetDSCP(config.DSCP); err != nil {
log.Log("[kcp]", err)
}
if err = ln.SetReadBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err)
}
if err = ln.SetWriteBuffer(config.SockBuf); err != nil {
log.Log("[kcp]", err)
}
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
if config.Signal {
go kcpSigHandler()
}
l := &kcpListener{
config: config,
ln: ln,
connChan: make(chan net.Conn, 128),
errChan: make(chan error),
}
go l.acceptLoop()
return l, nil
}
func (l *kcpListener) acceptLoop() {
for {
conn, err := l.ln.AcceptKCP()
if err != nil {
log.Log("[kcp] accept:", err)
continue
}
conn.SetStreamMode(true)
conn.SetNoDelay(l.config.NoDelay, l.config.Interval, l.config.Resend, l.config.NoCongestion)
conn.SetMtu(l.config.MTU)
conn.SetWindowSize(l.config.SndWnd, l.config.RcvWnd)
conn.SetACKNoDelay(l.config.AckNodelay)
conn.SetKeepAlive(l.config.KeepAlive)
go l.mux(conn)
}
}
func (l *kcpListener) Accept() (conn net.Conn, err error) {
select {
case conn = <-l.connChan:
case err = <-l.errChan:
}
return
}
func (l *kcpListener) mux(conn net.Conn) {
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = l.config.SockBuf
log.Logf("[kcp] %s - %s", conn.RemoteAddr(), l.Addr())
if !l.config.NoComp {
conn = newCompStreamConn(conn)
}
mux, err := smux.Server(conn, smuxConfig)
if err != nil {
log.Log("[kcp]", err)
return
}
defer mux.Close()
log.Logf("[kcp] %s <-> %s", conn.RemoteAddr(), l.Addr())
defer log.Logf("[kcp] %s >-< %s", conn.RemoteAddr(), l.Addr())
for {
stream, err := mux.AcceptStream()
if err != nil {
log.Log("[kcp] accept stream:", err)
return
}
select {
case l.connChan <- newKCPConn(conn, stream):
default:
log.Logf("[kcp] %s - %s: connection queue is full", conn.RemoteAddr(), l.Addr())
}
}
}
func (l *kcpListener) Addr() net.Addr {
return l.ln.Addr()
}
func (l *kcpListener) Close() error {
return l.ln.Close()
}
func blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
pass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)
switch crypt {
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
case "aes":
fallthrough
default: // aes
block, _ = kcp.NewAESBlockCrypt(pass)
}
return
}
func snmpLogger(format string, interval int) {
if format == "" || interval == 0 {
return
}
ticker := time.NewTicker(time.Duration(interval) * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
f, err := os.OpenFile(time.Now().Format(format), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Log("[kcp]", err)
return
}
w := csv.NewWriter(f)
// write header in empty file
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
log.Log("[kcp]", err)
}
}
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
log.Log("[kcp]", err)
}
kcp.DefaultSnmp.Reset()
w.Flush()
f.Close()
}
}
}
type compStreamConn struct {
conn net.Conn
w *snappy.Writer
r *snappy.Reader
}
func newCompStreamConn(conn net.Conn) *compStreamConn {
c := new(compStreamConn)
c.conn = conn
c.w = snappy.NewBufferedWriter(conn)
c.r = snappy.NewReader(conn)
return c
}
func (c *compStreamConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
func (c *compStreamConn) Write(b []byte) (n int, err error) {
n, err = c.w.Write(b)
err = c.w.Flush()
return n, err
}
func (c *compStreamConn) Close() error {
return c.conn.Close()
}
func (c *compStreamConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *compStreamConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *compStreamConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *compStreamConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *compStreamConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}

View File

@ -1,6 +1,6 @@
package gost package gost
// Node is a proxy node, mainly used to construct a proxy chain // Node is a proxy node, mainly used to construct a proxy chain.
type Node struct { type Node struct {
Addr string Addr string
Protocol string Protocol string

View File

@ -14,7 +14,7 @@ type Server struct {
handler Handler handler Handler
} }
// Handle sets a handler for the server // Handle sets a handler for the server.
func (s *Server) Handle(h Handler) { func (s *Server) Handle(h Handler) {
s.handler = h s.handler = h
} }
@ -57,6 +57,7 @@ type tcpListener struct {
net.Listener net.Listener
} }
// TCPListener creates a Listener for TCP proxy server.
func TCPListener(addr string) (Listener, error) { func TCPListener(addr string) (Listener, error) {
ln, err := net.Listen("tcp", addr) ln, err := net.Listen("tcp", addr)
if err != nil { if err != nil {

5
gost/signal.go Normal file
View File

@ -0,0 +1,5 @@
// +build windows
package gost
func kcpSigHandler() {}

24
gost/signal_unix.go Normal file
View File

@ -0,0 +1,24 @@
// +build !windows
package gost
import (
"os"
"os/signal"
"syscall"
"github.com/go-log/log"
"gopkg.in/xtaci/kcp-go.v2"
)
func kcpSigHandler() {
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGUSR1)
for {
switch <-ch {
case syscall.SIGUSR1:
log.Logf("[kcp] SNMP: %+v", kcp.DefaultSnmp.Copy())
}
}
}

View File

@ -18,12 +18,15 @@ import (
) )
const ( const (
MethodTLS uint8 = 0x80 // extended method for tls // MethodTLS is an extended SOCKS5 method for TLS.
MethodTLSAuth uint8 = 0x82 // extended method for tls+auth MethodTLS uint8 = 0x80
// MethodTLSAuth is an extended SOCKS5 method for TLS+AUTH.
MethodTLSAuth uint8 = 0x82
) )
const ( const (
CmdUdpTun uint8 = 0xF3 // extended method for udp over tcp // CmdUDPTun is an extended SOCKS5 method for UDP over TCP.
CmdUDPTun uint8 = 0xF3
) )
type clientSelector struct { type clientSelector struct {
@ -189,6 +192,8 @@ type socks5Connector struct {
User *url.Userinfo User *url.Userinfo
} }
// SOCKS5Connector creates a connector for SOCKS5 proxy client.
// It accepts an optional auth info for SOCKS5 Username/Password Authentication.
func SOCKS5Connector(user *url.Userinfo) Connector { func SOCKS5Connector(user *url.Userinfo) Connector {
return &socks5Connector{User: user} return &socks5Connector{User: user}
} }
@ -246,6 +251,7 @@ func (c *socks5Connector) Connect(conn net.Conn, addr string) (net.Conn, error)
type socks4Connector struct{} type socks4Connector struct{}
// SOCKS4Connector creates a Connector for SOCKS4 proxy client.
func SOCKS4Connector() Connector { func SOCKS4Connector() Connector {
return &socks4Connector{} return &socks4Connector{}
} }
@ -289,6 +295,7 @@ func (c *socks4Connector) Connect(conn net.Conn, addr string) (net.Conn, error)
type socks4aConnector struct{} type socks4aConnector struct{}
// SOCKS4AConnector creates a Connector for SOCKS4A proxy client.
func SOCKS4AConnector() Connector { func SOCKS4AConnector() Connector {
return &socks4aConnector{} return &socks4aConnector{}
} }
@ -331,7 +338,7 @@ type socks5Handler struct {
options *HandlerOptions options *HandlerOptions
} }
// SOCKS5Handler returns a SOCKS5 server handler // SOCKS5Handler creates a server Handler for SOCKS5 proxy server.
func SOCKS5Handler(opts ...HandlerOption) Handler { func SOCKS5Handler(opts ...HandlerOption) Handler {
options := &HandlerOptions{ options := &HandlerOptions{
Chain: new(Chain), Chain: new(Chain),
@ -381,7 +388,7 @@ func (h *socks5Handler) Handle(conn net.Conn) {
log.Logf("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr) log.Logf("[socks5-udp] %s - %s", conn.RemoteAddr(), req.Addr)
h.handleUDPRelay(conn, req) h.handleUDPRelay(conn, req)
case CmdUdpTun: case CmdUDPTun:
log.Logf("[socks5-rudp] %s - %s", conn.RemoteAddr(), req.Addr) log.Logf("[socks5-rudp] %s - %s", conn.RemoteAddr(), req.Addr)
h.handleUDPTunnel(conn, req) h.handleUDPTunnel(conn, req)
@ -613,7 +620,7 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
defer cc.Close() defer cc.Close()
cc.SetWriteDeadline(time.Now().Add(WriteTimeout)) cc.SetWriteDeadline(time.Now().Add(WriteTimeout))
r := gosocks5.NewRequest(CmdUdpTun, nil) r := gosocks5.NewRequest(CmdUDPTun, nil)
if err := r.Write(cc); err != nil { if err := r.Write(cc); err != nil {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err) log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), cc.RemoteAddr(), err)
return return
@ -647,8 +654,8 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr) log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr)
} }
func (s *socks5Handler) discardClientData(conn net.Conn) (err error) { func (h *socks5Handler) discardClientData(conn net.Conn) (err error) {
b := make([]byte, TinyBufferSize) b := make([]byte, tinyBufferSize)
n := 0 n := 0
for { for {
n, err = conn.Read(b) // discard any data from tcp connection n, err = conn.Read(b) // discard any data from tcp connection
@ -663,13 +670,13 @@ func (s *socks5Handler) discardClientData(conn net.Conn) (err error) {
return return
} }
func (s *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) { func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
errc := make(chan error, 2) errc := make(chan error, 2)
var clientAddr *net.UDPAddr var clientAddr *net.UDPAddr
go func() { go func() {
b := make([]byte, LargeBufferSize) b := make([]byte, largeBufferSize)
for { for {
n, laddr, err := relay.ReadFromUDP(b) n, laddr, err := relay.ReadFromUDP(b)
@ -701,7 +708,7 @@ func (s *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
}() }()
go func() { go func() {
b := make([]byte, LargeBufferSize) b := make([]byte, largeBufferSize)
for { for {
n, raddr, err := peer.ReadFromUDP(b) n, raddr, err := peer.ReadFromUDP(b)
@ -739,7 +746,7 @@ func (h *socks5Handler) tunnelClientUDP(uc *net.UDPConn, cc net.Conn) (err error
var clientAddr *net.UDPAddr var clientAddr *net.UDPAddr
go func() { go func() {
b := make([]byte, LargeBufferSize) b := make([]byte, largeBufferSize)
for { for {
n, addr, err := uc.ReadFromUDP(b) n, addr, err := uc.ReadFromUDP(b)
@ -864,7 +871,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
errc := make(chan error, 2) errc := make(chan error, 2)
go func() { go func() {
b := make([]byte, LargeBufferSize) b := make([]byte, largeBufferSize)
for { for {
n, addr, err := uc.ReadFromUDP(b) n, addr, err := uc.ReadFromUDP(b)
@ -939,7 +946,7 @@ type socks4Handler struct {
options *HandlerOptions options *HandlerOptions
} }
// SOCKS4Handler returns a SOCKS4 server handler // SOCKS4Handler creates a server Handler for SOCKS4(A) proxy server.
func SOCKS4Handler(opts ...HandlerOption) Handler { func SOCKS4Handler(opts ...HandlerOption) Handler {
options := &HandlerOptions{ options := &HandlerOptions{
Chain: new(Chain), Chain: new(Chain),

View File

@ -30,6 +30,8 @@ func main() {
go wsServer(&wg) go wsServer(&wg)
wg.Add(1) wg.Add(1)
go wssServer(&wg) go wssServer(&wg)
wg.Add(1)
go kcpServer(&wg)
wg.Wait() wg.Wait()
} }
@ -131,3 +133,15 @@ func wssServer(wg *sync.WaitGroup) {
} }
log.Fatal(s.Serve(ln)) log.Fatal(s.Serve(ln))
} }
func kcpServer(wg *sync.WaitGroup) {
defer wg.Done()
s := &gost.Server{}
s.Handle(gost.HTTPHandler())
ln, err := gost.KCPListener(":8388", nil)
if err != nil {
log.Fatal(err)
}
log.Fatal(s.Serve(ln))
}

View File

@ -14,7 +14,7 @@ import (
) )
// Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write, // Due to in/out byte length is inconsistent of the shadowsocks.Conn.Write,
// we wrap around it to make io.Copy happy // we wrap around it to make io.Copy happy.
type shadowConn struct { type shadowConn struct {
conn net.Conn conn net.Conn
} }
@ -57,6 +57,9 @@ type shadowConnector struct {
Cipher *url.Userinfo Cipher *url.Userinfo
} }
// ShadowConnector creates a Connector for shadowsocks proxy client.
// It accepts a cipher info for shadowsocks data encryption/decryption.
// The cipher must not be nil.
func ShadowConnector(cipher *url.Userinfo) Connector { func ShadowConnector(cipher *url.Userinfo) Connector {
return &shadowConnector{Cipher: cipher} return &shadowConnector{Cipher: cipher}
} }
@ -89,6 +92,7 @@ type shadowHandler struct {
options *HandlerOptions options *HandlerOptions
} }
// ShadowHandler creates a server Handler for shadowsocks proxy server.
func ShadowHandler(opts ...HandlerOption) Handler { func ShadowHandler(opts ...HandlerOption) Handler {
h := &shadowHandler{ h := &shadowHandler{
options: &HandlerOptions{ options: &HandlerOptions{
@ -158,7 +162,7 @@ func (h *shadowHandler) getRequest(conn net.Conn) (host string, err error) {
// buf size should at least have the same size with the largest possible // buf size should at least have the same size with the largest possible
// request size (when addrType is 3, domain name has at most 256 bytes) // request size (when addrType is 3, domain name has at most 256 bytes)
// 1(addrType) + 1(lenByte) + 256(max length address) + 2(port) // 1(addrType) + 1(lenByte) + 256(max length address) + 2(port)
buf := make([]byte, SmallBufferSize) buf := make([]byte, smallBufferSize)
// read till we get possible domain length field // read till we get possible domain length field
conn.SetReadDeadline(time.Now().Add(30 * time.Second)) conn.SetReadDeadline(time.Now().Add(30 * time.Second))

View File

@ -9,12 +9,14 @@ type tlsTransporter struct {
TLSClientConfig *tls.Config TLSClientConfig *tls.Config
} }
// TLSTransporter creates a Transporter that is used by TLS proxy client.
// 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{TLSClientConfig: cfg}
} }
func (tr *tlsTransporter) Network() string { func (tr *tlsTransporter) Dial(addr string) (net.Conn, error) {
return "tcp" return net.Dial("tcp", addr)
} }
func (tr *tlsTransporter) Handshake(conn net.Conn) (net.Conn, error) { func (tr *tlsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
@ -25,6 +27,7 @@ type tlsListener struct {
net.Listener net.Listener
} }
// TLSListener creates a Listener for TLS proxy server.
func TLSListener(addr string, config *tls.Config) (Listener, error) { func TLSListener(addr string, config *tls.Config) (Listener, error) {
ln, err := tls.Listen("tcp", addr, config) ln, err := tls.Listen("tcp", addr, config)
if err != nil { if err != nil {

View File

@ -13,6 +13,7 @@ import (
"gopkg.in/gorilla/websocket.v1" "gopkg.in/gorilla/websocket.v1"
) )
// WSOptions describes the options for websocket.
type WSOptions struct { type WSOptions struct {
ReadBufferSize int ReadBufferSize int
WriteBufferSize int WriteBufferSize int
@ -82,11 +83,11 @@ func (c *websocketConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr() return c.conn.RemoteAddr()
} }
func (conn *websocketConn) SetDeadline(t time.Time) error { func (c *websocketConn) SetDeadline(t time.Time) error {
if err := conn.SetReadDeadline(t); err != nil { if err := c.SetReadDeadline(t); err != nil {
return err return err
} }
return conn.SetWriteDeadline(t) return c.SetWriteDeadline(t)
} }
func (c *websocketConn) SetReadDeadline(t time.Time) error { func (c *websocketConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t) return c.conn.SetReadDeadline(t)
@ -101,6 +102,7 @@ type wsTransporter struct {
options *WSOptions options *WSOptions
} }
// WSTransporter creates a Transporter that is used by websocket proxy client.
func WSTransporter(addr string, opts *WSOptions) Transporter { func WSTransporter(addr string, opts *WSOptions) Transporter {
return &wsTransporter{ return &wsTransporter{
addr: addr, addr: addr,
@ -108,8 +110,9 @@ func WSTransporter(addr string, opts *WSOptions) Transporter {
} }
} }
func (tr *wsTransporter) Network() string { func (tr *wsTransporter) Dial(addr string) (net.Conn, error) {
return "tcp" tr.addr = addr // NOTE: the addr must match the initial tr.addr
return net.Dial("tcp", addr)
} }
func (tr *wsTransporter) Handshake(conn net.Conn) (net.Conn, error) { func (tr *wsTransporter) Handshake(conn net.Conn) (net.Conn, error) {
@ -122,6 +125,7 @@ type wssTransporter struct {
options *WSOptions options *WSOptions
} }
// WSSTransporter creates a Transporter that is used by websocket secure proxy client.
func WSSTransporter(addr string, opts *WSOptions) Transporter { func WSSTransporter(addr string, opts *WSOptions) Transporter {
return &wssTransporter{ return &wssTransporter{
addr: addr, addr: addr,
@ -129,8 +133,8 @@ func WSSTransporter(addr string, opts *WSOptions) Transporter {
} }
} }
func (tr *wssTransporter) Network() string { func (tr *wssTransporter) Dial(addr string) (net.Conn, error) {
return "tcp" return net.Dial("tcp", addr)
} }
func (tr *wssTransporter) Handshake(conn net.Conn) (net.Conn, error) { func (tr *wssTransporter) Handshake(conn net.Conn) (net.Conn, error) {
@ -146,6 +150,7 @@ type wsListener struct {
errChan chan error errChan chan error
} }
// WSListener creates a Listener for websocket proxy server.
func WSListener(addr string, options *WSOptions) (Listener, error) { func WSListener(addr string, options *WSOptions) (Listener, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr) tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil { if err != nil {
@ -162,7 +167,7 @@ func WSListener(addr string, options *WSOptions) (Listener, error) {
CheckOrigin: func(r *http.Request) bool { return true }, CheckOrigin: func(r *http.Request) bool { return true },
EnableCompression: options.EnableCompression, EnableCompression: options.EnableCompression,
}, },
connChan: make(chan net.Conn, 32), connChan: make(chan net.Conn, 128),
errChan: make(chan error, 1), errChan: make(chan error, 1),
} }
@ -202,7 +207,11 @@ func (l *wsListener) upgrade(w http.ResponseWriter, r *http.Request) {
log.Logf("[ws] %s - %s : %s", r.RemoteAddr, l.addr, err) log.Logf("[ws] %s - %s : %s", r.RemoteAddr, l.addr, err)
return return
} }
l.connChan <- websocketServerConn(conn) select {
case l.connChan <- websocketServerConn(conn):
default:
log.Logf("[ws] %s - %s: connection queue is full", r.RemoteAddr, l.addr)
}
} }
func (l *wsListener) Accept() (conn net.Conn, err error) { func (l *wsListener) Accept() (conn net.Conn, err error) {
@ -225,6 +234,7 @@ type wssListener struct {
*wsListener *wsListener
} }
// WSSListener creates a Listener for websocket secure proxy server.
func WSSListener(addr string, options *WSOptions) (Listener, error) { func WSSListener(addr string, options *WSOptions) (Listener, error) {
tcpAddr, err := net.ResolveTCPAddr("tcp", addr) tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil { if err != nil {
@ -242,7 +252,7 @@ func WSSListener(addr string, options *WSOptions) (Listener, error) {
CheckOrigin: func(r *http.Request) bool { return true }, CheckOrigin: func(r *http.Request) bool { return true },
EnableCompression: options.EnableCompression, EnableCompression: options.EnableCompression,
}, },
connChan: make(chan net.Conn, 32), connChan: make(chan net.Conn, 128),
errChan: make(chan error, 1), errChan: make(chan error, 1),
}, },
} }