diff --git a/main.go b/main.go index 281b6a8a..196eb018 100644 --- a/main.go +++ b/main.go @@ -21,6 +21,7 @@ import ( var config struct { Verbose bool UDPTimeout time.Duration + TCPCork bool } func main() { @@ -61,6 +62,7 @@ func main() { flag.StringVar(&flags.PluginOpts, "plugin-opts", "", "Set SIP003 plugin options. (e.g., \"server;tls;host=mydomain.me\")") flag.BoolVar(&flags.UDP, "udp", false, "(server-only) enable UDP support") flag.BoolVar(&flags.UDP, "tcp", true, "(server-only) enable TCP support") + flag.BoolVar(&config.TCPCork, "tcpcork", false, "(client-only) enable TCP_CORK (Linux) or TCP_NOPUSH (BSD) for the first few packets") flag.DurationVar(&config.UDPTimeout, "udptimeout", 5*time.Minute, "UDP tunnel timeout") flag.Parse() diff --git a/tcp.go b/tcp.go index 243b2704..b47e3c3b 100644 --- a/tcp.go +++ b/tcp.go @@ -70,7 +70,11 @@ func tcpLocal(addr, server string, shadow func(net.Conn) net.Conn, getAddr func( return } defer rc.Close() - rc.(*net.TCPConn).SetKeepAlive(true) + tc := rc.(*net.TCPConn) + tc.SetKeepAlive(true) + if config.TCPCork { + timedCork(tc, 10*time.Millisecond) + } rc = shadow(rc) if _, err = rc.Write(tgt); err != nil { diff --git a/tcp_darwin.go b/tcp_darwin.go index 1497d5d8..29e5eb5b 100644 --- a/tcp_darwin.go +++ b/tcp_darwin.go @@ -2,6 +2,8 @@ package main import ( "net" + "syscall" + "time" "github.com/shadowsocks/go-shadowsocks2/pfutil" "github.com/shadowsocks/go-shadowsocks2/socks" @@ -22,3 +24,18 @@ func natLookup(c net.Conn) (socks.Addr, error) { } panic("not TCP connection") } + +func timedCork(c *net.TCPConn, d time.Duration) error { + rc, err := c.SyscallConn() + if err != nil { + return err + } + rc.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NOPUSH, 1) }) + if err != nil { + return err + } + time.AfterFunc(d, func() { + rc.Control(func(fd uintptr) { syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_NOPUSH, 0) }) + }) + return nil +} diff --git a/tcp_linux.go b/tcp_linux.go index 6f4d11e7..60612d66 100644 --- a/tcp_linux.go +++ b/tcp_linux.go @@ -2,6 +2,8 @@ package main import ( "net" + "syscall" + "time" "github.com/shadowsocks/go-shadowsocks2/nfutil" "github.com/shadowsocks/go-shadowsocks2/socks" @@ -26,3 +28,18 @@ func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) { logf("TCP6 redirect %s <-> %s", addr, server) tcpLocal(addr, server, shadow, func(c net.Conn) (socks.Addr, error) { return getOrigDst(c, true) }) } + +func timedCork(c *net.TCPConn, d time.Duration) error { + rc, err := c.SyscallConn() + if err != nil { + return err + } + rc.Control(func(fd uintptr) { err = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_CORK, 1) }) + if err != nil { + return err + } + time.AfterFunc(d, func() { + rc.Control(func(fd uintptr) { syscall.SetsockoptInt(int(fd), syscall.IPPROTO_TCP, syscall.TCP_CORK, 0) }) + }) + return nil +} diff --git a/tcp_other.go b/tcp_other.go index aa4549a7..e5cde9ad 100644 --- a/tcp_other.go +++ b/tcp_other.go @@ -2,7 +2,10 @@ package main -import "net" +import ( + "net" + "time" +) func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) { logf("TCP redirect not supported") @@ -11,3 +14,5 @@ func redirLocal(addr, server string, shadow func(net.Conn) net.Conn) { func redir6Local(addr, server string, shadow func(net.Conn) net.Conn) { logf("TCP6 redirect not supported") } + +func timedCork(c *net.TCPConn, d time.Duration) error { return nil }