-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathclient.go
129 lines (109 loc) · 3.37 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package autonat
import (
"context"
"fmt"
"time"
"github.com/libp2p/go-libp2p/core/host"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/p2p/host/autonat/pb"
"github.com/libp2p/go-msgio/pbio"
)
// NewAutoNATClient creates a fresh instance of an AutoNATClient
// If addrFunc is nil, h.Addrs will be used
func NewAutoNATClient(h host.Host, addrFunc AddrFunc, mt MetricsTracer) Client {
if addrFunc == nil {
addrFunc = h.Addrs
}
return &client{h: h, addrFunc: addrFunc, mt: mt}
}
type client struct {
h host.Host
addrFunc AddrFunc
mt MetricsTracer
}
// DialBack asks peer p to dial us back on all addresses returned by the addrFunc.
// It blocks until we've received a response from the peer.
//
// Note: A returned error Message_E_DIAL_ERROR does not imply that the server
// actually performed a dial attempt. Servers that run a version < v0.20.0 also
// return Message_E_DIAL_ERROR if the dial was skipped due to the dialPolicy.
func (c *client) DialBack(ctx context.Context, p peer.ID) error {
s, err := c.h.NewStream(ctx, p, AutoNATProto)
if err != nil {
return err
}
if err := s.Scope().SetService(ServiceName); err != nil {
log.Debugf("error attaching stream to autonat service: %s", err)
s.Reset()
return err
}
if err := s.Scope().ReserveMemory(maxMsgSize, network.ReservationPriorityAlways); err != nil {
log.Debugf("error reserving memory for autonat stream: %s", err)
s.Reset()
return err
}
defer s.Scope().ReleaseMemory(maxMsgSize)
deadline := time.Now().Add(streamTimeout)
if ctxDeadline, ok := ctx.Deadline(); ok {
if ctxDeadline.Before(deadline) {
deadline = ctxDeadline
}
}
s.SetDeadline(deadline)
// Might as well just reset the stream. Once we get to this point, we
// don't care about being nice.
defer s.Close()
r := pbio.NewDelimitedReader(s, maxMsgSize)
w := pbio.NewDelimitedWriter(s)
req := newDialMessage(peer.AddrInfo{ID: c.h.ID(), Addrs: c.addrFunc()})
if err := w.WriteMsg(req); err != nil {
s.Reset()
return err
}
var res pb.Message
if err := r.ReadMsg(&res); err != nil {
s.Reset()
return err
}
if res.GetType() != pb.Message_DIAL_RESPONSE {
s.Reset()
return fmt.Errorf("unexpected response: %s", res.GetType().String())
}
status := res.GetDialResponse().GetStatus()
if c.mt != nil {
c.mt.ReceivedDialResponse(status)
}
switch status {
case pb.Message_OK:
return nil
default:
return Error{Status: status, Text: res.GetDialResponse().GetStatusText()}
}
}
// Error wraps errors signalled by AutoNAT services
type Error struct {
Status pb.Message_ResponseStatus
Text string
}
func (e Error) Error() string {
return fmt.Sprintf("AutoNAT error: %s (%s)", e.Text, e.Status.String())
}
// IsDialError returns true if the error was due to a dial back failure
func (e Error) IsDialError() bool {
return e.Status == pb.Message_E_DIAL_ERROR
}
// IsDialRefused returns true if the error was due to a refusal to dial back
func (e Error) IsDialRefused() bool {
return e.Status == pb.Message_E_DIAL_REFUSED
}
// IsDialError returns true if the AutoNAT peer signalled an error dialing back
func IsDialError(e error) bool {
ae, ok := e.(Error)
return ok && ae.IsDialError()
}
// IsDialRefused returns true if the AutoNAT peer signalled refusal to dial back
func IsDialRefused(e error) bool {
ae, ok := e.(Error)
return ok && ae.IsDialRefused()
}