@cryptotaxi247 / kubo / commits / 8d08e1e3d

reuseport: respect dialer timeout

Juan Batiz-Benet committed Jan 22, 2015 at 05:23 UTC 8d08e1e3d6e5cffe7becd9c43e8cb92885ee019f
9 files changed +406 -69
Godeps/Godeps.json
+1 -1
@@ -160,7 +160,7 @@
160 },
161 {
162 "ImportPath": "github.com/jbenet/go-reuseport",
163 - "Rev": "a2e454f12a99b8898c41f9dcebae6c35dc3efa3a"
163 + "Rev": "6924153aded2d61c89a83c8f0738ed4e8df9191f"
164 },
165 {
166 "ImportPath": "github.com/jbenet/go-sockaddr/net",
Godeps/_workspace/src/github.com/jbenet/go-reuseport/addr.go
+12
@@ -18,3 +18,15 @@ func ResolveAddr(network, address string) (net.Addr, error) {
18 return net.ResolveUnixAddr(network, address)
19 }
20 }
21 +
22 +// conn is a struct that stores a raddr to get around:
23 +// * https://github.com/golang/go/issues/9661#issuecomment-71043147
24 +// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d
25 +type conn struct {
26 + net.Conn
27 + raddr net.Addr
28 +}
29 +
30 +func (c *conn) RemoteAddr() net.Addr {
31 + return c.raddr
32 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/impl_unix.go
+70 -54
@@ -9,6 +9,7 @@ import (
9 "syscall"
10 "time"
11
12 + poll "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll"
13 sockaddrnet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-sockaddr/net"
14 )
15
@@ -30,20 +31,18 @@ func socket(family, socktype, protocol int) (fd int, err error) {
31 return -1, err
32 }
33
33 - // set non-blocking until after connect, because we cant poll using runtime :(
34 + // cant set it until after connect
35 // if err = syscall.SetNonblock(fd, true); err != nil {
36 // syscall.Close(fd)
37 // return -1, err
38 // }
39
40 if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReuseAddr, 1); err != nil {
40 - // fmt.Println("reuse addr failed")
41 syscall.Close(fd)
42 return -1, err
43 }
44
45 if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, soReusePort, 1); err != nil {
46 - // fmt.Println("reuse port failed")
46 syscall.Close(fd)
47 return -1, err
48 }
@@ -51,7 +50,6 @@ func socket(family, socktype, protocol int) (fd int, err error) {
50 // set setLinger to 5 as reusing exact same (srcip:srcport, dstip:dstport)
51 // will otherwise fail on connect.
52 if err = setLinger(fd, 5); err != nil {
54 - // fmt.Println("linger failed")
53 syscall.Close(fd)
54 return -1, err
55 }
@@ -68,13 +66,13 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
66 lprotocol int
67 rprotocol int
68 file *os.File
69 + deadline time.Time
70 remoteSockaddr syscall.Sockaddr
71 localSockaddr syscall.Sockaddr
72 )
73
74 netAddr, err := ResolveAddr(netw, addr)
75 if err != nil {
77 - // fmt.Println("resolve addr failed")
76 return nil, err
77 }
78
@@ -84,6 +82,13 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
82 return nil, ErrUnsupportedProtocol
83 }
84
85 + switch {
86 + case !dialer.Deadline.IsZero():
87 + deadline = dialer.Deadline
88 + case dialer.Timeout != 0:
89 + deadline = time.Now().Add(dialer.Timeout)
90 + }
91 +
92 localSockaddr = sockaddrnet.NetAddrToSockaddr(dialer.LocalAddr)
93 remoteSockaddr = sockaddrnet.NetAddrToSockaddr(netAddr)
94
@@ -109,18 +114,29 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
114 // look at dialTCP in http://golang.org/src/net/tcpsock_posix.go .... !
115 // here we just try again 3 times.
116 for i := 0; i < 3; i++ {
117 + if !deadline.IsZero() && deadline.Before(time.Now()) {
118 + err = errTimeout
119 + break
120 + }
121 +
122 if fd, err = socket(rfamily, socktype, rprotocol); err != nil {
123 return nil, err
124 }
125
116 - if err = syscall.Bind(fd, localSockaddr); err != nil {
117 - // fmt.Println("bind failed")
126 + if localSockaddr != nil {
127 + if err = syscall.Bind(fd, localSockaddr); err != nil {
128 + syscall.Close(fd)
129 + return nil, err
130 + }
131 + }
132 +
133 + if err = syscall.SetNonblock(fd, true); err != nil {
134 syscall.Close(fd)
135 return nil, err
136 }
121 - if err = connect(fd, remoteSockaddr); err != nil {
137 +
138 + if err = connect(fd, remoteSockaddr, deadline); err != nil {
139 syscall.Close(fd)
123 - // fmt.Println("connect failed", localSockaddr, err)
140 continue // try again.
141 }
142
@@ -133,48 +149,40 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
149 if rprotocol == syscall.IPPROTO_TCP {
150 // by default golang/net sets TCP no delay to true.
151 if err = setNoDelay(fd, true); err != nil {
136 - // fmt.Println("set no delay failed")
152 syscall.Close(fd)
153 return nil, err
154 }
155 }
156
142 - if err = syscall.SetNonblock(fd, true); err != nil {
157 + // File Name get be nil
158 + file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
159 + if c, err = net.FileConn(file); err != nil {
160 syscall.Close(fd)
161 return nil, err
162 }
163
147 - switch socktype {
148 - case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET:
149 -
150 - // File Name get be nil
151 - file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
152 - if c, err = net.FileConn(file); err != nil {
153 - // fmt.Println("fileconn failed")
154 - syscall.Close(fd)
155 - return nil, err
156 - }
157 -
158 - case syscall.SOCK_DGRAM:
159 -
160 - // File Name get be nil
161 - file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
162 - if c, err = net.FileConn(file); err != nil {
163 - // fmt.Println("fileconn failed")
164 - syscall.Close(fd)
165 - return nil, err
166 - }
167 - }
168 -
164 if err = file.Close(); err != nil {
170 - // fmt.Println("file close failed")
165 syscall.Close(fd)
166 return nil, err
167 }
168
169 + // c = wrapConnWithRemoteAddr(c, netAddr)
170 return c, err
171 }
172
173 +// there's a rare case where dial returns successfully but for some reason the
174 +// RemoteAddr is not yet set. So, since we know what raddr should be, we just
175 +// wrap it. This is not ideal in that sometimes getpeername() may return a
176 +// different addr. But until this is fixed, best way to do it.
177 +// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d
178 +// * https://github.com/golang/go/issues/9661#issuecomment-71043147
179 +func wrapConnWithRemoteAddr(c net.Conn, raddr net.Addr) net.Conn {
180 + if c.RemoteAddr() == nil {
181 + return &conn{Conn: c, raddr: raddr}
182 + }
183 + return c // it's fine, no need to wrap.
184 +}
185 +
186 func listen(netw, addr string) (fd int, err error) {
187 var (
188 family int
@@ -185,7 +193,6 @@ func listen(netw, addr string) (fd int, err error) {
193
194 netAddr, err := ResolveAddr(netw, addr)
195 if err != nil {
188 - // fmt.Println("resolve addr failed")
196 return -1, err
197 }
198
@@ -205,7 +212,6 @@ func listen(netw, addr string) (fd int, err error) {
212 }
213
214 if err = syscall.Bind(fd, sockaddr); err != nil {
208 - // fmt.Println("bind failed")
215 syscall.Close(fd)
216 return -1, err
217 }
@@ -213,7 +219,6 @@ func listen(netw, addr string) (fd int, err error) {
219 if protocol == syscall.IPPROTO_TCP {
220 // by default golang/net sets TCP no delay to true.
221 if err = setNoDelay(fd, true); err != nil {
216 - // fmt.Println("set no delay failed")
222 syscall.Close(fd)
223 return -1, err
224 }
@@ -239,20 +244,17 @@ func listenStream(netw, addr string) (l net.Listener, err error) {
244
245 // Set backlog size to the maximum
246 if err = syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
242 - // fmt.Println("listen failed")
247 syscall.Close(fd)
248 return nil, err
249 }
250
251 file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
252 if l, err = net.FileListener(file); err != nil {
249 - // fmt.Println("filelistener failed")
253 syscall.Close(fd)
254 return nil, err
255 }
256
257 if err = file.Close(); err != nil {
255 - // fmt.Println("file close failed")
258 syscall.Close(fd)
259 return nil, err
260 }
@@ -272,13 +274,11 @@ func listenPacket(netw, addr string) (p net.PacketConn, err error) {
274
275 file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
276 if p, err = net.FilePacketConn(file); err != nil {
275 - // fmt.Println("filelistener failed")
277 syscall.Close(fd)
278 return nil, err
279 }
280
281 if err = file.Close(); err != nil {
281 - // fmt.Println("file close failed")
282 syscall.Close(fd)
283 return nil, err
284 }
@@ -298,13 +298,11 @@ func listenUDP(netw, addr string) (c net.Conn, err error) {
298
299 file = os.NewFile(uintptr(fd), filePrefix+strconv.Itoa(os.Getpid()))
300 if c, err = net.FileConn(file); err != nil {
301 - // fmt.Println("filelistener failed")
301 syscall.Close(fd)
302 return nil, err
303 }
304
305 if err = file.Close(); err != nil {
307 - // fmt.Println("file close failed")
306 syscall.Close(fd)
307 return nil, err
308 }
@@ -313,26 +311,36 @@ func listenUDP(netw, addr string) (c net.Conn, err error) {
311 }
312
313 // this is close to the connect() function inside stdlib/net
316 -func connect(fd int, ra syscall.Sockaddr) error {
314 +func connect(fd int, ra syscall.Sockaddr, deadline time.Time) error {
315 switch err := syscall.Connect(fd, ra); err {
316 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
317 case nil, syscall.EISCONN:
318 + if !deadline.IsZero() && deadline.Before(time.Now()) {
319 + return errTimeout
320 + }
321 return nil
322 default:
323 return err
324 }
325
325 - var err error
326 - start := time.Now()
326 + poller, err := poll.New(fd)
327 + if err != nil {
328 + return err
329 + }
330 +
331 for {
332 + if err = poller.WaitWrite(deadline); err != nil {
333 + return err
334 + }
335 +
336 // if err := fd.pd.WaitWrite(); err != nil {
337 // return err
338 // }
339 // i'd use the above fd.pd.WaitWrite to poll io correctly, just like net sockets...
332 - // but of course, it uses fucking runtime_* functions that _cannot_ be used by
333 - // non-go-stdlib source... seriously guys, what kind of bullshit is that!?
340 + // but of course, it uses the damn runtime_* functions that _cannot_ be used by
341 + // non-go-stdlib source... seriously guys, this is not nice.
342 // we're relegated to using syscall.Select (what nightmare that is) or using
335 - // a simple but totally bogus time-based wait. garbage.
343 + // a simple but totally bogus time-based wait. such garbage.
344 var nerr int
345 nerr, err = syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_ERROR)
346 if err != nil {
@@ -340,14 +348,22 @@ func connect(fd int, ra syscall.Sockaddr) error {
348 }
349 switch err = syscall.Errno(nerr); err {
350 case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
343 - if time.Now().Sub(start) > time.Second {
344 - return err
345 - }
346 - <-time.After(20 * time.Microsecond)
351 + continue
352 case syscall.Errno(0), syscall.EISCONN:
353 + if !deadline.IsZero() && deadline.Before(time.Now()) {
354 + return errTimeout
355 + }
356 return nil
357 default:
358 return err
359 }
360 }
361 }
362 +
363 +var errTimeout = &timeoutError{}
364 +
365 +type timeoutError struct{}
366 +
367 +func (e *timeoutError) Error() string { return "i/o timeout" }
368 +func (e *timeoutError) Timeout() bool { return true }
369 +func (e *timeoutError) Temporary() bool { return true }
Godeps/_workspace/src/github.com/jbenet/go-reuseport/interface.go
+12 -13
@@ -91,21 +91,20 @@ type Dialer struct {
91 // Returns a net.Conn created from a file discriptor for a socket
92 // with SO_REUSEPORT and SO_REUSEADDR option set.
93 func (d *Dialer) Dial(network, address string) (net.Conn, error) {
94 - c, err := dial(d.D, network, address)
95 - if err != nil {
96 - return nil, err
94 + if !available() {
95 + return nil, syscall.Errno(syscall.ENOPROTOOPT)
96 }
97
99 - // there's a rare case where dial returns successfully but for some reason the
100 - // RemoteAddr is not yet set. We wait here a while until it is, and if too long
101 - // passes, we fail. This is horrendous.
102 - for start := time.Now(); c.RemoteAddr() == nil; {
103 - if time.Now().Sub(start) > (time.Millisecond * 500) {
104 - c.Close()
105 - return nil, ErrReuseFailed
106 - }
98 + return dial(d.D, network, address)
99 +}
100
108 - <-time.After(20 * time.Microsecond)
101 +func (d *Dialer) deadline(def time.Duration) time.Time {
102 + switch {
103 + case !d.D.Deadline.IsZero():
104 + return d.D.Deadline
105 + case d.D.Timeout != 0:
106 + return time.Now().Add(d.D.Timeout)
107 + default:
108 + return time.Now().Add(def)
109 }
110 - return c, nil
110 }
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/error.go new
+9
@@ -0,0 +1,9 @@
1 +package poll
2 +
3 +var errTimeout = &timeoutError{}
4 +
5 +type timeoutError struct{}
6 +
7 +func (e *timeoutError) Error() string { return "i/o timeout" }
8 +func (e *timeoutError) Timeout() bool { return true }
9 +func (e *timeoutError) Temporary() bool { return true }
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_bsd.go new
+59
@@ -0,0 +1,59 @@
1 +// +build darwin freebsd dragonfly netbsd openbsd
2 +
3 +package poll
4 +
5 +import (
6 + "syscall"
7 + "time"
8 +)
9 +
10 +type Poller struct {
11 + kqfd int
12 + event syscall.Kevent_t
13 +}
14 +
15 +func New(fd int) (p *Poller, err error) {
16 + p = &Poller{}
17 +
18 + p.kqfd, err = syscall.Kqueue()
19 + if p.kqfd == -1 || err != nil {
20 + return nil, err
21 + }
22 +
23 + p.event = syscall.Kevent_t{
24 + Ident: uint64(fd),
25 + Filter: syscall.EVFILT_WRITE,
26 + Flags: syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_ONESHOT,
27 + Fflags: 0,
28 + Data: 0,
29 + Udata: nil,
30 + }
31 + return p, nil
32 +}
33 +
34 +func (p *Poller) Close() error {
35 + return syscall.Close(p.kqfd)
36 +}
37 +
38 +func (p *Poller) WaitWrite(deadline time.Time) error {
39 +
40 + // setup timeout
41 + var timeout *syscall.Timespec
42 + if !deadline.IsZero() {
43 + d := deadline.Sub(time.Now())
44 + t := syscall.NsecToTimespec(d.Nanoseconds())
45 + timeout = &t
46 + }
47 +
48 + // wait on kevent
49 + events := make([]syscall.Kevent_t, 1)
50 + n, err := syscall.Kevent(p.kqfd, []syscall.Kevent_t{p.event}, events, timeout)
51 + if err != nil {
52 + return err
53 + }
54 +
55 + if n < 1 {
56 + return errTimeout
57 + }
58 + return nil
59 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_linux.go new
+51
@@ -0,0 +1,51 @@
1 +// +build linux
2 +
3 +package poll
4 +
5 +import (
6 + "syscall"
7 + "time"
8 +)
9 +
10 +type Poller struct {
11 + epfd int
12 + event syscall.EpollEvent
13 + events [32]syscall.EpollEvent
14 +}
15 +
16 +func New(fd int) (p *Poller, err error) {
17 + p = &Poller{}
18 + if p.epfd, err = syscall.EpollCreate1(0); err != nil {
19 + return nil, err
20 + }
21 +
22 + p.event.Events = syscall.EPOLLOUT
23 + p.event.Fd = int32(fd)
24 + if err = syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_ADD, fd, &p.event); err != nil {
25 + p.Close()
26 + return nil, err
27 + }
28 +
29 + return p, nil
30 +}
31 +
32 +func (p *Poller) Close() error {
33 + return syscall.Close(p.epfd)
34 +}
35 +
36 +func (p *Poller) WaitWrite(deadline time.Time) error {
37 + msec := -1
38 + if !deadline.IsZero() {
39 + d := deadline.Sub(time.Now())
40 + msec = int(d.Nanoseconds() / 1000000) // ms!? omg...
41 + }
42 +
43 + n, err := syscall.EpollWait(p.epfd, p.events[:], msec)
44 + if err != nil {
45 + return err
46 + }
47 + if n < 1 {
48 + return errTimeout
49 + }
50 + return nil
51 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/poll/poll_unsupported.go new
+11
@@ -0,0 +1,11 @@
1 +// +build windows plan9
2 +
3 +package poll
4 +
5 +import (
6 + "errors"
7 +)
8 +
9 +func WaitWrite(fd int) error {
10 + return errors.New("platform not supported")
11 +}
Godeps/_workspace/src/github.com/jbenet/go-reuseport/reuse_test.go
+181 -1
@@ -2,11 +2,15 @@ package reuseport
2
3 import (
4 "bytes"
5 + "errors"
6 + "fmt"
7 "io"
8 "net"
9 "os"
10 "strings"
11 + "sync"
12 "testing"
13 + "time"
14 )
15
16 func echo(c net.Conn) {
@@ -226,7 +230,7 @@ func TestStreamListenDialSamePort(t *testing.T) {
230
231 c1, err := Dial(network, l1.Addr().String(), l2.Addr().String())
232 if err != nil {
229 - t.Fatal(err)
233 + t.Fatal(err, network, l1.Addr().String(), l2.Addr().String())
234 continue
235 }
236 defer c1.Close()
@@ -260,6 +264,120 @@ func TestStreamListenDialSamePort(t *testing.T) {
264 }
265 }
266
267 +func TestStreamListenDialSamePortStressManyMsgs(t *testing.T) {
268 + testCases := [][]string{
269 + []string{"tcp", "127.0.0.1:0"},
270 + []string{"tcp4", "127.0.0.1:0"},
271 + []string{"tcp6", "[::]:0"},
272 + }
273 +
274 + for _, tcase := range testCases {
275 + subestStreamListenDialSamePortStress(t, tcase[0], tcase[1], 2, 1000)
276 + }
277 +}
278 +
279 +func TestStreamListenDialSamePortStressManyNodes(t *testing.T) {
280 + testCases := [][]string{
281 + []string{"tcp", "127.0.0.1:0"},
282 + []string{"tcp4", "127.0.0.1:0"},
283 + []string{"tcp6", "[::]:0"},
284 + }
285 +
286 + for _, tcase := range testCases {
287 + subestStreamListenDialSamePortStress(t, tcase[0], tcase[1], 50, 1)
288 + }
289 +}
290 +
291 +func TestStreamListenDialSamePortStressManyMsgsManyNodes(t *testing.T) {
292 + testCases := [][]string{
293 + []string{"tcp", "127.0.0.1:0"},
294 + []string{"tcp4", "127.0.0.1:0"},
295 + []string{"tcp6", "[::]:0"},
296 + }
297 +
298 + for _, tcase := range testCases {
299 + subestStreamListenDialSamePortStress(t, tcase[0], tcase[1], 50, 100)
300 + }
301 +}
302 +
303 +func subestStreamListenDialSamePortStress(t *testing.T, network, addr string, nodes int, msgs int) {
304 + t.Logf("testing %s:%s %d nodes %d msgs", network, addr, nodes, msgs)
305 +
306 + var ls []net.Listener
307 + for i := 0; i < nodes; i++ {
308 + l, err := Listen(network, addr)
309 + if err != nil {
310 + t.Fatal(err)
311 + }
312 + defer l.Close()
313 + go acceptAndEcho(l)
314 + ls = append(ls, l)
315 + t.Logf("listening %s", l.Addr())
316 + }
317 +
318 + // connect them all
319 + var cs []net.Conn
320 + for i := 0; i < nodes; i++ {
321 + for j := 0; j < i; j++ {
322 + if i == j {
323 + continue // cannot do self.
324 + }
325 +
326 + ia := ls[i].Addr().String()
327 + ja := ls[j].Addr().String()
328 + c, err := Dial(network, ia, ja)
329 + if err != nil {
330 + t.Fatal(network, ia, ja, err)
331 + }
332 + defer c.Close()
333 + cs = append(cs, c)
334 + t.Logf("dialed %s --> %s", c.LocalAddr(), c.RemoteAddr())
335 + }
336 + }
337 +
338 + errs := make(chan error)
339 +
340 + send := func(c net.Conn, buf []byte) {
341 + if _, err := c.Write(buf); err != nil {
342 + errs <- err
343 + }
344 + }
345 +
346 + recv := func(c net.Conn, buf []byte) {
347 + buf2 := make([]byte, len(buf))
348 + if _, err := c.Read(buf2); err != nil {
349 + errs <- err
350 + }
351 + if !bytes.Equal(buf, buf2) {
352 + errs <- fmt.Errorf("recv failure: %s <--> %s -- %s %s", c.RemoteAddr(), c.LocalAddr(), buf, buf2)
353 + }
354 + }
355 +
356 + t.Logf("sending %d msgs per conn", msgs)
357 + go func() {
358 + var wg sync.WaitGroup
359 + for _, c := range cs {
360 + wg.Add(1)
361 + go func(c net.Conn) {
362 + defer wg.Done()
363 + for i := 0; i < msgs; i++ {
364 + msg := []byte(fmt.Sprintf("message %d", i))
365 + send(c, msg)
366 + recv(c, msg)
367 + }
368 + }(c)
369 + }
370 + wg.Wait()
371 + close(errs)
372 + }()
373 +
374 + for err := range errs {
375 + if err != nil {
376 + t.Error(err)
377 + }
378 + }
379 +}
380 +
381 func TestPacketListenDialSamePort(t *testing.T) {
382
383 any := [][]string{
@@ -343,6 +461,68 @@ func TestPacketListenDialSamePort(t *testing.T) {
461 }
462 }
463
464 +func TestDialRespectsTimeout(t *testing.T) {
465 +
466 + testCases := [][]string{
467 + []string{"tcp", "127.0.0.1:6780", "1.2.3.4:6781"},
468 + []string{"tcp4", "127.0.0.1:6782", "1.2.3.4:6783"},
469 + []string{"tcp6", "[::1]:6784", "[::2]:6785"},
470 + }
471 +
472 + timeout := 50 * time.Millisecond
473 +
474 + for _, tcase := range testCases {
475 + network := tcase[0]
476 + laddr := tcase[1]
477 + raddr := tcase[2]
478 +
479 + // l, err := Listen(network, raddr)
480 + // if err != nil {
481 + // t.Error("without a listener it wont work")
482 + // continue
483 + // }
484 + // defer l.Close()
485 +
486 + nladdr, err := ResolveAddr(network, laddr)
487 + if err != nil {
488 + t.Error("failed to resolve addr", network, laddr, err)
489 + continue
490 + }
491 + t.Log("testing", network, nladdr, raddr)
492 +
493 + d := Dialer{
494 + D: net.Dialer{
495 + LocalAddr: nil,
496 + Timeout: timeout,
497 + },
498 + }
499 +
500 + errs := make(chan error)
501 + go func() {
502 + c, err := d.Dial(network, raddr)
503 + if err == nil {
504 + errs <- errors.New("should've not connected")
505 + c.Close()
506 + return
507 + }
508 + close(errs) // success!
509 + }()
510 +
511 + ErrDrain:
512 + select {
513 + case <-time.After(5 * time.Second):
514 + t.Fatal("took too long")
515 + case err, more := <-errs:
516 + if !more {
517 + break
518 + }
519 + t.Error(err)
520 + goto ErrDrain
521 + }
522 +
523 + }
524 +}
525 +
526 func TestUnixNotSupported(t *testing.T) {
527
528 testCases := [][]string{