Source file
src/net/dial.go
Documentation: net
1
2
3
4
5 package net
6
7 import (
8 "context"
9 "internal/nettrace"
10 "internal/poll"
11 "time"
12 )
13
14
15
16
17
18
19 type Dialer struct {
20
21
22
23
24
25
26
27
28
29
30
31
32 Timeout time.Duration
33
34
35
36
37
38 Deadline time.Time
39
40
41
42
43
44 LocalAddr Addr
45
46
47
48
49
50
51 DualStack bool
52
53
54
55
56 FallbackDelay time.Duration
57
58
59
60
61
62 KeepAlive time.Duration
63
64
65 Resolver *Resolver
66
67
68
69
70
71
72 Cancel <-chan struct{}
73 }
74
75 func minNonzeroTime(a, b time.Time) time.Time {
76 if a.IsZero() {
77 return b
78 }
79 if b.IsZero() || a.Before(b) {
80 return a
81 }
82 return b
83 }
84
85
86
87
88
89
90 func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Time) {
91 if d.Timeout != 0 {
92 earliest = now.Add(d.Timeout)
93 }
94 if d, ok := ctx.Deadline(); ok {
95 earliest = minNonzeroTime(earliest, d)
96 }
97 return minNonzeroTime(earliest, d.Deadline)
98 }
99
100 func (d *Dialer) resolver() *Resolver {
101 if d.Resolver != nil {
102 return d.Resolver
103 }
104 return DefaultResolver
105 }
106
107
108
109 func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, error) {
110 if deadline.IsZero() {
111 return deadline, nil
112 }
113 timeRemaining := deadline.Sub(now)
114 if timeRemaining <= 0 {
115 return time.Time{}, poll.ErrTimeout
116 }
117
118 timeout := timeRemaining / time.Duration(addrsRemaining)
119
120 const saneMinimum = 2 * time.Second
121 if timeout < saneMinimum {
122 if timeRemaining < saneMinimum {
123 timeout = timeRemaining
124 } else {
125 timeout = saneMinimum
126 }
127 }
128 return now.Add(timeout), nil
129 }
130
131 func (d *Dialer) fallbackDelay() time.Duration {
132 if d.FallbackDelay > 0 {
133 return d.FallbackDelay
134 } else {
135 return 300 * time.Millisecond
136 }
137 }
138
139 func parseNetwork(ctx context.Context, network string, needsProto bool) (afnet string, proto int, err error) {
140 i := last(network, ':')
141 if i < 0 {
142 switch network {
143 case "tcp", "tcp4", "tcp6":
144 case "udp", "udp4", "udp6":
145 case "ip", "ip4", "ip6":
146 if needsProto {
147 return "", 0, UnknownNetworkError(network)
148 }
149 case "unix", "unixgram", "unixpacket":
150 default:
151 return "", 0, UnknownNetworkError(network)
152 }
153 return network, 0, nil
154 }
155 afnet = network[:i]
156 switch afnet {
157 case "ip", "ip4", "ip6":
158 protostr := network[i+1:]
159 proto, i, ok := dtoi(protostr)
160 if !ok || i != len(protostr) {
161 proto, err = lookupProtocol(ctx, protostr)
162 if err != nil {
163 return "", 0, err
164 }
165 }
166 return afnet, proto, nil
167 }
168 return "", 0, UnknownNetworkError(network)
169 }
170
171
172
173
174 func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string, hint Addr) (addrList, error) {
175 afnet, _, err := parseNetwork(ctx, network, true)
176 if err != nil {
177 return nil, err
178 }
179 if op == "dial" && addr == "" {
180 return nil, errMissingAddress
181 }
182 switch afnet {
183 case "unix", "unixgram", "unixpacket":
184 addr, err := ResolveUnixAddr(afnet, addr)
185 if err != nil {
186 return nil, err
187 }
188 if op == "dial" && hint != nil && addr.Network() != hint.Network() {
189 return nil, &AddrError{Err: "mismatched local address type", Addr: hint.String()}
190 }
191 return addrList{addr}, nil
192 }
193 addrs, err := r.internetAddrList(ctx, afnet, addr)
194 if err != nil || op != "dial" || hint == nil {
195 return addrs, err
196 }
197 var (
198 tcp *TCPAddr
199 udp *UDPAddr
200 ip *IPAddr
201 wildcard bool
202 )
203 switch hint := hint.(type) {
204 case *TCPAddr:
205 tcp = hint
206 wildcard = tcp.isWildcard()
207 case *UDPAddr:
208 udp = hint
209 wildcard = udp.isWildcard()
210 case *IPAddr:
211 ip = hint
212 wildcard = ip.isWildcard()
213 }
214 naddrs := addrs[:0]
215 for _, addr := range addrs {
216 if addr.Network() != hint.Network() {
217 return nil, &AddrError{Err: "mismatched local address type", Addr: hint.String()}
218 }
219 switch addr := addr.(type) {
220 case *TCPAddr:
221 if !wildcard && !addr.isWildcard() && !addr.IP.matchAddrFamily(tcp.IP) {
222 continue
223 }
224 naddrs = append(naddrs, addr)
225 case *UDPAddr:
226 if !wildcard && !addr.isWildcard() && !addr.IP.matchAddrFamily(udp.IP) {
227 continue
228 }
229 naddrs = append(naddrs, addr)
230 case *IPAddr:
231 if !wildcard && !addr.isWildcard() && !addr.IP.matchAddrFamily(ip.IP) {
232 continue
233 }
234 naddrs = append(naddrs, addr)
235 }
236 }
237 if len(naddrs) == 0 {
238 return nil, &AddrError{Err: errNoSuitableAddress.Error(), Addr: hint.String()}
239 }
240 return naddrs, nil
241 }
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 func Dial(network, address string) (Conn, error) {
290 var d Dialer
291 return d.Dial(network, address)
292 }
293
294
295
296
297
298
299
300
301
302
303
304 func DialTimeout(network, address string, timeout time.Duration) (Conn, error) {
305 d := Dialer{Timeout: timeout}
306 return d.Dial(network, address)
307 }
308
309
310 type dialParam struct {
311 Dialer
312 network, address string
313 }
314
315
316
317
318
319 func (d *Dialer) Dial(network, address string) (Conn, error) {
320 return d.DialContext(context.Background(), network, address)
321 }
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) {
342 if ctx == nil {
343 panic("nil context")
344 }
345 deadline := d.deadline(ctx, time.Now())
346 if !deadline.IsZero() {
347 if d, ok := ctx.Deadline(); !ok || deadline.Before(d) {
348 subCtx, cancel := context.WithDeadline(ctx, deadline)
349 defer cancel()
350 ctx = subCtx
351 }
352 }
353 if oldCancel := d.Cancel; oldCancel != nil {
354 subCtx, cancel := context.WithCancel(ctx)
355 defer cancel()
356 go func() {
357 select {
358 case <-oldCancel:
359 cancel()
360 case <-subCtx.Done():
361 }
362 }()
363 ctx = subCtx
364 }
365
366
367 resolveCtx := ctx
368 if trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace); trace != nil {
369 shadow := *trace
370 shadow.ConnectStart = nil
371 shadow.ConnectDone = nil
372 resolveCtx = context.WithValue(resolveCtx, nettrace.TraceKey{}, &shadow)
373 }
374
375 addrs, err := d.resolver().resolveAddrList(resolveCtx, "dial", network, address, d.LocalAddr)
376 if err != nil {
377 return nil, &OpError{Op: "dial", Net: network, Source: nil, Addr: nil, Err: err}
378 }
379
380 dp := &dialParam{
381 Dialer: *d,
382 network: network,
383 address: address,
384 }
385
386 var primaries, fallbacks addrList
387 if d.DualStack && network == "tcp" {
388 primaries, fallbacks = addrs.partition(isIPv4)
389 } else {
390 primaries = addrs
391 }
392
393 var c Conn
394 if len(fallbacks) > 0 {
395 c, err = dialParallel(ctx, dp, primaries, fallbacks)
396 } else {
397 c, err = dialSerial(ctx, dp, primaries)
398 }
399 if err != nil {
400 return nil, err
401 }
402
403 if tc, ok := c.(*TCPConn); ok && d.KeepAlive > 0 {
404 setKeepAlive(tc.fd, true)
405 setKeepAlivePeriod(tc.fd, d.KeepAlive)
406 testHookSetKeepAlive()
407 }
408 return c, nil
409 }
410
411
412
413
414
415 func dialParallel(ctx context.Context, dp *dialParam, primaries, fallbacks addrList) (Conn, error) {
416 if len(fallbacks) == 0 {
417 return dialSerial(ctx, dp, primaries)
418 }
419
420 returned := make(chan struct{})
421 defer close(returned)
422
423 type dialResult struct {
424 Conn
425 error
426 primary bool
427 done bool
428 }
429 results := make(chan dialResult)
430
431 startRacer := func(ctx context.Context, primary bool) {
432 ras := primaries
433 if !primary {
434 ras = fallbacks
435 }
436 c, err := dialSerial(ctx, dp, ras)
437 select {
438 case results <- dialResult{Conn: c, error: err, primary: primary, done: true}:
439 case <-returned:
440 if c != nil {
441 c.Close()
442 }
443 }
444 }
445
446 var primary, fallback dialResult
447
448
449 primaryCtx, primaryCancel := context.WithCancel(ctx)
450 defer primaryCancel()
451 go startRacer(primaryCtx, true)
452
453
454 fallbackTimer := time.NewTimer(dp.fallbackDelay())
455 defer fallbackTimer.Stop()
456
457 for {
458 select {
459 case <-fallbackTimer.C:
460 fallbackCtx, fallbackCancel := context.WithCancel(ctx)
461 defer fallbackCancel()
462 go startRacer(fallbackCtx, false)
463
464 case res := <-results:
465 if res.error == nil {
466 return res.Conn, nil
467 }
468 if res.primary {
469 primary = res
470 } else {
471 fallback = res
472 }
473 if primary.done && fallback.done {
474 return nil, primary.error
475 }
476 if res.primary && fallbackTimer.Stop() {
477
478
479
480
481 fallbackTimer.Reset(0)
482 }
483 }
484 }
485 }
486
487
488
489 func dialSerial(ctx context.Context, dp *dialParam, ras addrList) (Conn, error) {
490 var firstErr error
491
492 for i, ra := range ras {
493 select {
494 case <-ctx.Done():
495 return nil, &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: mapErr(ctx.Err())}
496 default:
497 }
498
499 deadline, _ := ctx.Deadline()
500 partialDeadline, err := partialDeadline(time.Now(), deadline, len(ras)-i)
501 if err != nil {
502
503 if firstErr == nil {
504 firstErr = &OpError{Op: "dial", Net: dp.network, Source: dp.LocalAddr, Addr: ra, Err: err}
505 }
506 break
507 }
508 dialCtx := ctx
509 if partialDeadline.Before(deadline) {
510 var cancel context.CancelFunc
511 dialCtx, cancel = context.WithDeadline(ctx, partialDeadline)
512 defer cancel()
513 }
514
515 c, err := dialSingle(dialCtx, dp, ra)
516 if err == nil {
517 return c, nil
518 }
519 if firstErr == nil {
520 firstErr = err
521 }
522 }
523
524 if firstErr == nil {
525 firstErr = &OpError{Op: "dial", Net: dp.network, Source: nil, Addr: nil, Err: errMissingAddress}
526 }
527 return nil, firstErr
528 }
529
530
531
532 func dialSingle(ctx context.Context, dp *dialParam, ra Addr) (c Conn, err error) {
533 trace, _ := ctx.Value(nettrace.TraceKey{}).(*nettrace.Trace)
534 if trace != nil {
535 raStr := ra.String()
536 if trace.ConnectStart != nil {
537 trace.ConnectStart(dp.network, raStr)
538 }
539 if trace.ConnectDone != nil {
540 defer func() { trace.ConnectDone(dp.network, raStr, err) }()
541 }
542 }
543 la := dp.LocalAddr
544 switch ra := ra.(type) {
545 case *TCPAddr:
546 la, _ := la.(*TCPAddr)
547 c, err = dialTCP(ctx, dp.network, la, ra)
548 case *UDPAddr:
549 la, _ := la.(*UDPAddr)
550 c, err = dialUDP(ctx, dp.network, la, ra)
551 case *IPAddr:
552 la, _ := la.(*IPAddr)
553 c, err = dialIP(ctx, dp.network, la, ra)
554 case *UnixAddr:
555 la, _ := la.(*UnixAddr)
556 c, err = dialUnix(ctx, dp.network, la, ra)
557 default:
558 return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: dp.address}}
559 }
560 if err != nil {
561 return nil, &OpError{Op: "dial", Net: dp.network, Source: la, Addr: ra, Err: err}
562 }
563 return c, nil
564 }
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584 func Listen(network, address string) (Listener, error) {
585 addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
586 if err != nil {
587 return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
588 }
589 var l Listener
590 switch la := addrs.first(isIPv4).(type) {
591 case *TCPAddr:
592 l, err = ListenTCP(network, la)
593 case *UnixAddr:
594 l, err = ListenUnix(network, la)
595 default:
596 return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
597 }
598 if err != nil {
599 return nil, err
600 }
601 return l, nil
602 }
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626 func ListenPacket(network, address string) (PacketConn, error) {
627 addrs, err := DefaultResolver.resolveAddrList(context.Background(), "listen", network, address, nil)
628 if err != nil {
629 return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: nil, Err: err}
630 }
631 var l PacketConn
632 switch la := addrs.first(isIPv4).(type) {
633 case *UDPAddr:
634 l, err = ListenUDP(network, la)
635 case *IPAddr:
636 l, err = ListenIP(network, la)
637 case *UnixAddr:
638 l, err = ListenUnixgram(network, la)
639 default:
640 return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: address}}
641 }
642 if err != nil {
643 return nil, err
644 }
645 return l, nil
646 }
647
View as plain text