diff --git a/cmd/gost/route.go b/cmd/gost/route.go index 21a34b7..8128fda 100644 --- a/cmd/gost/route.go +++ b/cmd/gost/route.go @@ -139,6 +139,13 @@ func parseChainNode(ns string) (nodes []gost.Node, err error) { if err != nil { return nil, err } + if config == nil { + conf := gost.DefaultKCPConfig + if node.GetBool("tcp") { + conf.TCP = true + } + config = &conf + } tr = gost.KCPTransporter(config) case "ssh": if node.Protocol == "direct" || node.Protocol == "remote" { @@ -322,6 +329,13 @@ func (r *route) GenRouters() ([]router, error) { if er != nil { return nil, er } + if config == nil { + conf := gost.DefaultKCPConfig + if node.GetBool("tcp") { + conf.TCP = true + } + config = &conf + } ln, err = gost.KCPListener(node.Addr, config) case "ssh": config := &gost.SSHConfig{ diff --git a/ftcp.go b/ftcp.go index 281255b..3d50007 100644 --- a/ftcp.go +++ b/ftcp.go @@ -99,11 +99,7 @@ func (l *fakeTCPListener) listenLoop() { conn, ok := l.connMap.Get(raddr.String()) if !ok { - cc := &fakeTCPConn{ - raddr: raddr, - PacketConn: l.ln, - } - conn = newUDPServerConn(cc, raddr, l.config.TTL, l.config.QueueSize) + conn = newUDPServerConn(l.ln, raddr, l.config.TTL, l.config.QueueSize) conn.onClose = func() { l.connMap.Delete(raddr.String()) log.Logf("[ftcp] %s closed (%d)", raddr, l.connMap.Size()) @@ -157,7 +153,6 @@ func (l *fakeTCPListener) Close() error { } type fakeTCPConn struct { - mss int raddr net.Addr net.PacketConn } @@ -171,33 +166,6 @@ func (c *fakeTCPConn) Write(b []byte) (n int, err error) { return c.WriteTo(b, c.raddr) } -func (c *fakeTCPConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { - mss := c.mss - if mss <= 0 { - mss = 1460 - } - - for { - if len(b) == 0 { - break - } - var nn int - if len(b) <= mss { - nn, err = c.PacketConn.WriteTo(b, addr) - n += nn - break - } - nn, err = c.PacketConn.WriteTo(b[:mss], addr) - n += nn - if err != nil { - break - } - b = b[mss:] - } - - return -} - func (c *fakeTCPConn) RemoteAddr() net.Addr { return c.raddr } diff --git a/kcp.go b/kcp.go index a793d55..9f1f6c5 100644 --- a/kcp.go +++ b/kcp.go @@ -15,6 +15,7 @@ import ( "github.com/go-log/log" "github.com/klauspost/compress/snappy" + "github.com/xtaci/tcpraw" "gopkg.in/xtaci/kcp-go.v4" "gopkg.in/xtaci/smux.v1" ) @@ -46,6 +47,7 @@ type KCPConfig struct { SnmpLog string `json:"snmplog"` SnmpPeriod int `json:"snmpperiod"` Signal bool `json:"signal"` // Signal enables the signal SIGUSR1 feature. + TCP bool `json:"tcp"` } // Init initializes the KCP config. @@ -85,6 +87,7 @@ var ( SnmpLog: "", SnmpPeriod: 60, Signal: false, + TCP: false, } ) @@ -129,13 +132,24 @@ func (tr *kcpTransporter) Dial(addr string, options ...DialOption) (conn net.Con ok = false } if !ok { - timeout := opts.Timeout - if timeout <= 0 { - timeout = DialTimeout - } - conn, err = net.DialTimeout("udp", addr, timeout) + raddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { - return + return nil, err + } + if tr.config.TCP { + pc, err := tcpraw.Dial("tcp", addr) + if err != nil { + return nil, err + } + conn = &fakeTCPConn{ + raddr: raddr, + PacketConn: pc, + } + } else { + conn, err = net.ListenUDP("udp", nil) + if err != nil { + return nil, err + } } session = &muxSession{conn: conn} tr.sessions[addr] = session @@ -184,14 +198,14 @@ func (tr *kcpTransporter) Handshake(conn net.Conn, options ...HandshakeOption) ( } func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPConfig) (*muxSession, error) { - udpConn, ok := conn.(*net.UDPConn) + pc, ok := conn.(net.PacketConn) if !ok { return nil, errors.New("kcp: wrong connection type") } kcpconn, err := kcp.NewConn(addr, blockCrypt(config.Key, config.Crypt, KCPSalt), - config.DataShard, config.ParityShard, &connectedUDPConn{udpConn}) + config.DataShard, config.ParityShard, pc) if err != nil { return nil, err } @@ -203,9 +217,11 @@ func (tr *kcpTransporter) initSession(addr string, conn net.Conn, config *KCPCon kcpconn.SetMtu(config.MTU) kcpconn.SetACKNoDelay(config.AckNodelay) - // if err := kcpconn.SetDSCP(config.DSCP); err != nil { - // log.Log("[kcp]", err) - // } + if config.DSCP > 0 { + if err := kcpconn.SetDSCP(config.DSCP); err != nil { + log.Log("[kcp]", err) + } + } if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil { log.Log("[kcp]", err) } @@ -247,14 +263,31 @@ func KCPListener(addr string, config *KCPConfig) (Listener, error) { } config.Init() - ln, err := kcp.ListenWithOptions(addr, - blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard) + var err error + var ln *kcp.Listener + if config.TCP { + var conn net.PacketConn + conn, err = tcpraw.Listen("tcp", addr) + if err != nil { + return nil, err + } + ln, err = kcp.ServeConn( + blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard, conn) + if err != nil { + return nil, err + } + } else { + ln, err = kcp.ListenWithOptions(addr, + blockCrypt(config.Key, config.Crypt, KCPSalt), config.DataShard, config.ParityShard) + } if err != nil { return nil, err } - // if err = ln.SetDSCP(config.DSCP); err != nil { - // log.Log("[kcp]", err) - // } + if config.DSCP > 0 { + if err = ln.SetDSCP(config.DSCP); err != nil { + log.Log("[kcp]", err) + } + } if err = ln.SetReadBuffer(config.SockBuf); err != nil { log.Log("[kcp]", err) } @@ -468,11 +501,3 @@ func (c *compStreamConn) SetReadDeadline(t time.Time) error { func (c *compStreamConn) SetWriteDeadline(t time.Time) error { return c.conn.SetWriteDeadline(t) } - -// connectedUDPConn is a wrapper for net.UDPConn which converts WriteTo syscalls -// to Write syscalls that are 4 times faster on some OS'es. This should only be -// used for connections that were produced by a net.Dial* call. -type connectedUDPConn struct{ *net.UDPConn } - -// WriteTo redirects all writes to the Write syscall, which is 4 times faster. -func (c *connectedUDPConn) WriteTo(b []byte, addr net.Addr) (int, error) { return c.Write(b) }