add more test cases for socks5

This commit is contained in:
ginuerzh 2018-12-31 20:44:20 +08:00
parent 72d7850c2c
commit 445889c47e
23 changed files with 1168 additions and 417 deletions

View File

@ -5,8 +5,8 @@ go:
install: true
script:
- env GO111MODULE=on go test -race -v -coverprofile=coverage.txt -covermode=atomic
- cd cmd/gost && env GO111MODULE=on go build
- go test -race -v -coverprofile=coverage.txt -covermode=atomic
- cd cmd/gost && go build
after_success:
- bash <(curl -s https://codecov.io/bash)

View File

@ -2,6 +2,7 @@ package gost
import (
"bytes"
"fmt"
"io"
"testing"
"time"
@ -158,10 +159,13 @@ var bypassContainTests = []struct {
func TestBypassContains(t *testing.T) {
for i, tc := range bypassContainTests {
bp := NewBypassPatterns(tc.reversed, tc.patterns...)
if bp.Contains(tc.addr) != tc.bypassed {
t.Errorf("#%d test failed: %v, %s", i, tc.patterns, tc.addr)
}
tc := tc
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
bp := NewBypassPatterns(tc.reversed, tc.patterns...)
if bp.Contains(tc.addr) != tc.bypassed {
t.Errorf("#%d test failed: %v, %s", i, tc.patterns, tc.addr)
}
})
}
}
@ -244,6 +248,9 @@ func TestByapssReload(t *testing.T) {
}
if tc.stopped {
bp.Stop()
if bp.Period() >= 0 {
t.Errorf("period of the stopped reloader should be minus value")
}
}
if bp.Stopped() != tc.stopped {
t.Errorf("#%d test failed: stopped value should be %v, got %v",

View File

@ -1,16 +1,18 @@
# resolver timeout, default 30s.
timeout 10s
# resolver cache TTL, default 60s, minus value means that cache is disabled.
ttl 300s
# resolver cache TTL,
# minus value means that cache is disabled,
# default to the TTL in DNS server response.
# ttl 300s
# period for live reloading
reload 10s
# ip[:port] [protocol] [hostname]
https://1.0.0.1/dns-query
1.1.1.1:853 tls cloudflare-dns.com
https://1.0.0.1/dns-query https
8.8.8.8
8.8.8.8 tcp
1.1.1.1 udp

View File

@ -10,7 +10,6 @@ import (
"net/url"
"os"
"strings"
"time"
"github.com/ginuerzh/gost"
)
@ -196,8 +195,6 @@ func parseResolver(cfg string) gost.Resolver {
if cfg == "" {
return nil
}
timeout := 30 * time.Second
ttl := 60 * time.Second
var nss []gost.NameServer
f, err := os.Open(cfg)
@ -237,11 +234,11 @@ func parseResolver(cfg string) gost.Resolver {
}
}
}
return gost.NewResolver(timeout, ttl, nss...)
return gost.NewResolver(0, nss...)
}
defer f.Close()
resolver := gost.NewResolver(timeout, ttl)
resolver := gost.NewResolver(0)
resolver.Reload(f)
go gost.PeriodReload(resolver, cfg)

View File

@ -1,12 +1,16 @@
package gost
import (
"bufio"
"bytes"
"crypto/tls"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"sync"
"time"
)
@ -37,6 +41,110 @@ var (
})
)
// proxyConn obtains a connection to the proxy server.
func proxyConn(client *Client, server *Server) (net.Conn, error) {
conn, err := client.Dial(server.Addr().String())
if err != nil {
return nil, err
}
cc, err := client.Handshake(conn, AddrHandshakeOption(server.Addr().String()))
if err != nil {
conn.Close()
return nil, err
}
return cc, nil
}
// httpRoundtrip does a HTTP request-response roundtrip, and checks the data received.
func httpRoundtrip(conn net.Conn, targetURL string, data []byte) (err error) {
req, err := http.NewRequest(
http.MethodGet,
targetURL,
bytes.NewReader(data),
)
if err != nil {
return
}
if err = req.Write(conn); err != nil {
return
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
recv, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return
}
func udpRoundtrip(client *Client, server *Server, host string, data []byte) (err error) {
conn, err := proxyConn(client, server)
if err != nil {
return
}
defer conn.Close()
conn, err = client.Connect(conn, host)
if err != nil {
return
}
conn.SetDeadline(time.Now().Add(3 * time.Second))
defer conn.SetDeadline(time.Time{})
if _, err = conn.Write(data); err != nil {
return
}
recv := make([]byte, len(data))
if _, err = conn.Read(recv); err != nil {
return
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return
}
func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) {
conn, err := proxyConn(client, server)
if err != nil {
return err
}
defer conn.Close()
u, err := url.Parse(targetURL)
if err != nil {
return
}
conn, err = client.Connect(conn, u.Host)
if err != nil {
return
}
conn.SetDeadline(time.Now().Add(500 * time.Millisecond))
defer conn.SetDeadline(time.Time{})
return httpRoundtrip(conn, targetURL, data)
}
type udpRequest struct {
Body io.Reader
RemoteAddr string
@ -55,11 +163,12 @@ type udpHandlerFunc func(w io.Writer, r *udpRequest)
// udpTestServer is a UDP server for test.
type udpTestServer struct {
ln net.PacketConn
handler udpHandlerFunc
wg sync.WaitGroup
mu sync.Mutex // guards closed and conns
closed bool
ln net.PacketConn
handler udpHandlerFunc
wg sync.WaitGroup
mu sync.Mutex // guards closed and conns
closed bool
exitChan chan struct{}
}
func newUDPTestServer(handler udpHandlerFunc) *udpTestServer {
@ -68,9 +177,13 @@ func newUDPTestServer(handler udpHandlerFunc) *udpTestServer {
if err != nil {
panic(fmt.Sprintf("udptest: failed to listen on a port: %v", err))
}
ln.SetReadBuffer(1024 * 1024)
ln.SetWriteBuffer(1024 * 1024)
return &udpTestServer{
ln: ln,
handler: handler,
ln: ln,
handler: handler,
exitChan: make(chan struct{}),
}
}
@ -83,7 +196,7 @@ func (s *udpTestServer) serve() {
data := make([]byte, 1024)
n, raddr, err := s.ln.ReadFrom(data)
if err != nil {
return
break
}
if s.handler != nil {
s.wg.Add(1)
@ -101,6 +214,9 @@ func (s *udpTestServer) serve() {
}()
}
}
// signal the listener has been exited.
close(s.exitChan)
}
func (s *udpTestServer) Addr() string {
@ -119,6 +235,8 @@ func (s *udpTestServer) Close() error {
s.closed = true
s.mu.Unlock()
<-s.exitChan
s.wg.Wait()
return err

View File

@ -662,10 +662,6 @@ func TCPRemoteForwardListener(addr string, chain *Chain) (Listener, error) {
go ln.listenLoop()
// if err = <-ln.errChan; err != nil {
// ln.Close()
// }
return ln, err
}
@ -680,17 +676,10 @@ func (l *tcpRemoteForwardListener) isChainValid() bool {
func (l *tcpRemoteForwardListener) listenLoop() {
var tempDelay time.Duration
// var once sync.Once
for {
conn, err := l.accept()
// once.Do(func() {
// l.errChan <- err
// log.Log("once.Do error:", err)
// close(l.errChan)
// })
select {
case <-l.closed:
if conn != nil {

View File

@ -1,13 +1,10 @@
package gost
import (
"bytes"
"crypto/rand"
"fmt"
"net/http/httptest"
"net/url"
"testing"
"time"
)
func tcpDirectForwardRoundtrip(targetURL string, data []byte) error {
@ -122,37 +119,6 @@ func BenchmarkTCPDirectForwardParallel(b *testing.B) {
})
}
func udpRoundtrip(client *Client, server *Server, host string, data []byte) (err error) {
conn, err := proxyConn(client, server)
if err != nil {
return
}
defer conn.Close()
conn.SetDeadline(time.Now().Add(1 * time.Second))
defer conn.SetDeadline(time.Time{})
conn, err = client.Connect(conn, host)
if err != nil {
return
}
if _, err = conn.Write(data); err != nil {
return
}
recv := make([]byte, len(data))
if _, err = conn.Read(recv); err != nil {
return
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return
}
func udpDirectForwardRoundtrip(host string, data []byte) error {
ln, err := UDPDirectForwardListener("localhost:0", 0)
if err != nil {

47
gost.go
View File

@ -7,8 +7,10 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"io"
"math/big"
"net"
"sync"
"time"
@ -137,3 +139,48 @@ func (rw *readWriter) Read(p []byte) (n int, err error) {
func (rw *readWriter) Write(p []byte) (n int, err error) {
return rw.w.Write(p)
}
var (
nopClientConn = &nopConn{}
)
// a nop connection implements net.Conn,
// it does nothing.
type nopConn struct{}
func (c *nopConn) Read(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "read", Net: "nop", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}
func (c *nopConn) Write(b []byte) (n int, err error) {
return 0, &net.OpError{Op: "write", Net: "nop", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}
func (c *nopConn) Close() error {
return nil
}
func (c *nopConn) LocalAddr() net.Addr {
return nil
}
func (c *nopConn) RemoteAddr() net.Addr {
return nil
}
func (c *nopConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *nopConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *nopConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
// Accepter represents a network endpoint that can accept connection from peer.
type Accepter interface {
Accept() (net.Conn, error)
}

View File

@ -18,7 +18,6 @@ func autoHTTPProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Useri
Connector: HTTPConnector(clientInfo),
Transporter: TCPTransporter(),
}
server := &Server{
Listener: ln,
Handler: AutoHandler(
@ -111,7 +110,7 @@ func TestAutoSOCKS5Proxy(t *testing.T) {
}
}
func autoSOCKS4ProxyRoundtrip(targetURL string, data []byte) error {
func autoSOCKS4ProxyRoundtrip(targetURL string, data []byte, options ...HandlerOption) error {
ln, err := TCPListener("")
if err != nil {
return err
@ -124,7 +123,7 @@ func autoSOCKS4ProxyRoundtrip(targetURL string, data []byte) error {
server := &Server{
Listener: ln,
Handler: AutoHandler(),
Handler: AutoHandler(options...),
}
go server.Run()
defer server.Close()
@ -139,14 +138,17 @@ func TestAutoSOCKS4Proxy(t *testing.T) {
sendData := make([]byte, 128)
rand.Read(sendData)
err := autoSOCKS4ProxyRoundtrip(httpSrv.URL, sendData)
// t.Logf("#%d %v", i, err)
if err != nil {
if err := autoSOCKS4ProxyRoundtrip(httpSrv.URL, sendData); err != nil {
t.Errorf("got error: %v", err)
}
if err := autoSOCKS4ProxyRoundtrip(httpSrv.URL, sendData,
UsersHandlerOption(url.UserPassword("admin", "123456"))); err == nil {
t.Errorf("authentication required auto handler for SOCKS4 should failed")
}
}
func autoSocks4aProxyRoundtrip(targetURL string, data []byte) error {
func autoSocks4aProxyRoundtrip(targetURL string, data []byte, options ...HandlerOption) error {
ln, err := TCPListener("")
if err != nil {
return err
@ -159,7 +161,7 @@ func autoSocks4aProxyRoundtrip(targetURL string, data []byte) error {
server := &Server{
Listener: ln,
Handler: AutoHandler(),
Handler: AutoHandler(options...),
}
go server.Run()
@ -175,11 +177,14 @@ func TestAutoSOCKS4AProxy(t *testing.T) {
sendData := make([]byte, 128)
rand.Read(sendData)
err := autoSocks4aProxyRoundtrip(httpSrv.URL, sendData)
// t.Logf("#%d %v", i, err)
if err != nil {
if err := autoSocks4aProxyRoundtrip(httpSrv.URL, sendData); err != nil {
t.Errorf("got error: %v", err)
}
if err := autoSocks4aProxyRoundtrip(httpSrv.URL, sendData,
UsersHandlerOption(url.UserPassword("admin", "123456"))); err == nil {
t.Errorf("authentication required auto handler for SOCKS4A should failed")
}
}
func autoSSProxyRoundtrip(targetURL string, data []byte, clientInfo *url.Userinfo, serverInfo *url.Userinfo) error {

View File

@ -28,7 +28,8 @@ var hostsLookupTests = []struct {
func TestHostsLookup(t *testing.T) {
for i, tc := range hostsLookupTests {
hosts := NewHosts(tc.hosts...)
hosts := NewHosts()
hosts.AddHost(tc.hosts...)
ip := hosts.Lookup(tc.host)
if !ip.Equal(tc.ip) {
t.Errorf("#%d test failed: lookup should be %s, got %s", i, tc.ip, ip)
@ -61,6 +62,11 @@ var HostsReloadTests = []struct {
host: "example.com",
ip: nil,
},
{
r: bytes.NewBufferString("#reload 10s\ninvalid.ip.addr example.com"),
period: 0,
ip: nil,
},
{
r: bytes.NewBufferString("reload 10s\n192.168.1.1"),
period: 10 * time.Second,
@ -112,6 +118,9 @@ func TestHostsReload(t *testing.T) {
}
if tc.stopped {
hosts.Stop()
if hosts.Period() >= 0 {
t.Errorf("period of the stopped reloader should be minus value")
}
}
if hosts.Stopped() != tc.stopped {
t.Errorf("#%d test failed: stopped value should be %v, got %v",

View File

@ -569,12 +569,13 @@ func HTTP2Listener(addr string, config *tls.Config) (Listener, error) {
}
l.server = server
ln, err := tls.Listen("tcp", addr, config)
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
l.addr = ln.Addr()
ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
go func() {
err := server.Serve(ln)
if err != nil {
@ -875,42 +876,11 @@ func (c *http2ServerConn) SetWriteDeadline(t time.Time) error {
// a dummy HTTP2 client conn used by HTTP2 client connector
type http2ClientConn struct {
nopConn
addr string
client *http.Client
}
func (c *http2ClientConn) 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 *http2ClientConn) 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 *http2ClientConn) Close() error {
return nil
}
func (c *http2ClientConn) LocalAddr() net.Addr {
return nil
}
func (c *http2ClientConn) RemoteAddr() net.Addr {
return nil
}
func (c *http2ClientConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *http2ClientConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "http2", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func (c *http2ClientConn) 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
}

View File

@ -1,93 +1,12 @@
package gost
import (
"bufio"
"bytes"
"crypto/rand"
"errors"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
)
// proxyConn obtains a connection to the proxy server.
func proxyConn(client *Client, server *Server) (net.Conn, error) {
conn, err := client.Dial(server.Addr().String())
if err != nil {
return nil, err
}
cc, err := client.Handshake(conn, AddrHandshakeOption(server.Addr().String()))
if err != nil {
conn.Close()
return nil, err
}
return cc, nil
}
// httpRoundtrip does a HTTP request-response roundtrip, and checks the data received.
func httpRoundtrip(conn net.Conn, targetURL string, data []byte) (err error) {
req, err := http.NewRequest(
http.MethodGet,
targetURL,
bytes.NewReader(data),
)
if err != nil {
return
}
if err = req.Write(conn); err != nil {
return
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
recv, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return
}
func proxyRoundtrip(client *Client, server *Server, targetURL string, data []byte) (err error) {
conn, err := proxyConn(client, server)
if err != nil {
return err
}
defer conn.Close()
u, err := url.Parse(targetURL)
if err != nil {
return
}
conn, err = client.Connect(conn, u.Host)
if err != nil {
return
}
conn.SetDeadline(time.Now().Add(500 * time.Millisecond))
defer conn.SetDeadline(time.Time{})
return httpRoundtrip(conn, targetURL, data)
}
var httpProxyTests = []struct {
cliUser *url.Userinfo
srvUsers []*url.Userinfo

5
kcp.go
View File

@ -123,6 +123,11 @@ func (tr *kcpTransporter) Dial(addr string, options ...DialOption) (conn net.Con
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if session != nil && session.session != nil && session.session.IsClosed() {
session.Close()
delete(tr.sessions, addr) // session is dead
ok = false
}
if !ok {
timeout := opts.Timeout
if timeout <= 0 {

6
mux.go
View File

@ -45,10 +45,16 @@ func (session *muxSession) Accept() (net.Conn, error) {
}
func (session *muxSession) Close() error {
if session.session == nil {
return nil
}
return session.session.Close()
}
func (session *muxSession) IsClosed() bool {
if session.session == nil {
return true
}
return session.session.IsClosed()
}

View File

@ -331,7 +331,7 @@ func Obfs4Listener(addr string) (Listener, error) {
}
l := &obfs4Listener{
addr: addr,
Listener: ln,
Listener: tcpKeepAliveListener{ln.(*net.TCPListener)},
}
return l, nil
}

View File

@ -2,6 +2,7 @@ package gost
import (
"crypto/rand"
"crypto/sha256"
"fmt"
"net/http/httptest"
"net/url"
@ -405,3 +406,57 @@ func TestQUICForwardTunnel(t *testing.T) {
t.Error(err)
}
}
func httpOverCipherQUICRoundtrip(targetURL string, data []byte,
clientInfo *url.Userinfo, serverInfo []*url.Userinfo) error {
sum := sha256.Sum256([]byte("12345678"))
cfg := &QUICConfig{
Key: sum[:],
}
ln, err := QUICListener("localhost:0", cfg)
if err != nil {
return err
}
client := &Client{
Connector: HTTPConnector(clientInfo),
Transporter: QUICTransporter(cfg),
}
server := &Server{
Listener: ln,
Handler: HTTPHandler(
UsersHandlerOption(serverInfo...),
),
}
go server.Run()
defer server.Close()
return proxyRoundtrip(client, server, targetURL, data)
}
func TestHTTPOverCipherQUIC(t *testing.T) {
httpSrv := httptest.NewServer(httpTestHandler)
defer httpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
for i, tc := range httpProxyTests {
err := httpOverCipherQUICRoundtrip(httpSrv.URL, sendData, tc.cliUser, tc.srvUsers)
if err == nil {
if tc.errStr != "" {
t.Errorf("#%d should failed with error %s", i, tc.errStr)
}
} else {
if tc.errStr == "" {
t.Errorf("#%d got error %v", i, err)
}
if err.Error() != tc.errStr {
t.Errorf("#%d got error %v, want %v", i, err, tc.errStr)
}
}
}
}

View File

@ -23,8 +23,6 @@ import (
var (
// DefaultResolverTimeout is the default timeout for name resolution.
DefaultResolverTimeout = 5 * time.Second
// DefaultResolverTTL is the default cache TTL for name resolution.
DefaultResolverTTL = 1 * time.Hour
)
// Resolver is a name resolver for domain name.
@ -53,13 +51,18 @@ type NameServer struct {
// Init initializes the name server.
func (ns *NameServer) Init() error {
timeout := ns.Timeout
if timeout <= 0 {
timeout = DefaultResolverTimeout
}
switch strings.ToLower(ns.Protocol) {
case "tcp":
ns.exchanger = &dnsExchanger{
endpoint: ns.Addr,
client: &dns.Client{
Net: "tcp",
Timeout: ns.Timeout,
Timeout: timeout,
},
}
case "tls":
@ -74,7 +77,7 @@ func (ns *NameServer) Init() error {
endpoint: ns.Addr,
client: &dns.Client{
Net: "tcp-tls",
Timeout: ns.Timeout,
Timeout: timeout,
TLSConfig: cfg,
},
}
@ -95,7 +98,7 @@ func (ns *NameServer) Init() error {
endpoint: u,
client: &http.Client{
Transport: transport,
Timeout: ns.Timeout,
Timeout: timeout,
},
}
case "udp":
@ -105,7 +108,7 @@ func (ns *NameServer) Init() error {
endpoint: ns.Addr,
client: &dns.Client{
Net: "udp",
Timeout: ns.Timeout,
Timeout: timeout,
},
}
}
@ -125,15 +128,9 @@ func (ns NameServer) String() string {
return fmt.Sprintf("%s/%s", addr, prot)
}
type resolverCacheItem struct {
IPs []net.IP
ts int64
}
type resolver struct {
Servers []NameServer
mCache *sync.Map
Timeout time.Duration
TTL time.Duration
period time.Duration
domain string
@ -142,22 +139,14 @@ type resolver struct {
}
// NewResolver create a new Resolver with the given name servers and resolution timeout.
func NewResolver(timeout, ttl time.Duration, servers ...NameServer) ReloadResolver {
r := newResolver(timeout, ttl, servers...)
if r.Timeout <= 0 {
r.Timeout = DefaultResolverTimeout
}
if r.TTL == 0 {
r.TTL = DefaultResolverTTL
}
func NewResolver(ttl time.Duration, servers ...NameServer) ReloadResolver {
r := newResolver(ttl, servers...)
return r
}
func newResolver(timeout, ttl time.Duration, servers ...NameServer) *resolver {
func newResolver(ttl time.Duration, servers ...NameServer) *resolver {
return &resolver{
Servers: servers,
Timeout: timeout,
TTL: ttl,
mCache: &sync.Map{},
stopped: make(chan struct{}),
@ -204,25 +193,25 @@ func (r *resolver) Resolve(host string) (ips []net.IP, err error) {
}
for _, ns := range servers {
ips, err = r.resolve(ns.exchanger, host)
ips, ttl, err = r.resolve(ns.exchanger, host)
if err != nil {
log.Logf("[resolver] %s via %s : %s", host, ns, err)
continue
}
if Debug {
log.Logf("[resolver] %s via %s %v", host, ns, ips)
log.Logf("[resolver] %s via %s %v(ttl: %v)", host, ns, ips, ttl)
}
if len(ips) > 0 {
break
}
}
r.storeCache(host, ips)
r.storeCache(host, ips, ttl)
return
}
func (*resolver) resolve(ex Exchanger, host string) (ips []net.IP, err error) {
func (*resolver) resolve(ex Exchanger, host string) (ips []net.IP, ttl time.Duration, err error) {
if ex == nil {
return
}
@ -236,11 +225,18 @@ func (*resolver) resolve(ex Exchanger, host string) (ips []net.IP, err error) {
for _, ans := range mr.Answer {
if ar, _ := ans.(*dns.A); ar != nil {
ips = append(ips, ar.A)
ttl = time.Duration(ar.Header().Ttl) * time.Second
}
}
return
}
type resolverCacheItem struct {
IPs []net.IP
ts int64
ttl time.Duration
}
func (r *resolver) loadCache(name string, ttl time.Duration) []net.IP {
if ttl < 0 {
return nil
@ -248,6 +244,10 @@ func (r *resolver) loadCache(name string, ttl time.Duration) []net.IP {
if v, ok := r.mCache.Load(name); ok {
item, _ := v.(*resolverCacheItem)
if ttl == 0 {
ttl = item.ttl
}
if item == nil || time.Since(time.Unix(item.ts, 0)) > ttl {
return nil
}
@ -257,13 +257,14 @@ func (r *resolver) loadCache(name string, ttl time.Duration) []net.IP {
return nil
}
func (r *resolver) storeCache(name string, ips []net.IP) {
func (r *resolver) storeCache(name string, ips []net.IP, ttl time.Duration) {
if name == "" || len(ips) == 0 {
return
}
r.mCache.Store(name, &resolverCacheItem{
IPs: ips,
ts: time.Now().Unix(),
ttl: ttl,
})
}
@ -343,10 +344,10 @@ func (r *resolver) Reload(rd io.Reader) error {
ns.Hostname = ss[2]
}
ns.Timeout = timeout
if timeout <= 0 {
ns.Timeout = DefaultResolverTimeout
if strings.HasPrefix(ns.Addr, "https") {
ns.Protocol = "https"
}
ns.Timeout = timeout
if err := ns.Init(); err == nil {
nss = append(nss, ns)
@ -359,7 +360,6 @@ func (r *resolver) Reload(rd io.Reader) error {
}
r.mux.Lock()
r.Timeout = timeout
r.TTL = ttl
r.domain = domain
r.period = period
@ -408,9 +408,9 @@ func (r *resolver) String() string {
defer r.mux.RUnlock()
b := &bytes.Buffer{}
fmt.Fprintf(b, "Timeout %v\n", r.Timeout)
fmt.Fprintf(b, "TTL %v\n", r.TTL)
fmt.Fprintf(b, "Reload %v\n", r.period)
fmt.Fprintf(b, "Domain %v\n", r.domain)
for i := range r.Servers {
fmt.Fprintln(b, r.Servers[i])
}

View File

@ -46,7 +46,7 @@ func TestDNSResolver(t *testing.T) {
t.Error(err)
}
t.Log(ns)
r := NewResolver(0, 0, ns)
r := NewResolver(0, ns)
err := dnsResolverRoundtrip(t, r, tc.host)
if err != nil {
if tc.pass {
@ -103,13 +103,12 @@ var resolverReloadTests = []struct {
{
r: bytes.NewBufferString("1.1.1.1"),
ns: &NameServer{
Addr: "1.1.1.1",
Timeout: DefaultResolverTimeout,
Addr: "1.1.1.1",
},
stopped: true,
},
{
r: bytes.NewBufferString("timeout 10s\nsearch\nnameserver \nnameserver 1.1.1.1 udp"),
r: bytes.NewBufferString("\n# comment\ntimeout 10s\nsearch\nnameserver \nnameserver 1.1.1.1 udp"),
ns: &NameServer{
Protocol: "udp",
Addr: "1.1.1.1",
@ -123,7 +122,6 @@ var resolverReloadTests = []struct {
ns: &NameServer{
Addr: "1.1.1.1",
Protocol: "tcp",
Timeout: DefaultResolverTimeout,
},
stopped: true,
},
@ -133,7 +131,6 @@ var resolverReloadTests = []struct {
Addr: "1.1.1.1:853",
Protocol: "tls",
Hostname: "cloudflare-dns.com",
Timeout: DefaultResolverTimeout,
},
stopped: true,
},
@ -142,7 +139,6 @@ var resolverReloadTests = []struct {
ns: &NameServer{
Addr: "1.1.1.1:853",
Protocol: "tls",
Timeout: DefaultResolverTimeout,
},
stopped: true,
},
@ -151,11 +147,10 @@ var resolverReloadTests = []struct {
stopped: true,
},
{
r: bytes.NewBufferString("https://1.0.0.1/dns-query https"),
r: bytes.NewBufferString("https://1.0.0.1/dns-query"),
ns: &NameServer{
Addr: "https://1.0.0.1/dns-query",
Protocol: "https",
Timeout: DefaultResolverTimeout,
},
stopped: true,
},
@ -164,15 +159,11 @@ var resolverReloadTests = []struct {
func TestResolverReload(t *testing.T) {
for i, tc := range resolverReloadTests {
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
r := newResolver(0, 0)
r := newResolver(0)
if err := r.Reload(tc.r); err != nil {
t.Error(err)
}
t.Log(r.String())
if r.Timeout != tc.timeout {
t.Errorf("timeout value should be %v, got %v",
tc.timeout, r.Timeout)
}
if r.TTL != tc.ttl {
t.Errorf("ttl value should be %v, got %v",
tc.ttl, r.TTL)
@ -198,6 +189,9 @@ func TestResolverReload(t *testing.T) {
if tc.stopped {
r.Stop()
if r.Period() >= 0 {
t.Errorf("period of the stopped reloader should be minus value")
}
}
if r.Stopped() != tc.stopped {
t.Errorf("stopped value should be %v, got %v",

387
socks.go
View File

@ -340,6 +340,164 @@ func (c *socks5BindConnector) Connect(conn net.Conn, addr string, options ...Con
return &socks5BindConn{Conn: conn, laddr: baddr}, nil
}
type socks5MuxBindConnector struct{}
// Socks5MuxBindConnector creates a Connector for SOCKS5 multiplex bind client.
func Socks5MuxBindConnector() Connector {
return &socks5MuxBindConnector{}
}
// NOTE: the conn must be *muxBindClientConn.
func (c *socks5MuxBindConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {
accepter, ok := conn.(Accepter)
if !ok {
return nil, errors.New("wrong connection type")
}
return accepter.Accept()
}
type socks5MuxBindTransporter struct {
bindAddr string
sessions map[string]*muxSession // server addr to session mapping
sessionMutex sync.Mutex
}
// SOCKS5MuxBindTransporter creates a Transporter for SOCKS5 multiplex bind client.
func SOCKS5MuxBindTransporter(bindAddr string) Transporter {
return &socks5MuxBindTransporter{
bindAddr: bindAddr,
sessions: make(map[string]*muxSession),
}
}
func (tr *socks5MuxBindTransporter) Dial(addr string, options ...DialOption) (conn net.Conn, err error) {
opts := &DialOptions{}
for _, option := range options {
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if session != nil && session.IsClosed() {
delete(tr.sessions, addr)
ok = false
}
if !ok {
if opts.Chain == nil {
conn, err = net.DialTimeout("tcp", addr, timeout)
} else {
conn, err = opts.Chain.Dial(addr)
}
if err != nil {
return
}
session = &muxSession{conn: conn}
tr.sessions[addr] = session
}
return session.conn, nil
}
func (tr *socks5MuxBindTransporter) Handshake(conn net.Conn, options ...HandshakeOption) (net.Conn, error) {
opts := &HandshakeOptions{}
for _, option := range options {
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
conn.SetDeadline(time.Now().Add(timeout))
defer conn.SetDeadline(time.Time{})
session, ok := tr.sessions[opts.Addr]
if !ok || session.session == nil {
s, err := tr.initSession(conn, tr.bindAddr, opts)
if err != nil {
conn.Close()
delete(tr.sessions, opts.Addr)
return nil, err
}
session = s
tr.sessions[opts.Addr] = session
}
return &muxBindClientConn{session: session}, nil
}
func (tr *socks5MuxBindTransporter) initSession(conn net.Conn, addr string, opts *HandshakeOptions) (*muxSession, error) {
if opts == nil {
opts = &HandshakeOptions{}
}
cc, err := socks5Handshake(conn, opts.User)
if err != nil {
return nil, err
}
conn = cc
bindAddr, err := net.ResolveTCPAddr("tcp", addr)
if err != nil {
return nil, err
}
req := gosocks5.NewRequest(CmdMuxBind, &gosocks5.Addr{
Type: gosocks5.AddrIPv4,
Host: bindAddr.IP.String(),
Port: uint16(bindAddr.Port),
})
if err = req.Write(conn); err != nil {
return nil, err
}
if Debug {
log.Log("[socks5] mbind\n", req)
}
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
if Debug {
log.Log("[socks5] mbind\n", reply)
}
if reply.Rep != gosocks5.Succeeded {
log.Logf("[socks5] mbind on %s failure", addr)
return nil, fmt.Errorf("SOCKS5 mbind on %s failure", addr)
}
baddr, err := net.ResolveTCPAddr("tcp", reply.Addr.String())
if err != nil {
return nil, err
}
log.Logf("[socks5] mbind on %s OK", baddr)
// Upgrade connection to multiplex stream.
session, err := smux.Server(conn, smux.DefaultConfig())
if err != nil {
return nil, err
}
return &muxSession{conn: conn, session: session}, nil
}
func (tr *socks5MuxBindTransporter) Multiplex() bool {
return true
}
type socks5UDPConnector struct {
User *url.Userinfo
}
@ -387,12 +545,10 @@ func (c *socks5UDPConnector) Connect(conn net.Conn, addr string, options ...Conn
log.Log("[socks5] udp\n", req)
}
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
conn.SetReadDeadline(time.Time{})
if Debug {
log.Log("[socks5] udp\n", reply)
@ -412,11 +568,142 @@ func (c *socks5UDPConnector) Connect(conn net.Conn, addr string, options ...Conn
if err != nil {
return nil, err
}
log.Logf("udp laddr:%s, raddr:%s", uc.LocalAddr(), uc.RemoteAddr())
// log.Logf("udp laddr:%s, raddr:%s", uc.LocalAddr(), uc.RemoteAddr())
return &socks5UDPConn{UDPConn: uc, taddr: taddr}, nil
}
type socks5UDPTunConnector struct {
User *url.Userinfo
}
// SOCKS5UDPTunConnector creates a connector for SOCKS5 UDP-over-TCP relay.
// It accepts an optional auth info for SOCKS5 Username/Password Authentication.
func SOCKS5UDPTunConnector(user *url.Userinfo) Connector {
return &socks5UDPTunConnector{User: user}
}
func (c *socks5UDPTunConnector) Connect(conn net.Conn, addr string, options ...ConnectOption) (net.Conn, error) {
opts := &ConnectOptions{}
for _, option := range options {
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = ConnectTimeout
}
conn.SetDeadline(time.Now().Add(timeout))
defer conn.SetDeadline(time.Time{})
cc, err := socks5Handshake(conn, c.User)
if err != nil {
return nil, err
}
conn = cc
taddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
req := gosocks5.NewRequest(CmdUDPTun, &gosocks5.Addr{
Type: gosocks5.AddrIPv4,
})
if err := req.Write(conn); err != nil {
return nil, err
}
if Debug {
log.Log("[socks5] udp\n", req)
}
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
if Debug {
log.Log("[socks5] udp\n", reply)
}
if reply.Rep != gosocks5.Succeeded {
log.Logf("[socks5] udp relay failure")
return nil, fmt.Errorf("SOCKS5 udp relay failure")
}
baddr, err := net.ResolveUDPAddr("udp", reply.Addr.String())
if err != nil {
return nil, err
}
log.Logf("[socks5] udp-tun associate on %s OK", baddr)
return &udpTunnelConn{Conn: conn, raddr: taddr.String()}, nil
}
func (c *socks5UDPTunConnector) tunnelClientUDP(pc net.PacketConn, cc net.Conn) (err error) {
errc := make(chan error, 2)
go func() {
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, addr, err := pc.ReadFrom(b)
if err != nil {
log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)
errc <- err
return
}
// pipe from peer to tunnel
dgram := gosocks5.NewUDPDatagram(
gosocks5.NewUDPHeader(uint16(n), 0, toSocksAddr(addr)), b[:n])
if err := dgram.Write(cc); err != nil {
log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), dgram.Header.Addr, err)
errc <- err
return
}
if Debug {
log.Logf("[udp-tun] %s <<< %s length: %d", cc.RemoteAddr(), dgram.Header.Addr, len(dgram.Data))
}
}
}()
go func() {
for {
dgram, err := gosocks5.ReadUDPDatagram(cc)
if err != nil {
log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)
errc <- err
return
}
// pipe from tunnel to peer
addr, err := net.ResolveUDPAddr("udp", dgram.Header.Addr.String())
if err != nil {
continue // drop silently
}
if _, err := pc.WriteTo(dgram.Data, addr); err != nil {
log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)
errc <- err
return
}
if Debug {
log.Logf("[udp-tun] %s >>> %s length: %d", cc.RemoteAddr(), addr, len(dgram.Data))
}
}
}()
select {
case err = <-errc:
}
return
}
type socks4Connector struct{}
// SOCKS4Connector creates a Connector for SOCKS4 proxy client.
@ -850,11 +1137,11 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
relay, err := net.ListenUDP("udp", nil)
if err != nil {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), relay.LocalAddr(), err)
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(conn)
if Debug {
log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), relay.LocalAddr(), reply)
log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply)
}
return
}
@ -864,39 +1151,39 @@ func (h *socks5Handler) handleUDPRelay(conn net.Conn, req *gosocks5.Request) {
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(conn); err != nil {
log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), relay.LocalAddr(), err)
log.Logf("[socks5-udp] %s <- %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
return
}
if Debug {
log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), reply.Addr, reply)
log.Logf("[socks5-udp] %s <- %s\n%s", conn.RemoteAddr(), conn.LocalAddr(), reply)
}
log.Logf("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), relay.LocalAddr(), socksAddr)
log.Logf("[socks5-udp] %s - %s BIND ON %s OK", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)
// serve as standard socks5 udp relay local <-> remote
if h.options.Chain.IsEmpty() {
peer, er := net.ListenUDP("udp", nil)
if er != nil {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, er)
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), conn.LocalAddr(), er)
return
}
defer peer.Close()
go h.transportUDP(relay, peer)
log.Logf("[socks5-udp] %s <-> %s", conn.RemoteAddr(), socksAddr)
log.Logf("[socks5-udp] %s <-> %s : associated on %s", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)
if err := h.discardClientData(conn); err != nil {
log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), socksAddr, err)
log.Logf("[socks5-udp] %s - %s : %s", conn.RemoteAddr(), conn.LocalAddr(), err)
}
log.Logf("[socks5-udp] %s >-< %s", conn.RemoteAddr(), socksAddr)
log.Logf("[socks5-udp] %s >-< %s : associated on %s", conn.RemoteAddr(), conn.LocalAddr(), socksAddr)
return
}
// forward udp local <-> tunnel
cc, err := h.options.Chain.Conn()
// connection error
if err != nil {
log.Logf("[socks5-udp] %s -> %s : %s", conn.RemoteAddr(), socksAddr, err)
return
}
// forward udp local <-> tunnel
defer cc.Close()
cc, err = socks5Handshake(cc, h.options.Chain.LastNode().User)
@ -956,17 +1243,17 @@ func (h *socks5Handler) discardClientData(conn net.Conn) (err error) {
return
}
func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
func (h *socks5Handler) transportUDP(relay, peer net.PacketConn) (err error) {
errc := make(chan error, 2)
var clientAddr *net.UDPAddr
var clientAddr net.Addr
go func() {
b := mPool.Get().([]byte)
defer mPool.Put(b)
for {
n, laddr, err := relay.ReadFromUDP(b)
n, laddr, err := relay.ReadFrom(b)
if err != nil {
errc <- err
return
@ -988,7 +1275,7 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
log.Log("[socks5-udp] [bypass] write to", raddr)
continue // bypass
}
if _, err := peer.WriteToUDP(dgram.Data, raddr); err != nil {
if _, err := peer.WriteTo(dgram.Data, raddr); err != nil {
errc <- err
return
}
@ -1003,7 +1290,7 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
defer mPool.Put(b)
for {
n, raddr, err := peer.ReadFromUDP(b)
n, raddr, err := peer.ReadFrom(b)
if err != nil {
errc <- err
return
@ -1018,7 +1305,7 @@ func (h *socks5Handler) transportUDP(relay, peer *net.UDPConn) (err error) {
buf := bytes.Buffer{}
dgram := gosocks5.NewUDPDatagram(gosocks5.NewUDPHeader(0, 0, toSocksAddr(raddr)), b[:n])
dgram.Write(&buf)
if _, err := relay.WriteToUDP(buf.Bytes(), clientAddr); err != nil {
if _, err := relay.WriteTo(buf.Bytes(), clientAddr); err != nil {
errc <- err
return
}
@ -1178,7 +1465,7 @@ func (h *socks5Handler) handleUDPTunnel(conn net.Conn, req *gosocks5.Request) {
log.Logf("[socks5-udp] %s >-< %s [tun]", conn.RemoteAddr(), cc.RemoteAddr())
}
func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error) {
func (h *socks5Handler) tunnelServerUDP(cc net.Conn, pc net.PacketConn) (err error) {
errc := make(chan error, 2)
go func() {
@ -1186,9 +1473,9 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
defer mPool.Put(b)
for {
n, addr, err := uc.ReadFromUDP(b)
n, addr, err := pc.ReadFrom(b)
if err != nil {
log.Logf("[udp-tun] %s <- %s : %s", cc.RemoteAddr(), addr, err)
// log.Logf("[udp-tun] %s : %s", cc.RemoteAddr(), err)
errc <- err
return
}
@ -1215,7 +1502,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
for {
dgram, err := gosocks5.ReadUDPDatagram(cc)
if err != nil {
log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)
// log.Logf("[udp-tun] %s -> 0 : %s", cc.RemoteAddr(), err)
errc <- err
return
}
@ -1229,7 +1516,7 @@ func (h *socks5Handler) tunnelServerUDP(cc net.Conn, uc *net.UDPConn) (err error
log.Log("[udp-tun] [bypass] write to", addr)
continue // bypass
}
if _, err := uc.WriteToUDP(dgram.Data, addr); err != nil {
if _, err := pc.WriteTo(dgram.Data, addr); err != nil {
log.Logf("[udp-tun] %s -> %s : %s", cc.RemoteAddr(), addr, err)
errc <- err
return
@ -1260,11 +1547,11 @@ func (h *socks5Handler) handleMuxBind(conn net.Conn, req *gosocks5.Request) {
cc, err := h.options.Chain.Conn()
if err != nil {
log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), req.Addr, err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(conn)
if Debug {
log.Logf("[socks5-mbind] %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)
log.Logf("[socks5] mbind %s <- %s\n%s", conn.RemoteAddr(), req.Addr, reply)
}
return
}
@ -1274,16 +1561,16 @@ func (h *socks5Handler) handleMuxBind(conn net.Conn, req *gosocks5.Request) {
// so we don't need to authenticate it, as it's as explicit as whitelisting.
defer cc.Close()
req.Write(cc)
log.Logf("[socks5-mbind] %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())
log.Logf("[socks5] mbind %s <-> %s", conn.RemoteAddr(), cc.RemoteAddr())
transport(conn, cc)
log.Logf("[socks5-mbind] %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())
log.Logf("[socks5] mbind %s >-< %s", conn.RemoteAddr(), cc.RemoteAddr())
}
func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
bindAddr, _ := net.ResolveTCPAddr("tcp", addr)
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
if err != nil {
log.Logf("[socks5-mbind] %s -> %s : %s", conn.RemoteAddr(), addr, err)
log.Logf("[socks5] mbind %s -> %s : %s", conn.RemoteAddr(), addr, err)
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
return
}
@ -1294,23 +1581,23 @@ func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
if err := reply.Write(conn); err != nil {
log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), addr, err)
log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), addr, err)
return
}
if Debug {
log.Logf("[socks5-mbind] %s <- %s\n%s", conn.RemoteAddr(), addr, reply)
log.Logf("[socks5] mbind %s <- %s\n%s", conn.RemoteAddr(), addr, reply)
}
log.Logf("[socks5-mbind] %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr)
log.Logf("[socks5] mbind %s - %s BIND ON %s OK", conn.RemoteAddr(), addr, socksAddr)
// Upgrade connection to multiplex stream.
s, err := smux.Client(conn, smux.DefaultConfig())
if err != nil {
log.Logf("[socks5-mbind] %s - %s : %s", conn.RemoteAddr(), socksAddr, err)
log.Logf("[socks5] mbind %s - %s : %s", conn.RemoteAddr(), socksAddr, err)
return
}
log.Logf("[socks5-mbind] %s <-> %s", conn.RemoteAddr(), socksAddr)
defer log.Logf("[socks5-mbind] %s >-< %s", conn.RemoteAddr(), socksAddr)
log.Logf("[socks5] mbind %s <-> %s", conn.RemoteAddr(), socksAddr)
defer log.Logf("[socks5] mbind %s >-< %s", conn.RemoteAddr(), socksAddr)
session := &muxSession{
conn: conn,
@ -1322,6 +1609,7 @@ func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
for {
conn, err := session.Accept()
if err != nil {
log.Logf("[socks5] mbind accept : %v", err)
ln.Close()
return
}
@ -1332,10 +1620,10 @@ func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
for {
cc, err := ln.Accept()
if err != nil {
// log.Logf("[socks5-mbind] %s <- %s : %v", conn.RemoteAddr(), socksAddr, err)
log.Logf("[socks5] mbind %s <- %s : %v", conn.RemoteAddr(), socksAddr, err)
return
}
log.Logf("[socks5-mbind] %s <- %s : ACCEPT peer %s",
log.Logf("[socks5] mbind %s <- %s : ACCEPT peer %s",
conn.RemoteAddr(), socksAddr, cc.RemoteAddr())
go func(c net.Conn) {
@ -1343,9 +1631,11 @@ func (h *socks5Handler) muxBindOn(conn net.Conn, addr string) {
sc, err := session.GetConn()
if err != nil {
log.Logf("[socks5-mbind] %s <- %s : %s", conn.RemoteAddr(), socksAddr, err)
log.Logf("[socks5] mbind %s <- %s : %s", conn.RemoteAddr(), socksAddr, err)
return
}
defer sc.Close()
transport(sc, c)
}(cc)
}
@ -1550,6 +1840,10 @@ func getSOCKS5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) {
if err != nil {
return nil, err
}
conn.SetDeadline(time.Now().Add(HandshakeTimeout))
defer conn.SetDeadline(time.Time{})
cc, err := socks5Handshake(conn, chain.LastNode().User)
if err != nil {
conn.Close()
@ -1557,7 +1851,6 @@ func getSOCKS5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) {
}
conn = cc
conn.SetWriteDeadline(time.Now().Add(WriteTimeout))
req := gosocks5.NewRequest(CmdUDPTun, toSocksAddr(addr))
if err := req.Write(conn); err != nil {
conn.Close()
@ -1566,15 +1859,13 @@ func getSOCKS5UDPTunnel(chain *Chain, addr net.Addr) (net.Conn, error) {
if Debug {
log.Log("[socks5]", req)
}
conn.SetWriteDeadline(time.Time{})
conn.SetReadDeadline(time.Now().Add(ReadTimeout))
reply, err := gosocks5.ReadReply(conn)
if err != nil {
conn.Close()
return nil, err
}
conn.SetReadDeadline(time.Time{})
if Debug {
log.Log("[socks5]", reply)
}
@ -1647,7 +1938,7 @@ func (c *udpTunnelConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
return len(b), nil
}
// socks5BindConn is a connection for SOCKS5 bind request.
// socks5BindConn is a connection for SOCKS5 bind client.
type socks5BindConn struct {
raddr net.Addr
laddr net.Addr
@ -1758,3 +2049,13 @@ func (c *socks5UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
}
return len(b), nil
}
// a dummy client conn for multiplex bind used by SOCKS5 multiplex bind client connector
type muxBindClientConn struct {
nopConn
session *muxSession
}
func (c *muxBindClientConn) Accept() (net.Conn, error) {
return c.session.Accept()
}

View File

@ -1,11 +1,14 @@
package gost
import (
"bytes"
"crypto/rand"
"fmt"
"net"
"net/http/httptest"
"net/url"
"testing"
"time"
)
var socks5ProxyTests = []struct {
@ -407,6 +410,128 @@ func TestSOCKS5Bind(t *testing.T) {
}
}
func socks5MuxBindRoundtrip(t *testing.T, targetURL string, data []byte) (err error) {
ln, err := TCPListener("")
if err != nil {
return
}
l, err := net.Listen("tcp", "")
if err != nil {
return err
}
bindAddr := l.Addr().String()
l.Close()
client := &Client{
Connector: Socks5MuxBindConnector(),
Transporter: SOCKS5MuxBindTransporter(bindAddr),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
return muxBindRoundtrip(client, server, bindAddr, targetURL, data)
}
func muxBindRoundtrip(client *Client, server *Server, bindAddr, targetURL string, data []byte) (err error) {
cn, err := client.Dial(server.Addr().String())
if err != nil {
return err
}
conn, err := client.Handshake(cn,
AddrHandshakeOption(server.Addr().String()),
UserHandshakeOption(url.UserPassword("admin", "123456")),
)
if err != nil {
cn.Close()
return err
}
defer conn.Close()
cc, err := net.Dial("tcp", bindAddr)
if err != nil {
return
}
defer cc.Close()
conn, err = client.Connect(conn, "")
if err != nil {
return
}
u, err := url.Parse(targetURL)
if err != nil {
return
}
hc, err := net.Dial("tcp", u.Host)
if err != nil {
return
}
defer hc.Close()
go transport(hc, conn)
return httpRoundtrip(cc, targetURL, data)
}
func TestSOCKS5MuxBind(t *testing.T) {
httpSrv := httptest.NewServer(httpTestHandler)
defer httpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
if err := socks5MuxBindRoundtrip(t, httpSrv.URL, sendData); err != nil {
t.Errorf("got error: %v", err)
}
}
func BenchmarkSOCKS5MuxBind(b *testing.B) {
httpSrv := httptest.NewServer(httpTestHandler)
defer httpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
ln, err := TCPListener("")
if err != nil {
b.Error(err)
}
l, err := net.Listen("tcp", "")
if err != nil {
b.Error(err)
}
bindAddr := l.Addr().String()
l.Close()
client := &Client{
Connector: Socks5MuxBindConnector(),
Transporter: SOCKS5MuxBindTransporter(bindAddr),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
for i := 0; i < b.N; i++ {
if err := muxBindRoundtrip(client, server, bindAddr, httpSrv.URL, sendData); err != nil {
b.Error(err)
}
}
}
func socks5UDPRoundtrip(t *testing.T, host string, data []byte) (err error) {
ln, err := TCPListener("")
if err != nil {
@ -440,3 +565,226 @@ func TestSOCKS5UDP(t *testing.T) {
t.Errorf("got error: %v", err)
}
}
// TODO: fix a probability of timeout.
func BenchmarkSOCKS5UDP(b *testing.B) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
ln, err := TCPListener("")
if err != nil {
b.Error(err)
}
client := &Client{
Connector: SOCKS5UDPConnector(url.UserPassword("admin", "123456")),
Transporter: TCPTransporter(),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
for i := 0; i < b.N; i++ {
if err := udpRoundtrip(client, server, udpSrv.Addr(), sendData); err != nil {
b.Error(err)
}
}
}
func BenchmarkSOCKS5UDPSingleConn(b *testing.B) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
ln, err := TCPListener("")
if err != nil {
b.Error(err)
}
client := &Client{
Connector: SOCKS5UDPConnector(url.UserPassword("admin", "123456")),
Transporter: TCPTransporter(),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
conn, err := proxyConn(client, server)
if err != nil {
b.Error(err)
}
defer conn.Close()
conn, err = client.Connect(conn, udpSrv.Addr())
if err != nil {
b.Error(err)
}
roundtrip := func(conn net.Conn, data []byte) error {
conn.SetDeadline(time.Now().Add(1 * time.Second))
defer conn.SetDeadline(time.Time{})
if _, err = conn.Write(data); err != nil {
return err
}
recv := make([]byte, len(data))
if _, err = conn.Read(recv); err != nil {
return err
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return nil
}
for i := 0; i < b.N; i++ {
if err := roundtrip(conn, sendData); err != nil {
b.Error(err)
}
}
}
func socks5UDPTunRoundtrip(t *testing.T, host string, data []byte) (err error) {
ln, err := TCPListener("")
if err != nil {
return
}
client := &Client{
Connector: SOCKS5UDPTunConnector(url.UserPassword("admin", "123456")),
Transporter: TCPTransporter(),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
return udpRoundtrip(client, server, host, data)
}
func TestSOCKS5UDPTun(t *testing.T) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
if err := socks5UDPTunRoundtrip(t, udpSrv.Addr(), sendData); err != nil {
t.Errorf("got error: %v", err)
}
}
func BenchmarkSOCKS5UDPTun(b *testing.B) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
ln, err := TCPListener("")
if err != nil {
b.Error(err)
}
client := &Client{
Connector: SOCKS5UDPTunConnector(url.UserPassword("admin", "123456")),
Transporter: TCPTransporter(),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
for i := 0; i < b.N; i++ {
if err := udpRoundtrip(client, server, udpSrv.Addr(), sendData); err != nil {
b.Error(err)
}
}
}
func BenchmarkSOCKS5UDPTunSingleConn(b *testing.B) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()
sendData := make([]byte, 128)
rand.Read(sendData)
ln, err := TCPListener("")
if err != nil {
b.Error(err)
}
client := &Client{
Connector: SOCKS5UDPTunConnector(url.UserPassword("admin", "123456")),
Transporter: TCPTransporter(),
}
server := &Server{
Handler: SOCKS5Handler(UsersHandlerOption(url.UserPassword("admin", "123456"))),
Listener: ln,
}
go server.Run()
defer server.Close()
conn, err := proxyConn(client, server)
if err != nil {
b.Error(err)
}
defer conn.Close()
conn, err = client.Connect(conn, udpSrv.Addr())
if err != nil {
b.Error(err)
}
roundtrip := func(conn net.Conn, data []byte) error {
conn.SetDeadline(time.Now().Add(1 * time.Second))
defer conn.SetDeadline(time.Time{})
if _, err = conn.Write(data); err != nil {
return err
}
recv := make([]byte, len(data))
if _, err = conn.Read(recv); err != nil {
return err
}
if !bytes.Equal(data, recv) {
return fmt.Errorf("data not equal")
}
return nil
}
for i := 0; i < b.N; i++ {
if err := roundtrip(conn, sendData); err != nil {
b.Error(err)
}
}
}

View File

@ -2,6 +2,7 @@ package gost
import (
"crypto/rand"
"fmt"
"net/http/httptest"
"net/url"
"testing"
@ -148,20 +149,23 @@ func TestSSProxy(t *testing.T) {
rand.Read(sendData)
for i, tc := range ssTests {
err := ssProxyRoundtrip(httpSrv.URL, sendData,
tc.clientCipher,
tc.serverCipher,
)
if err == nil {
if !tc.pass {
t.Errorf("#%d should failed", i)
tc := tc
t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
err := ssProxyRoundtrip(httpSrv.URL, sendData,
tc.clientCipher,
tc.serverCipher,
)
if err == nil {
if !tc.pass {
t.Errorf("#%d should failed", i)
}
} else {
// t.Logf("#%d %v", i, err)
if tc.pass {
t.Errorf("#%d got error: %v", i, err)
}
}
} else {
// t.Logf("#%d %v", i, err)
if tc.pass {
t.Errorf("#%d got error: %v", i, err)
}
}
})
}
}
@ -317,7 +321,7 @@ func shadowUDPRoundtrip(t *testing.T, host string, data []byte) error {
return udpRoundtrip(client, server, host, data)
}
func TestShadowUDP(t *testing.T) {
func _TestShadowUDP(t *testing.T) {
udpSrv := newUDPTestServer(udpTestHandler)
udpSrv.Start()
defer udpSrv.Close()

23
tls.go
View File

@ -58,21 +58,20 @@ func (tr *mtlsTransporter) Dial(addr string, options ...DialOption) (conn net.Co
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if session != nil && session.session != nil && session.session.IsClosed() {
session.Close()
if session != nil && session.IsClosed() {
delete(tr.sessions, addr)
ok = false
ok = false // session is dead
}
if !ok {
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
if opts.Chain == nil {
conn, err = net.DialTimeout("tcp", addr, timeout)
} else {
@ -159,10 +158,12 @@ func TLSListener(addr string, config *tls.Config) (Listener, error) {
if config == nil {
config = DefaultTLSConfig
}
ln, err := tls.Listen("tcp", addr, config)
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
ln = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config)
return &tlsListener{ln}, nil
}
@ -177,13 +178,13 @@ func MTLSListener(addr string, config *tls.Config) (Listener, error) {
if config == nil {
config = DefaultTLSConfig
}
ln, err := tls.Listen("tcp", addr, config)
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
l := &mtlsListener{
ln: ln,
ln: tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener)}, config),
connChan: make(chan net.Conn, 1024),
errChan: make(chan error, 1),
}

224
ws.go
View File

@ -28,92 +28,6 @@ type WSOptions struct {
UserAgent string
}
type websocketConn struct {
conn *websocket.Conn
rb []byte
}
func websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, options *WSOptions) (net.Conn, error) {
if options == nil {
options = &WSOptions{}
}
timeout := options.HandshakeTimeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
dialer := websocket.Dialer{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
TLSClientConfig: tlsConfig,
HandshakeTimeout: timeout,
EnableCompression: options.EnableCompression,
NetDial: func(net, addr string) (net.Conn, error) {
return conn, nil
},
}
header := http.Header{}
header.Set("User-Agent", DefaultUserAgent)
if options.UserAgent != "" {
header.Set("User-Agent", options.UserAgent)
}
c, resp, err := dialer.Dial(url, header)
if err != nil {
return nil, err
}
resp.Body.Close()
return &websocketConn{conn: c}, nil
}
func websocketServerConn(conn *websocket.Conn) net.Conn {
// conn.EnableWriteCompression(true)
return &websocketConn{
conn: conn,
}
}
func (c *websocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.conn.ReadMessage()
}
n = copy(b, c.rb)
c.rb = c.rb[n:]
return
}
func (c *websocketConn) Write(b []byte) (n int, err error) {
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
return
}
func (c *websocketConn) Close() error {
return c.conn.Close()
}
func (c *websocketConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *websocketConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *websocketConn) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}
func (c *websocketConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *websocketConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
type wsTransporter struct {
tcpTransporter
options *WSOptions
@ -160,21 +74,20 @@ func (tr *mwsTransporter) Dial(addr string, options ...DialOption) (conn net.Con
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if session != nil && session.session != nil && session.session.IsClosed() {
session.Close()
if session != nil && session.IsClosed() {
delete(tr.sessions, addr)
ok = false
}
if !ok {
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
if opts.Chain == nil {
conn, err = net.DialTimeout("tcp", addr, timeout)
} else {
@ -302,21 +215,20 @@ func (tr *mwssTransporter) Dial(addr string, options ...DialOption) (conn net.Co
option(opts)
}
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
tr.sessionMutex.Lock()
defer tr.sessionMutex.Unlock()
session, ok := tr.sessions[addr]
if session != nil && session.session != nil && session.session.IsClosed() {
session.Close()
if session != nil && session.IsClosed() {
delete(tr.sessions, addr)
ok = false
}
if !ok {
timeout := opts.Timeout
if timeout <= 0 {
timeout = DialTimeout
}
if opts.Chain == nil {
conn, err = net.DialTimeout("tcp", addr, timeout)
} else {
@ -428,7 +340,11 @@ func WSListener(addr string, options *WSOptions) (Listener, error) {
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{Addr: addr, Handler: mux}
l.srv = &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 30 * time.Second,
}
ln, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
@ -517,7 +433,11 @@ func MWSListener(addr string, options *WSOptions) (Listener, error) {
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{Addr: addr, Handler: mux}
l.srv = &http.Server{
Addr: addr,
Handler: mux,
ReadHeaderTimeout: 30 * time.Second,
}
ln, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
@ -634,9 +554,10 @@ func WSSListener(addr string, tlsConfig *tls.Config, options *WSOptions) (Listen
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{
Addr: addr,
TLSConfig: tlsConfig,
Handler: mux,
Addr: addr,
TLSConfig: tlsConfig,
Handler: mux,
ReadHeaderTimeout: 30 * time.Second,
}
ln, err := net.ListenTCP("tcp", tcpAddr)
@ -694,9 +615,10 @@ func MWSSListener(addr string, tlsConfig *tls.Config, options *WSOptions) (Liste
mux := http.NewServeMux()
mux.Handle("/ws", http.HandlerFunc(l.upgrade))
l.srv = &http.Server{
Addr: addr,
TLSConfig: tlsConfig,
Handler: mux,
Addr: addr,
TLSConfig: tlsConfig,
Handler: mux,
ReadHeaderTimeout: 30 * time.Second,
}
ln, err := net.ListenTCP("tcp", tcpAddr)
@ -737,3 +659,89 @@ func generateChallengeKey() (string, error) {
}
return base64.StdEncoding.EncodeToString(p), nil
}
type websocketConn struct {
conn *websocket.Conn
rb []byte
}
func websocketClientConn(url string, conn net.Conn, tlsConfig *tls.Config, options *WSOptions) (net.Conn, error) {
if options == nil {
options = &WSOptions{}
}
timeout := options.HandshakeTimeout
if timeout <= 0 {
timeout = HandshakeTimeout
}
dialer := websocket.Dialer{
ReadBufferSize: options.ReadBufferSize,
WriteBufferSize: options.WriteBufferSize,
TLSClientConfig: tlsConfig,
HandshakeTimeout: timeout,
EnableCompression: options.EnableCompression,
NetDial: func(net, addr string) (net.Conn, error) {
return conn, nil
},
}
header := http.Header{}
header.Set("User-Agent", DefaultUserAgent)
if options.UserAgent != "" {
header.Set("User-Agent", options.UserAgent)
}
c, resp, err := dialer.Dial(url, header)
if err != nil {
return nil, err
}
resp.Body.Close()
return &websocketConn{conn: c}, nil
}
func websocketServerConn(conn *websocket.Conn) net.Conn {
// conn.EnableWriteCompression(true)
return &websocketConn{
conn: conn,
}
}
func (c *websocketConn) Read(b []byte) (n int, err error) {
if len(c.rb) == 0 {
_, c.rb, err = c.conn.ReadMessage()
}
n = copy(b, c.rb)
c.rb = c.rb[n:]
return
}
func (c *websocketConn) Write(b []byte) (n int, err error) {
err = c.conn.WriteMessage(websocket.BinaryMessage, b)
n = len(b)
return
}
func (c *websocketConn) Close() error {
return c.conn.Close()
}
func (c *websocketConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *websocketConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *websocketConn) SetDeadline(t time.Time) error {
if err := c.SetReadDeadline(t); err != nil {
return err
}
return c.SetWriteDeadline(t)
}
func (c *websocketConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *websocketConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}