From 32e8ab9cf830a3adfb1195dafd6d062421beb9ff Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 5 May 2018 16:12:39 +0300 Subject: [PATCH 01/51] protobuf --- pb/autonat.pb.go | 208 +++++++++++++++++++++++++++++++++++++++++++++++ pb/autonat.proto | 34 ++++++++ 2 files changed, 242 insertions(+) create mode 100644 pb/autonat.pb.go create mode 100644 pb/autonat.proto diff --git a/pb/autonat.pb.go b/pb/autonat.pb.go new file mode 100644 index 0000000..79a0991 --- /dev/null +++ b/pb/autonat.pb.go @@ -0,0 +1,208 @@ +// Code generated by protoc-gen-gogo. +// source: autonat.proto +// DO NOT EDIT! + +/* +Package autonat_pb is a generated protocol buffer package. + +It is generated from these files: + autonat.proto + +It has these top-level messages: + Message +*/ +package autonat_pb + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type Message_MessageType int32 + +const ( + Message_DIAL Message_MessageType = 0 + Message_DIAL_RESPONSE Message_MessageType = 1 +) + +var Message_MessageType_name = map[int32]string{ + 0: "DIAL", + 1: "DIAL_RESPONSE", +} +var Message_MessageType_value = map[string]int32{ + "DIAL": 0, + "DIAL_RESPONSE": 1, +} + +func (x Message_MessageType) Enum() *Message_MessageType { + p := new(Message_MessageType) + *p = x + return p +} +func (x Message_MessageType) String() string { + return proto.EnumName(Message_MessageType_name, int32(x)) +} +func (x *Message_MessageType) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Message_MessageType_value, data, "Message_MessageType") + if err != nil { + return err + } + *x = Message_MessageType(value) + return nil +} + +type Message_ResponseStatus int32 + +const ( + Message_OK Message_ResponseStatus = 0 + Message_E_DIAL_ERROR Message_ResponseStatus = 100 + Message_E_DIAL_REFUSED Message_ResponseStatus = 101 + Message_E_INTERNAL_ERROR Message_ResponseStatus = 200 +) + +var Message_ResponseStatus_name = map[int32]string{ + 0: "OK", + 100: "E_DIAL_ERROR", + 101: "E_DIAL_REFUSED", + 200: "E_INTERNAL_ERROR", +} +var Message_ResponseStatus_value = map[string]int32{ + "OK": 0, + "E_DIAL_ERROR": 100, + "E_DIAL_REFUSED": 101, + "E_INTERNAL_ERROR": 200, +} + +func (x Message_ResponseStatus) Enum() *Message_ResponseStatus { + p := new(Message_ResponseStatus) + *p = x + return p +} +func (x Message_ResponseStatus) String() string { + return proto.EnumName(Message_ResponseStatus_name, int32(x)) +} +func (x *Message_ResponseStatus) UnmarshalJSON(data []byte) error { + value, err := proto.UnmarshalJSONEnum(Message_ResponseStatus_value, data, "Message_ResponseStatus") + if err != nil { + return err + } + *x = Message_ResponseStatus(value) + return nil +} + +type Message struct { + Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=autonat.pb.Message_MessageType" json:"type,omitempty"` + Dial *Message_Dial `protobuf:"bytes,2,opt,name=dial" json:"dial,omitempty"` + DialResponse *Message_DialResponse `protobuf:"bytes,3,opt,name=dialResponse" json:"dialResponse,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} + +func (m *Message) GetType() Message_MessageType { + if m != nil && m.Type != nil { + return *m.Type + } + return Message_DIAL +} + +func (m *Message) GetDial() *Message_Dial { + if m != nil { + return m.Dial + } + return nil +} + +func (m *Message) GetDialResponse() *Message_DialResponse { + if m != nil { + return m.DialResponse + } + return nil +} + +type Message_PeerInfo struct { + Id []byte `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Message_PeerInfo) Reset() { *m = Message_PeerInfo{} } +func (m *Message_PeerInfo) String() string { return proto.CompactTextString(m) } +func (*Message_PeerInfo) ProtoMessage() {} + +func (m *Message_PeerInfo) GetId() []byte { + if m != nil { + return m.Id + } + return nil +} + +func (m *Message_PeerInfo) GetAddrs() [][]byte { + if m != nil { + return m.Addrs + } + return nil +} + +type Message_Dial struct { + Peer *Message_PeerInfo `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Message_Dial) Reset() { *m = Message_Dial{} } +func (m *Message_Dial) String() string { return proto.CompactTextString(m) } +func (*Message_Dial) ProtoMessage() {} + +func (m *Message_Dial) GetPeer() *Message_PeerInfo { + if m != nil { + return m.Peer + } + return nil +} + +type Message_DialResponse struct { + Status *Message_ResponseStatus `protobuf:"varint,1,opt,name=status,enum=autonat.pb.Message_ResponseStatus" json:"status,omitempty"` + StatusText *string `protobuf:"bytes,2,opt,name=statusText" json:"statusText,omitempty"` + Addr []byte `protobuf:"bytes,3,opt,name=addr" json:"addr,omitempty"` + XXX_unrecognized []byte `json:"-"` +} + +func (m *Message_DialResponse) Reset() { *m = Message_DialResponse{} } +func (m *Message_DialResponse) String() string { return proto.CompactTextString(m) } +func (*Message_DialResponse) ProtoMessage() {} + +func (m *Message_DialResponse) GetStatus() Message_ResponseStatus { + if m != nil && m.Status != nil { + return *m.Status + } + return Message_OK +} + +func (m *Message_DialResponse) GetStatusText() string { + if m != nil && m.StatusText != nil { + return *m.StatusText + } + return "" +} + +func (m *Message_DialResponse) GetAddr() []byte { + if m != nil { + return m.Addr + } + return nil +} + +func init() { + proto.RegisterType((*Message)(nil), "autonat.pb.Message") + proto.RegisterType((*Message_PeerInfo)(nil), "autonat.pb.Message.PeerInfo") + proto.RegisterType((*Message_Dial)(nil), "autonat.pb.Message.Dial") + proto.RegisterType((*Message_DialResponse)(nil), "autonat.pb.Message.DialResponse") + proto.RegisterEnum("autonat.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) + proto.RegisterEnum("autonat.pb.Message_ResponseStatus", Message_ResponseStatus_name, Message_ResponseStatus_value) +} diff --git a/pb/autonat.proto b/pb/autonat.proto new file mode 100644 index 0000000..5399919 --- /dev/null +++ b/pb/autonat.proto @@ -0,0 +1,34 @@ +package autonat.pb; + +message Message { + enum MessageType { + DIAL = 0; + DIAL_RESPONSE = 1; + } + + enum ResponseStatus { + OK = 0; + E_DIAL_ERROR = 100; + E_DIAL_REFUSED = 101; + E_INTERNAL_ERROR = 200; + } + + message PeerInfo { + optional bytes id = 1; + repeated bytes addrs = 2; + } + + message Dial { + optional PeerInfo peer = 1; + } + + message DialResponse { + optional ResponseStatus status = 1; + optional string statusText = 2; + optional bytes addr = 3; + } + + optional MessageType type = 1; + optional Dial dial = 2; + optional DialResponse dialResponse = 3; +} From 70f7dd8fae98fcc1edf791d026c311b2db6afb26 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 5 May 2018 16:45:58 +0300 Subject: [PATCH 02/51] basic client --- client.go | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 52 ++++++++++++++++++++++++++++++++ proto.go | 23 +++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 client.go create mode 100644 package.json create mode 100644 proto.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..6709abc --- /dev/null +++ b/client.go @@ -0,0 +1,83 @@ +package autonat + +import ( + "context" + "fmt" + + pb "github.com/libp2p/go-libp2p-autonat/pb" + + ggio "github.com/gogo/protobuf/io" + host "github.com/libp2p/go-libp2p-host" + inet "github.com/libp2p/go-libp2p-net" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + ma "github.com/multiformats/go-multiaddr" +) + +type AutoNATClient interface { + Dial(ctx context.Context) (ma.Multiaddr, error) +} + +type AutoNATError struct { + Status pb.Message_ResponseStatus + Text string +} + +func NewAutoNATClient(h host.Host, p peer.ID) AutoNATClient { + return &client{h: h, p: p} +} + +type client struct { + h host.Host + p peer.ID +} + +func (c *client) Dial(ctx context.Context) (ma.Multiaddr, error) { + s, err := c.h.NewStream(ctx, c.p, AutoNATProto) + if err != nil { + return nil, err + } + defer s.Close() + + r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(s) + + req := newDialMessage(pstore.PeerInfo{ID: c.h.ID(), Addrs: c.h.Addrs()}) + err = w.WriteMsg(req) + if err != nil { + return nil, err + } + + var res pb.Message + err = r.ReadMsg(&res) + if err != nil { + return nil, err + } + + if res.GetType() != pb.Message_DIAL_RESPONSE { + return nil, fmt.Errorf("Unexpected response: %s", res.GetType().String()) + } + + status := res.GetDialResponse().GetStatus() + switch status { + case pb.Message_OK: + addr := res.GetDialResponse().GetAddr() + return ma.NewMultiaddrBytes(addr) + + default: + return nil, AutoNATError{Status: status, Text: res.GetDialResponse().GetStatusText()} + } +} + +func (e AutoNATError) Error() string { + return fmt.Sprintf("AutoNAT error: %s (%s)", e.Text, e.Status.String()) +} + +func (e AutoNATError) IsDialError() bool { + return e.Status == pb.Message_E_DIAL_ERROR +} + +func IsDialError(e error) bool { + ae, ok := e.(AutoNATError) + return ok && ae.IsDialError() +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..b3d986e --- /dev/null +++ b/package.json @@ -0,0 +1,52 @@ +{ + "author": "vyzo", + "bugs": {}, + "gx": { + "dvcsimport": "github.com/libp2p/go-libp2p-autonat" + }, + "gxDependencies": [ + { + "author": "whyrusleeping", + "hash": "QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D", + "name": "go-libp2p-host", + "version": "2.1.7" + }, + { + "author": "whyrusleeping", + "hash": "QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74", + "name": "go-libp2p-peer", + "version": "2.3.2" + }, + { + "author": "whyrusleeping", + "hash": "QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh", + "name": "go-libp2p-peerstore", + "version": "1.4.17" + }, + { + "author": "whyrusleeping", + "hash": "QmXoz9o2PT3tEzf7hicegwex5UgVP54n3k82K7jrWFyN86", + "name": "go-libp2p-net", + "version": "2.0.7" + }, + { + "author": "multiformats", + "hash": "QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb", + "name": "go-multiaddr", + "version": "1.2.6" + }, + { + "author": "whyrusleeping", + "hash": "QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV", + "name": "gogo-protobuf", + "version": "0.0.0" + } + ], + "gxVersion": "0.12.1", + "language": "go", + "license": "", + "name": "go-libp2p-autonat", + "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", + "version": "0.0.0" +} + diff --git a/proto.go b/proto.go new file mode 100644 index 0000000..142469c --- /dev/null +++ b/proto.go @@ -0,0 +1,23 @@ +package autonat + +import ( + pb "github.com/libp2p/go-libp2p-autonat/pb" + + pstore "github.com/libp2p/go-libp2p-peerstore" +) + +const AutoNATProto = "/autonat/1.0.0" + +func newDialMessage(pi pstore.PeerInfo) *pb.Message { + msg := new(pb.Message) + msg.Type = pb.Message_DIAL.Enum() + msg.Dial = new(pb.Message_Dial) + msg.Dial.Peer = new(pb.Message_PeerInfo) + msg.Dial.Peer.Id = []byte(pi.ID) + msg.Dial.Peer.Addrs = make([][]byte, len(pi.Addrs)) + for i, addr := range pi.Addrs { + msg.Dial.Peer.Addrs[i] = addr.Bytes() + } + + return msg +} From f3d9a24395110537ff17a6e7acf3e175b5ae9b03 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 09:45:30 +0300 Subject: [PATCH 03/51] add E_BAD_REQUEST to protobuf --- pb/autonat.pb.go | 9 ++++++--- pb/autonat.proto | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/pb/autonat.pb.go b/pb/autonat.pb.go index 79a0991..d1c981c 100644 --- a/pb/autonat.pb.go +++ b/pb/autonat.pb.go @@ -61,20 +61,23 @@ const ( Message_OK Message_ResponseStatus = 0 Message_E_DIAL_ERROR Message_ResponseStatus = 100 Message_E_DIAL_REFUSED Message_ResponseStatus = 101 - Message_E_INTERNAL_ERROR Message_ResponseStatus = 200 + Message_E_BAD_REQUEST Message_ResponseStatus = 200 + Message_E_INTERNAL_ERROR Message_ResponseStatus = 300 ) var Message_ResponseStatus_name = map[int32]string{ 0: "OK", 100: "E_DIAL_ERROR", 101: "E_DIAL_REFUSED", - 200: "E_INTERNAL_ERROR", + 200: "E_BAD_REQUEST", + 300: "E_INTERNAL_ERROR", } var Message_ResponseStatus_value = map[string]int32{ "OK": 0, "E_DIAL_ERROR": 100, "E_DIAL_REFUSED": 101, - "E_INTERNAL_ERROR": 200, + "E_BAD_REQUEST": 200, + "E_INTERNAL_ERROR": 300, } func (x Message_ResponseStatus) Enum() *Message_ResponseStatus { diff --git a/pb/autonat.proto b/pb/autonat.proto index 5399919..7107e1c 100644 --- a/pb/autonat.proto +++ b/pb/autonat.proto @@ -10,7 +10,8 @@ message Message { OK = 0; E_DIAL_ERROR = 100; E_DIAL_REFUSED = 101; - E_INTERNAL_ERROR = 200; + E_BAD_REQUEST = 200; + E_INTERNAL_ERROR = 300; } message PeerInfo { From cc058d6400c8ccfc06b2c9b9f9f07d8e29f090cb Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 09:49:02 +0300 Subject: [PATCH 04/51] service implementation --- package.json | 11 ++++ proto.go | 18 ++++++ svc.go | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+) create mode 100644 svc.go diff --git a/package.json b/package.json index b3d986e..14a148a 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,17 @@ "hash": "QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV", "name": "gogo-protobuf", "version": "0.0.0" + }, + { + "author": "whyrusleeping", + "hash": "QmWsV6kzPaYGBDVyuUfWBvyQygEc9Qrv9vzo8vZ7X4mdLN", + "name": "go-libp2p", + "version": "5.0.17" + }, + { + "hash": "QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7", + "name": "go-log", + "version": "1.4.1" } ], "gxVersion": "0.12.1", diff --git a/proto.go b/proto.go index 142469c..7d28d4b 100644 --- a/proto.go +++ b/proto.go @@ -3,11 +3,15 @@ package autonat import ( pb "github.com/libp2p/go-libp2p-autonat/pb" + logging "github.com/ipfs/go-log" pstore "github.com/libp2p/go-libp2p-peerstore" + ma "github.com/multiformats/go-multiaddr" ) const AutoNATProto = "/autonat/1.0.0" +var log = logging.Logger("autonat") + func newDialMessage(pi pstore.PeerInfo) *pb.Message { msg := new(pb.Message) msg.Type = pb.Message_DIAL.Enum() @@ -21,3 +25,17 @@ func newDialMessage(pi pstore.PeerInfo) *pb.Message { return msg } + +func newDialResponseOK(addr ma.Multiaddr) *pb.Message_DialResponse { + dr := new(pb.Message_DialResponse) + dr.Status = pb.Message_OK.Enum() + dr.Addr = addr.Bytes() + return dr +} + +func newDialResponseError(status pb.Message_ResponseStatus, text string) *pb.Message_DialResponse { + dr := new(pb.Message_DialResponse) + dr.Status = status.Enum() + dr.StatusText = &text + return dr +} diff --git a/svc.go b/svc.go new file mode 100644 index 0000000..863f97c --- /dev/null +++ b/svc.go @@ -0,0 +1,175 @@ +package autonat + +import ( + "context" + "sync" + "time" + + pb "github.com/libp2p/go-libp2p-autonat/pb" + + ggio "github.com/gogo/protobuf/io" + libp2p "github.com/libp2p/go-libp2p" + host "github.com/libp2p/go-libp2p-host" + inet "github.com/libp2p/go-libp2p-net" + peer "github.com/libp2p/go-libp2p-peer" + pstore "github.com/libp2p/go-libp2p-peerstore" + ma "github.com/multiformats/go-multiaddr" +) + +const P_CIRCUIT = 290 + +type AutoNATService struct { + ctx context.Context + dialer host.Host + peers map[peer.ID]struct{} + mx sync.Mutex +} + +func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) { + dialer, err := libp2p.New(ctx) + if err != nil { + return nil, err + } + + as := &AutoNATService{ + ctx: ctx, + dialer: dialer, + peers: make(map[peer.ID]struct{}), + } + h.SetStreamHandler(AutoNATProto, as.handleStream) + + go as.resetPeers() + + return as, nil +} + +func (as *AutoNATService) handleStream(s inet.Stream) { + defer s.Close() + + pid := s.Conn().RemotePeer() + log.Debugf("New stream from %s", pid.Pretty()) + + r := ggio.NewDelimitedReader(s, inet.MessageSizeMax) + w := ggio.NewDelimitedWriter(s) + + var req pb.Message + var res pb.Message + + err := r.ReadMsg(&req) + if err != nil { + s.Reset() + return + } + + t := req.GetType() + if t != pb.Message_DIAL { + log.Debugf("Unexpected message from: %s", t.String()) + s.Reset() + return + } + + dr := as.handleDial(pid, req.GetDial().GetPeer()) + res.Type = pb.Message_DIAL_RESPONSE.Enum() + res.DialResponse = dr + + err = w.WriteMsg(&res) + if err != nil { + log.Debugf("Error writing response: %s", err.Error()) + s.Reset() + return + } +} + +func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse { + if mpi == nil { + return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info") + } + + mpid := mpi.GetId() + if mpid != nil { + mp, err := peer.IDFromBytes(mpid) + if err != nil { + return newDialResponseError(pb.Message_E_BAD_REQUEST, "bad peer id") + } + + if mp != p { + return newDialResponseError(pb.Message_E_BAD_REQUEST, "peer id mismatch") + } + } + + addrs := make([]ma.Multiaddr, 0) + for _, maddr := range mpi.GetAddrs() { + addr, err := ma.NewMultiaddrBytes(maddr) + if err != nil { + log.Debugf("Error parsing multiaddr: %s", err.Error()) + continue + } + + // skip relay addresses + _, err = addr.ValueForProtocol(P_CIRCUIT) + if err == nil { + continue + } + + addrs = append(addrs, addr) + } + + if len(addrs) == 0 { + return newDialResponseError(pb.Message_E_DIAL_ERROR, "no dialable addresses") + } + + return as.doDial(pstore.PeerInfo{ID: p, Addrs: addrs}) +} + +func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { + // rate limit check + as.mx.Lock() + _, ok := as.peers[pi.ID] + if ok { + as.mx.Unlock() + return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials") + } + as.peers[pi.ID] = struct{}{} + as.mx.Unlock() + + ctx, cancel := context.WithTimeout(as.ctx, 42*time.Second) + defer cancel() + + err := as.dialer.Connect(ctx, pi) + if err != nil { + log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error()) + // wait for the context to timeout to avoid leaking timing information + // this renders the service ineffective as a port scanner + select { + case <-ctx.Done(): + return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed") + } + } + + conns := as.dialer.Network().ConnsToPeer(pi.ID) + if len(conns) == 0 { + log.Errorf("supposedly connected to %s, but no connection to peer", pi.ID.Pretty()) + return newDialResponseError(pb.Message_E_INTERNAL_ERROR, "internal service error") + } + + ra := conns[0].RemoteMultiaddr() + conns[0].Close() + return newDialResponseOK(ra) +} + +func (as *AutoNATService) resetPeers() { + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + as.mx.Lock() + as.peers = make(map[peer.ID]struct{}) + as.mx.Unlock() + + case <-as.ctx.Done(): + return + } + } +} From ef097b5555f0d3381ac76b3c3b07969e92fb413a Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 11:22:32 +0300 Subject: [PATCH 05/51] NAT autodetection --- autonat.go | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ client.go | 9 ++++ notify.go | 35 +++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 autonat.go create mode 100644 notify.go diff --git a/autonat.go b/autonat.go new file mode 100644 index 0000000..77b9b35 --- /dev/null +++ b/autonat.go @@ -0,0 +1,143 @@ +package autonat + +import ( + "context" + "errors" + "math/rand" + "sync" + "time" + + host "github.com/libp2p/go-libp2p-host" + peer "github.com/libp2p/go-libp2p-peer" + ma "github.com/multiformats/go-multiaddr" +) + +// attach to any host and auto-discovery autonat servers +// periodically query them to deal with changing NAT situation + +type NATStatus int + +const ( + NATStatusUnknown = iota + NATStatusPublic + NATStatusPrivate +) + +type AutoNAT interface { + Status() NATStatus + PublicAddr() (ma.Multiaddr, error) +} + +type AutoNATState struct { + ctx context.Context + host host.Host + peers map[peer.ID]bool + status NATStatus + addr ma.Multiaddr + mx sync.Mutex +} + +func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { + as := &AutoNATState{ + ctx: ctx, + host: h, + peers: make(map[peer.ID]bool), + status: NATStatusUnknown, + } + + go as.background() + return as +} + +func (as *AutoNATState) Status() NATStatus { + return as.status +} + +func (as *AutoNATState) PublicAddr() (ma.Multiaddr, error) { + as.mx.Lock() + defer as.mx.Unlock() + + if as.status != NATStatusPublic { + return nil, errors.New("NAT Status is not public") + } + + return as.addr, nil +} + +func (as *AutoNATState) background() { + // wait a bit for the node to come online and establish some connections + // before starting autodetection + time.Sleep(10 * time.Second) + for { + as.autodetect() + select { + case <-time.After(15 * time.Minute): + case <-as.ctx.Done(): + return + } + } +} + +func (as *AutoNATState) autodetect() { + if len(as.peers) == 0 { + log.Debugf("skipping NAT auto detection; no autonat peers") + return + } + + as.mx.Lock() + peers := make([]peer.ID, 0, len(as.peers)) + for p, c := range as.peers { + if c { + peers = append(peers, p) + } + } + + if len(peers) == 0 { + // we don't have any open connections, try any autonat peer that we know about + for p := range as.peers { + peers = append(peers, p) + } + } + + as.mx.Unlock() + + shufflePeers(peers) + + for _, p := range peers { + cli := NewAutoNATClient(as.host, p) + ctx, cancel := context.WithTimeout(as.ctx, 60*time.Second) + a, err := cli.Dial(ctx) + cancel() + + switch { + case err == nil: + log.Debugf("NAT status is public; address through %s: %s", p.Pretty(), a.String()) + as.mx.Lock() + as.addr = a + as.status = NATStatusPublic + as.mx.Unlock() + return + + case IsDialError(err): + log.Debugf("NAT status is private; dial error through %s: %s", p.Pretty(), err.Error()) + as.mx.Lock() + as.status = NATStatusPrivate + as.mx.Unlock() + return + + default: + log.Debugf("Error dialing through %s: %s", p.Pretty(), err.Error()) + } + } + + as.mx.Lock() + as.status = NATStatusUnknown + as.mx.Unlock() +} + +func shufflePeers(peers []peer.ID) { + for i := range peers { + j := rand.Intn(i + 1) + peers[i], peers[j] = peers[j], peers[i] + } +} diff --git a/client.go b/client.go index 6709abc..50c5426 100644 --- a/client.go +++ b/client.go @@ -77,7 +77,16 @@ func (e AutoNATError) IsDialError() bool { return e.Status == pb.Message_E_DIAL_ERROR } +func (e AutoNATError) IsDialRefused() bool { + return e.Status == pb.Message_E_DIAL_REFUSED +} + func IsDialError(e error) bool { ae, ok := e.(AutoNATError) return ok && ae.IsDialError() } + +func IsDialRefused(e error) bool { + ae, ok := e.(AutoNATError) + return ok && ae.IsDialRefused() +} diff --git a/notify.go b/notify.go new file mode 100644 index 0000000..958f2e0 --- /dev/null +++ b/notify.go @@ -0,0 +1,35 @@ +package autonat + +import ( + inet "github.com/libp2p/go-libp2p-net" + peer "github.com/libp2p/go-libp2p-peer" + ma "github.com/multiformats/go-multiaddr" +) + +var _ inet.Notifiee = (*AutoNATState)(nil) + +func (as *AutoNATState) Listen(net inet.Network, a ma.Multiaddr) {} +func (as *AutoNATState) ListenClose(net inet.Network, a ma.Multiaddr) {} +func (as *AutoNATState) OpenedStream(net inet.Network, s inet.Stream) {} +func (as *AutoNATState) ClosedStream(net inet.Network, s inet.Stream) {} + +func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { + go func(p peer.ID) { + s, err := as.host.NewStream(as.ctx, p, AutoNATProto) + if err != nil { + return + } + s.Close() + + log.Infof("Discovered AutoNAT peer %s", p.Pretty()) + as.mx.Lock() + as.peers[p] = true + as.mx.Unlock() + }(c.RemotePeer()) +} + +func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) { + as.mx.Lock() + delete(as.peers, c.RemotePeer()) + as.mx.Unlock() +} From 6efad8f63281149e0c49d35508ee752be125464b Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 11:28:24 +0300 Subject: [PATCH 06/51] remove left-over design notes --- autonat.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/autonat.go b/autonat.go index 77b9b35..d809d0d 100644 --- a/autonat.go +++ b/autonat.go @@ -12,9 +12,6 @@ import ( ma "github.com/multiformats/go-multiaddr" ) -// attach to any host and auto-discovery autonat servers -// periodically query them to deal with changing NAT situation - type NATStatus int const ( From 2aa66e5634077b39f867d93c451d82e1abcfc121 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 12:00:17 +0300 Subject: [PATCH 07/51] don't delete autonat peers on disconnect, just mark them as disconnected --- notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify.go b/notify.go index 958f2e0..2c5f6c7 100644 --- a/notify.go +++ b/notify.go @@ -30,6 +30,6 @@ func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) { as.mx.Lock() - delete(as.peers, c.RemotePeer()) + as.peers[c.RemotePeer()] = false as.mx.Unlock() } From ea43bf5fd40aadaeb2de9c52f6814ca2e6be460d Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 12:02:54 +0300 Subject: [PATCH 08/51] we only track autonat peers fix bug introduced in last commit --- notify.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/notify.go b/notify.go index 2c5f6c7..6a1a138 100644 --- a/notify.go +++ b/notify.go @@ -30,6 +30,10 @@ func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) { as.mx.Lock() - as.peers[c.RemotePeer()] = false + p := c.RemotePeer() + _, ok := as.peers[p] + if ok { + as.peers[p] = false + } as.mx.Unlock() } From 00fb7e78af7ecbbc1a6ce40f7a6ed2eaee18e4ce Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 13:13:19 +0300 Subject: [PATCH 09/51] fix autonat peer tracking connection state can be determined through the network interface on demand --- autonat.go | 8 ++++---- notify.go | 12 ++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/autonat.go b/autonat.go index d809d0d..5dabd31 100644 --- a/autonat.go +++ b/autonat.go @@ -28,7 +28,7 @@ type AutoNAT interface { type AutoNATState struct { ctx context.Context host host.Host - peers map[peer.ID]bool + peers map[peer.ID]struct{} status NATStatus addr ma.Multiaddr mx sync.Mutex @@ -38,7 +38,7 @@ func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { as := &AutoNATState{ ctx: ctx, host: h, - peers: make(map[peer.ID]bool), + peers: make(map[peer.ID]struct{}), status: NATStatusUnknown, } @@ -83,8 +83,8 @@ func (as *AutoNATState) autodetect() { as.mx.Lock() peers := make([]peer.ID, 0, len(as.peers)) - for p, c := range as.peers { - if c { + for p := range as.peers { + if len(as.host.Network().ConnsToPeer(p)) > 0 { peers = append(peers, p) } } diff --git a/notify.go b/notify.go index 6a1a138..2c68e8e 100644 --- a/notify.go +++ b/notify.go @@ -23,17 +23,9 @@ func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { log.Infof("Discovered AutoNAT peer %s", p.Pretty()) as.mx.Lock() - as.peers[p] = true + as.peers[p] = struct{}{} as.mx.Unlock() }(c.RemotePeer()) } -func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) { - as.mx.Lock() - p := c.RemotePeer() - _, ok := as.peers[p] - if ok { - as.peers[p] = false - } - as.mx.Unlock() -} +func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) {} From 0377627c8053338ec44ce340b877328187151f4f Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 13:24:16 +0300 Subject: [PATCH 10/51] add TODO in service about skipping private network addresses --- svc.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/svc.go b/svc.go index 863f97c..e2aa252 100644 --- a/svc.go +++ b/svc.go @@ -111,6 +111,8 @@ func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Me continue } + // TODO we probably also want to skip all addresses that are not IANA public routable + addrs = append(addrs, addr) } From 7fad996c973e4b9e790f7d8d1337dd2d9844d138 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 15:39:24 +0300 Subject: [PATCH 11/51] add network notifee --- autonat.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/autonat.go b/autonat.go index 5dabd31..6f3ecd3 100644 --- a/autonat.go +++ b/autonat.go @@ -42,7 +42,9 @@ func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { status: NATStatusUnknown, } + h.Network().Notify(as) go as.background() + return as } From 9af8715d912b28607dec148432db56e0e13da266 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 17:47:53 +0300 Subject: [PATCH 12/51] don't try to dial private network addresses addresses service TODO --- addr.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ svc.go | 5 ++++- 2 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 addr.go diff --git a/addr.go b/addr.go new file mode 100644 index 0000000..0eecb4f --- /dev/null +++ b/addr.go @@ -0,0 +1,62 @@ +package autonat + +import ( + "net" + + ma "github.com/multiformats/go-multiaddr" +) + +var private4, private6 []*net.IPNet +var privateCIDR4 = []string{ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "100.64.0.0/10", + "169.254.0.0/16", +} +var privateCIDR6 = []string{ + "fc00::/7", + "fe80::/10", +} + +func init() { + private4 = parsePrivateCIDR(privateCIDR4) + private6 = parsePrivateCIDR(privateCIDR6) +} + +func parsePrivateCIDR(cidrs []string) []*net.IPNet { + ipnets := make([]*net.IPNet, len(cidrs)) + for i, cidr := range cidrs { + _, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + panic(err) + } + ipnets[i] = ipnet + } + return ipnets +} + +func isPublicAddr(a ma.Multiaddr) bool { + ip, err := a.ValueForProtocol(ma.P_IP4) + if err == nil { + return !inAddrRange(ip, private4) + } + + ip, err = a.ValueForProtocol(ma.P_IP6) + if err == nil { + return !inAddrRange(ip, private6) + } + + return false +} + +func inAddrRange(s string, ipnets []*net.IPNet) bool { + ip := net.ParseIP(s) + for _, ipnet := range ipnets { + if ipnet.Contains(ip) { + return true + } + } + + return false +} diff --git a/svc.go b/svc.go index e2aa252..73cff76 100644 --- a/svc.go +++ b/svc.go @@ -111,7 +111,10 @@ func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Me continue } - // TODO we probably also want to skip all addresses that are not IANA public routable + // skip private network (unroutable) addresses + if !isPublicAddr(addr) { + continue + } addrs = append(addrs, addr) } From bc41c7ac0818d70fa6e0c17f381cbcee38a35772 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sun, 6 May 2018 18:33:21 +0300 Subject: [PATCH 13/51] add localhost to private addr ranges --- addr.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addr.go b/addr.go index 0eecb4f..4e078e3 100644 --- a/addr.go +++ b/addr.go @@ -8,14 +8,22 @@ import ( var private4, private6 []*net.IPNet var privateCIDR4 = []string{ + // localhost + "127.0.0.0/8", + // private networks "10.0.0.0/8", + "100.64.0.0/10", "172.16.0.0/12", "192.168.0.0/16", - "100.64.0.0/10", + // link local "169.254.0.0/16", } var privateCIDR6 = []string{ + // localhost + "::1/128", + // ULA reserved "fc00::/7", + // link local "fe80::/10", } From dcbcfce085666cd4d72c8c8132d07d295d6f84c2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 7 May 2018 12:03:21 +0300 Subject: [PATCH 14/51] no need to select; it's a one shot sync --- svc.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/svc.go b/svc.go index 73cff76..3152098 100644 --- a/svc.go +++ b/svc.go @@ -145,10 +145,8 @@ func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { log.Debugf("error dialing %s: %s", pi.ID.Pretty(), err.Error()) // wait for the context to timeout to avoid leaking timing information // this renders the service ineffective as a port scanner - select { - case <-ctx.Done(): - return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed") - } + <-ctx.Done() + return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed") } conns := as.dialer.Network().ConnsToPeer(pi.ID) From 9efd0ec98fbc067bce6ec207489e7050b31c3ecf Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 7 May 2018 12:08:13 +0300 Subject: [PATCH 15/51] typed NATStatus constants --- autonat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index 6f3ecd3..4c0212c 100644 --- a/autonat.go +++ b/autonat.go @@ -15,7 +15,7 @@ import ( type NATStatus int const ( - NATStatusUnknown = iota + NATStatusUnknown NATStatus = iota NATStatusPublic NATStatusPrivate ) From aaaa90eb54441beec2d85de9d2e04e796d4230b7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Mon, 7 May 2018 12:25:35 +0300 Subject: [PATCH 16/51] bump initial autodiscovery delay to 15s --- autonat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index 4c0212c..79b0c89 100644 --- a/autonat.go +++ b/autonat.go @@ -66,7 +66,7 @@ func (as *AutoNATState) PublicAddr() (ma.Multiaddr, error) { func (as *AutoNATState) background() { // wait a bit for the node to come online and establish some connections // before starting autodetection - time.Sleep(10 * time.Second) + time.Sleep(15 * time.Second) for { as.autodetect() select { From 1562e1b88105746a309172c5974c74667ab6d220 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 8 May 2018 16:11:33 +0300 Subject: [PATCH 17/51] AutoNATState is AmbientAutoNAT --- autonat.go | 12 ++++++------ notify.go | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/autonat.go b/autonat.go index 79b0c89..cad8a8d 100644 --- a/autonat.go +++ b/autonat.go @@ -25,7 +25,7 @@ type AutoNAT interface { PublicAddr() (ma.Multiaddr, error) } -type AutoNATState struct { +type AmbientAutoNAT struct { ctx context.Context host host.Host peers map[peer.ID]struct{} @@ -35,7 +35,7 @@ type AutoNATState struct { } func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { - as := &AutoNATState{ + as := &AmbientAutoNAT{ ctx: ctx, host: h, peers: make(map[peer.ID]struct{}), @@ -48,11 +48,11 @@ func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { return as } -func (as *AutoNATState) Status() NATStatus { +func (as *AmbientAutoNAT) Status() NATStatus { return as.status } -func (as *AutoNATState) PublicAddr() (ma.Multiaddr, error) { +func (as *AmbientAutoNAT) PublicAddr() (ma.Multiaddr, error) { as.mx.Lock() defer as.mx.Unlock() @@ -63,7 +63,7 @@ func (as *AutoNATState) PublicAddr() (ma.Multiaddr, error) { return as.addr, nil } -func (as *AutoNATState) background() { +func (as *AmbientAutoNAT) background() { // wait a bit for the node to come online and establish some connections // before starting autodetection time.Sleep(15 * time.Second) @@ -77,7 +77,7 @@ func (as *AutoNATState) background() { } } -func (as *AutoNATState) autodetect() { +func (as *AmbientAutoNAT) autodetect() { if len(as.peers) == 0 { log.Debugf("skipping NAT auto detection; no autonat peers") return diff --git a/notify.go b/notify.go index 2c68e8e..4e2bc9d 100644 --- a/notify.go +++ b/notify.go @@ -6,14 +6,14 @@ import ( ma "github.com/multiformats/go-multiaddr" ) -var _ inet.Notifiee = (*AutoNATState)(nil) +var _ inet.Notifiee = (*AmbientAutoNAT)(nil) -func (as *AutoNATState) Listen(net inet.Network, a ma.Multiaddr) {} -func (as *AutoNATState) ListenClose(net inet.Network, a ma.Multiaddr) {} -func (as *AutoNATState) OpenedStream(net inet.Network, s inet.Stream) {} -func (as *AutoNATState) ClosedStream(net inet.Network, s inet.Stream) {} +func (as *AmbientAutoNAT) Listen(net inet.Network, a ma.Multiaddr) {} +func (as *AmbientAutoNAT) ListenClose(net inet.Network, a ma.Multiaddr) {} +func (as *AmbientAutoNAT) OpenedStream(net inet.Network, s inet.Stream) {} +func (as *AmbientAutoNAT) ClosedStream(net inet.Network, s inet.Stream) {} -func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { +func (as *AmbientAutoNAT) Connected(net inet.Network, c inet.Conn) { go func(p peer.ID) { s, err := as.host.NewStream(as.ctx, p, AutoNATProto) if err != nil { @@ -28,4 +28,4 @@ func (as *AutoNATState) Connected(net inet.Network, c inet.Conn) { }(c.RemotePeer()) } -func (as *AutoNATState) Disconnected(net inet.Network, c inet.Conn) {} +func (as *AmbientAutoNAT) Disconnected(net inet.Network, c inet.Conn) {} From 6d4bc417cf792d9430db3cb490e2973c301bf58b Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 8 May 2018 16:17:05 +0300 Subject: [PATCH 18/51] variables for background delays --- autonat.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/autonat.go b/autonat.go index cad8a8d..54b2b56 100644 --- a/autonat.go +++ b/autonat.go @@ -20,6 +20,11 @@ const ( NATStatusPrivate ) +var ( + AutoNATBootDelay = 15 * time.Second + AutoNATRefreshInterval = 15 * time.Minute +) + type AutoNAT interface { Status() NATStatus PublicAddr() (ma.Multiaddr, error) @@ -66,11 +71,11 @@ func (as *AmbientAutoNAT) PublicAddr() (ma.Multiaddr, error) { func (as *AmbientAutoNAT) background() { // wait a bit for the node to come online and establish some connections // before starting autodetection - time.Sleep(15 * time.Second) + time.Sleep(AutoNATBootDelay) for { as.autodetect() select { - case <-time.After(15 * time.Minute): + case <-time.After(AutoNATRefreshInterval): case <-as.ctx.Done(): return } From fa14117521021ef03db7503a709e26a0f34c49a6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 9 May 2018 11:37:49 +0300 Subject: [PATCH 19/51] named magic number incantations --- autonat.go | 4 +++- svc.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/autonat.go b/autonat.go index 54b2b56..3025532 100644 --- a/autonat.go +++ b/autonat.go @@ -23,6 +23,8 @@ const ( var ( AutoNATBootDelay = 15 * time.Second AutoNATRefreshInterval = 15 * time.Minute + + AutoNATRequestTimeout = 60 * time.Second ) type AutoNAT interface { @@ -109,7 +111,7 @@ func (as *AmbientAutoNAT) autodetect() { for _, p := range peers { cli := NewAutoNATClient(as.host, p) - ctx, cancel := context.WithTimeout(as.ctx, 60*time.Second) + ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) a, err := cli.Dial(ctx) cancel() diff --git a/svc.go b/svc.go index 3152098..26d08f1 100644 --- a/svc.go +++ b/svc.go @@ -18,6 +18,8 @@ import ( const P_CIRCUIT = 290 +var AutoNATServiceResetInterval = 1 * time.Minute + type AutoNATService struct { ctx context.Context dialer host.Host @@ -161,7 +163,7 @@ func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { } func (as *AutoNATService) resetPeers() { - ticker := time.NewTicker(1 * time.Minute) + ticker := time.NewTicker(AutoNATServiceResetInterval) defer ticker.Stop() for { From d16ca790a8e5363a7f43507eb8c5f0ad8513c3c5 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 9 May 2018 11:47:16 +0300 Subject: [PATCH 20/51] refactor getPeers for locked scope --- autonat.go | 50 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/autonat.go b/autonat.go index 3025532..765996b 100644 --- a/autonat.go +++ b/autonat.go @@ -85,30 +85,13 @@ func (as *AmbientAutoNAT) background() { } func (as *AmbientAutoNAT) autodetect() { - if len(as.peers) == 0 { - log.Debugf("skipping NAT auto detection; no autonat peers") - return - } - - as.mx.Lock() - peers := make([]peer.ID, 0, len(as.peers)) - for p := range as.peers { - if len(as.host.Network().ConnsToPeer(p)) > 0 { - peers = append(peers, p) - } - } + peers := as.getPeers() if len(peers) == 0 { - // we don't have any open connections, try any autonat peer that we know about - for p := range as.peers { - peers = append(peers, p) - } + log.Debugf("skipping NAT auto detection; no autonat peers") + return } - as.mx.Unlock() - - shufflePeers(peers) - for _, p := range peers { cli := NewAutoNATClient(as.host, p) ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) @@ -141,6 +124,33 @@ func (as *AmbientAutoNAT) autodetect() { as.mx.Unlock() } +func (as *AmbientAutoNAT) getPeers() []peer.ID { + as.mx.Lock() + defer as.mx.Unlock() + + if len(as.peers) == 0 { + return nil + } + + peers := make([]peer.ID, 0, len(as.peers)) + for p := range as.peers { + if len(as.host.Network().ConnsToPeer(p)) > 0 { + peers = append(peers, p) + } + } + + if len(peers) == 0 { + // we don't have any open connections, try any autonat peer that we know about + for p := range as.peers { + peers = append(peers, p) + } + } + + shufflePeers(peers) + + return peers +} + func shufflePeers(peers []peer.ID) { for i := range peers { j := rand.Intn(i + 1) From b1733eb0cce95ff5a7024cc2f0a8752cd81e0ade Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 9 May 2018 11:53:15 +0300 Subject: [PATCH 21/51] don't throw away read errors; log them. --- svc.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/svc.go b/svc.go index 26d08f1..068b272 100644 --- a/svc.go +++ b/svc.go @@ -59,13 +59,14 @@ func (as *AutoNATService) handleStream(s inet.Stream) { err := r.ReadMsg(&req) if err != nil { + log.Debugf("Error reading message from %s: %s", pid.Pretty(), err.Error()) s.Reset() return } t := req.GetType() if t != pb.Message_DIAL { - log.Debugf("Unexpected message from: %s", t.String()) + log.Debugf("Unexpected message from %s: %s (%d)", pid.Pretty(), t.String(), t) s.Reset() return } @@ -76,7 +77,7 @@ func (as *AutoNATService) handleStream(s inet.Stream) { err = w.WriteMsg(&res) if err != nil { - log.Debugf("Error writing response: %s", err.Error()) + log.Debugf("Error writing response to %s: %s", pid.Pretty(), err.Error()) s.Reset() return } From bb5cad4585562c338bf620f604c96f413d12cd26 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 9 May 2018 12:16:16 +0300 Subject: [PATCH 22/51] simplify autonat client itnerface --- autonat.go | 5 +++-- client.go | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/autonat.go b/autonat.go index 765996b..3a58e2b 100644 --- a/autonat.go +++ b/autonat.go @@ -92,10 +92,11 @@ func (as *AmbientAutoNAT) autodetect() { return } + cli := NewAutoNATClient(as.host) + for _, p := range peers { - cli := NewAutoNATClient(as.host, p) ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) - a, err := cli.Dial(ctx) + a, err := cli.Dial(ctx, p) cancel() switch { diff --git a/client.go b/client.go index 50c5426..f996ff3 100644 --- a/client.go +++ b/client.go @@ -15,7 +15,7 @@ import ( ) type AutoNATClient interface { - Dial(ctx context.Context) (ma.Multiaddr, error) + Dial(ctx context.Context, p peer.ID) (ma.Multiaddr, error) } type AutoNATError struct { @@ -23,17 +23,16 @@ type AutoNATError struct { Text string } -func NewAutoNATClient(h host.Host, p peer.ID) AutoNATClient { - return &client{h: h, p: p} +func NewAutoNATClient(h host.Host) AutoNATClient { + return &client{h: h} } type client struct { h host.Host - p peer.ID } -func (c *client) Dial(ctx context.Context) (ma.Multiaddr, error) { - s, err := c.h.NewStream(ctx, c.p, AutoNATProto) +func (c *client) Dial(ctx context.Context, p peer.ID) (ma.Multiaddr, error) { + s, err := c.h.NewStream(ctx, p, AutoNATProto) if err != nil { return nil, err } From cd7a8757fa205a98942214585d08fa977e430c11 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 11 May 2018 08:13:53 +0300 Subject: [PATCH 23/51] mutex hat --- autonat.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/autonat.go b/autonat.go index 3a58e2b..8d96fd1 100644 --- a/autonat.go +++ b/autonat.go @@ -33,12 +33,13 @@ type AutoNAT interface { } type AmbientAutoNAT struct { - ctx context.Context - host host.Host + ctx context.Context + host host.Host + + mx sync.Mutex peers map[peer.ID]struct{} status NATStatus addr ma.Multiaddr - mx sync.Mutex } func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { From 7b3981e78620330cc6f0383aa228c32110e7c368 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 11 May 2018 09:40:36 +0300 Subject: [PATCH 24/51] docstrings and another mutex hat. --- autonat.go | 9 +++++++++ client.go | 6 ++++++ svc.go | 7 +++++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/autonat.go b/autonat.go index 8d96fd1..972cae2 100644 --- a/autonat.go +++ b/autonat.go @@ -15,8 +15,11 @@ import ( type NATStatus int const ( + // NAT status is unknown NATStatusUnknown NATStatus = iota + // NAT status is publicly dialable NATStatusPublic + // NAT status is private network NATStatusPrivate ) @@ -27,11 +30,16 @@ var ( AutoNATRequestTimeout = 60 * time.Second ) +// AutoNAT is the interface for ambient NAT autodiscovery type AutoNAT interface { + // Status returns the current NAT status Status() NATStatus + // PublicAddr returns the public dial address when NAT status is public and an + // error otherwise PublicAddr() (ma.Multiaddr, error) } +// AmbientAutoNAT is the implementation of ambient NAT autodiscovery type AmbientAutoNAT struct { ctx context.Context host host.Host @@ -42,6 +50,7 @@ type AmbientAutoNAT struct { addr ma.Multiaddr } +// NewAutoNAT creates a new ambient NAT auto-discovery instance func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { as := &AmbientAutoNAT{ ctx: ctx, diff --git a/client.go b/client.go index f996ff3..812ce47 100644 --- a/client.go +++ b/client.go @@ -14,15 +14,19 @@ import ( ma "github.com/multiformats/go-multiaddr" ) +// AutoNATClient is a stateless client interface to AutoNAT peers type AutoNATClient interface { + // Dial requests from a peer providing AutoNAT services to test dial back Dial(ctx context.Context, p peer.ID) (ma.Multiaddr, error) } +// AutoNATError is the class of errors signalled by AutoNAT services type AutoNATError struct { Status pb.Message_ResponseStatus Text string } +// NewAutoNATClient creates a fresh instance of an AutoNATClient func NewAutoNATClient(h host.Host) AutoNATClient { return &client{h: h} } @@ -80,11 +84,13 @@ func (e AutoNATError) 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.(AutoNATError) return ok && ae.IsDialError() } +// IsDialRefused returns true if the AutoNAT peer signalled refusal to dial back func IsDialRefused(e error) bool { ae, ok := e.(AutoNATError) return ok && ae.IsDialRefused() diff --git a/svc.go b/svc.go index 068b272..236f162 100644 --- a/svc.go +++ b/svc.go @@ -20,13 +20,16 @@ const P_CIRCUIT = 290 var AutoNATServiceResetInterval = 1 * time.Minute +// AutoNATService provides NAT autodetection services to other peers type AutoNATService struct { ctx context.Context dialer host.Host - peers map[peer.ID]struct{} - mx sync.Mutex + + mx sync.Mutex + peers map[peer.ID]struct{} } +// NewAutoNATService creates a new AutoNATService instance attached to a host func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) { dialer, err := libp2p.New(ctx) if err != nil { From cf04a09ba031bb1668270f7e97283cf99647f78b Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 11 May 2018 09:46:16 +0300 Subject: [PATCH 25/51] improve docstring for NewAutoNAT --- autonat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index 972cae2..c059659 100644 --- a/autonat.go +++ b/autonat.go @@ -50,7 +50,7 @@ type AmbientAutoNAT struct { addr ma.Multiaddr } -// NewAutoNAT creates a new ambient NAT auto-discovery instance +// NewAutoNAT creates a new ambient NAT autodiscovery instance attached to a host func NewAutoNAT(ctx context.Context, h host.Host) AutoNAT { as := &AmbientAutoNAT{ ctx: ctx, From 7c097ed53dfd4e8539de4fa47dbc96ae9271be4c Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 11 May 2018 15:50:50 +0300 Subject: [PATCH 26/51] improve NATStatusUknown docstring --- autonat.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index c059659..bb6561a 100644 --- a/autonat.go +++ b/autonat.go @@ -12,10 +12,13 @@ import ( ma "github.com/multiformats/go-multiaddr" ) +// NATStatus is the state of NAT as detected by the ambient service. type NATStatus int const ( - // NAT status is unknown + // NAT status is unknown; this means that the ambient serice has not been + // able to decide the presence of NAT in the most recent attempt to test + // dial through known autonat peers. initial state. NATStatusUnknown NATStatus = iota // NAT status is publicly dialable NATStatusPublic From 5837cc5ef5d2114420486a912b40759ec8097530 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 12 May 2018 08:20:04 +0300 Subject: [PATCH 27/51] fix typo --- autonat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autonat.go b/autonat.go index bb6561a..bdf1e8d 100644 --- a/autonat.go +++ b/autonat.go @@ -16,7 +16,7 @@ import ( type NATStatus int const ( - // NAT status is unknown; this means that the ambient serice has not been + // NAT status is unknown; this means that the ambient service has not been // able to decide the presence of NAT in the most recent attempt to test // dial through known autonat peers. initial state. NATStatusUnknown NATStatus = iota From 56a09663baac70c361c81cf74ce12f5bb54bffb3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 7 Sep 2018 13:36:50 +0300 Subject: [PATCH 28/51] update gx deps --- package.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 14a148a..9154209 100644 --- a/package.json +++ b/package.json @@ -7,50 +7,50 @@ "gxDependencies": [ { "author": "whyrusleeping", - "hash": "QmfZTdmunzKzAGJrSvXXQbQ5kLLUiEMX5vdwux7iXkdk7D", + "hash": "QmfH9FKYv3Jp1xiyL8sPchGBUBg6JA6XviwajAo3qgnT3B", "name": "go-libp2p-host", - "version": "2.1.7" + "version": "3.0.8" }, { "author": "whyrusleeping", - "hash": "QmcJukH2sAFjY3HdBKq35WDzWoL3UUu2gt9wdfqZTUyM74", + "hash": "QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W", "name": "go-libp2p-peer", - "version": "2.3.2" + "version": "2.3.7" }, { "author": "whyrusleeping", - "hash": "QmdeiKhUy1TVGBaKxt7y1QmBDLBdisSrLJ1x58Eoj4PXUh", + "hash": "QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64", "name": "go-libp2p-peerstore", - "version": "1.4.17" + "version": "1.4.24" }, { "author": "whyrusleeping", - "hash": "QmXoz9o2PT3tEzf7hicegwex5UgVP54n3k82K7jrWFyN86", + "hash": "QmQSbtGXCyNrj34LWL8EgXyNNYDZ8r3SwQcpW5pPxVhLnM", "name": "go-libp2p-net", - "version": "2.0.7" + "version": "3.0.8" }, { "author": "multiformats", - "hash": "QmWWQ2Txc2c6tqjsBpzg5Ar652cHPGNsQQp2SejkNmkUMb", + "hash": "QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7", "name": "go-multiaddr", - "version": "1.2.6" + "version": "1.3.0" }, { "author": "whyrusleeping", - "hash": "QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV", + "hash": "QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8", "name": "gogo-protobuf", "version": "0.0.0" }, { "author": "whyrusleeping", - "hash": "QmWsV6kzPaYGBDVyuUfWBvyQygEc9Qrv9vzo8vZ7X4mdLN", + "hash": "Qmf1u2efhjXYtuyP8SMHYtw4dCkbghnniex2PSp7baA7FP", "name": "go-libp2p", - "version": "5.0.17" + "version": "6.0.11" }, { - "hash": "QmTG23dvpBCBjqQwyDxV8CQT6jmS4PSftNr1VqHhE3MLy7", + "hash": "QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr", "name": "go-log", - "version": "1.4.1" + "version": "1.5.5" } ], "gxVersion": "0.12.1", From 54fb466dd3b9c587d08dde1080bed2c17985e2b2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 7 Sep 2018 13:37:10 +0300 Subject: [PATCH 29/51] regenerate protobuf --- pb/autonat.pb.go | 1018 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 994 insertions(+), 24 deletions(-) diff --git a/pb/autonat.pb.go b/pb/autonat.pb.go index d1c981c..3f2db2e 100644 --- a/pb/autonat.pb.go +++ b/pb/autonat.pb.go @@ -1,27 +1,25 @@ -// Code generated by protoc-gen-gogo. +// Code generated by protoc-gen-gogo. DO NOT EDIT. // source: autonat.proto -// DO NOT EDIT! -/* -Package autonat_pb is a generated protocol buffer package. - -It is generated from these files: - autonat.proto - -It has these top-level messages: - Message -*/ package autonat_pb import proto "github.com/gogo/protobuf/proto" import fmt "fmt" import math "math" +import io "io" + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + type Message_MessageType int32 const ( @@ -54,6 +52,9 @@ func (x *Message_MessageType) UnmarshalJSON(data []byte) error { *x = Message_MessageType(value) return nil } +func (Message_MessageType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0, 0} +} type Message_ResponseStatus int32 @@ -96,17 +97,51 @@ func (x *Message_ResponseStatus) UnmarshalJSON(data []byte) error { *x = Message_ResponseStatus(value) return nil } +func (Message_ResponseStatus) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0, 1} +} type Message struct { - Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=autonat.pb.Message_MessageType" json:"type,omitempty"` - Dial *Message_Dial `protobuf:"bytes,2,opt,name=dial" json:"dial,omitempty"` - DialResponse *Message_DialResponse `protobuf:"bytes,3,opt,name=dialResponse" json:"dialResponse,omitempty"` - XXX_unrecognized []byte `json:"-"` + Type *Message_MessageType `protobuf:"varint,1,opt,name=type,enum=autonat.pb.Message_MessageType" json:"type,omitempty"` + Dial *Message_Dial `protobuf:"bytes,2,opt,name=dial" json:"dial,omitempty"` + DialResponse *Message_DialResponse `protobuf:"bytes,3,opt,name=dialResponse" json:"dialResponse,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Message) Reset() { *m = Message{} } func (m *Message) String() string { return proto.CompactTextString(m) } func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0} +} +func (m *Message) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(dst, src) +} +func (m *Message) XXX_Size() int { + return m.Size() +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo func (m *Message) GetType() Message_MessageType { if m != nil && m.Type != nil { @@ -130,14 +165,45 @@ func (m *Message) GetDialResponse() *Message_DialResponse { } type Message_PeerInfo struct { - Id []byte `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` - Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` - XXX_unrecognized []byte `json:"-"` + Id []byte `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Addrs [][]byte `protobuf:"bytes,2,rep,name=addrs" json:"addrs,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Message_PeerInfo) Reset() { *m = Message_PeerInfo{} } func (m *Message_PeerInfo) String() string { return proto.CompactTextString(m) } func (*Message_PeerInfo) ProtoMessage() {} +func (*Message_PeerInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0, 0} +} +func (m *Message_PeerInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message_PeerInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message_PeerInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Message_PeerInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message_PeerInfo.Merge(dst, src) +} +func (m *Message_PeerInfo) XXX_Size() int { + return m.Size() +} +func (m *Message_PeerInfo) XXX_DiscardUnknown() { + xxx_messageInfo_Message_PeerInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_Message_PeerInfo proto.InternalMessageInfo func (m *Message_PeerInfo) GetId() []byte { if m != nil { @@ -154,13 +220,44 @@ func (m *Message_PeerInfo) GetAddrs() [][]byte { } type Message_Dial struct { - Peer *Message_PeerInfo `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` - XXX_unrecognized []byte `json:"-"` + Peer *Message_PeerInfo `protobuf:"bytes,1,opt,name=peer" json:"peer,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Message_Dial) Reset() { *m = Message_Dial{} } func (m *Message_Dial) String() string { return proto.CompactTextString(m) } func (*Message_Dial) ProtoMessage() {} +func (*Message_Dial) Descriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0, 1} +} +func (m *Message_Dial) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message_Dial) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message_Dial.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Message_Dial) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message_Dial.Merge(dst, src) +} +func (m *Message_Dial) XXX_Size() int { + return m.Size() +} +func (m *Message_Dial) XXX_DiscardUnknown() { + xxx_messageInfo_Message_Dial.DiscardUnknown(m) +} + +var xxx_messageInfo_Message_Dial proto.InternalMessageInfo func (m *Message_Dial) GetPeer() *Message_PeerInfo { if m != nil { @@ -170,15 +267,46 @@ func (m *Message_Dial) GetPeer() *Message_PeerInfo { } type Message_DialResponse struct { - Status *Message_ResponseStatus `protobuf:"varint,1,opt,name=status,enum=autonat.pb.Message_ResponseStatus" json:"status,omitempty"` - StatusText *string `protobuf:"bytes,2,opt,name=statusText" json:"statusText,omitempty"` - Addr []byte `protobuf:"bytes,3,opt,name=addr" json:"addr,omitempty"` - XXX_unrecognized []byte `json:"-"` + Status *Message_ResponseStatus `protobuf:"varint,1,opt,name=status,enum=autonat.pb.Message_ResponseStatus" json:"status,omitempty"` + StatusText *string `protobuf:"bytes,2,opt,name=statusText" json:"statusText,omitempty"` + Addr []byte `protobuf:"bytes,3,opt,name=addr" json:"addr,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Message_DialResponse) Reset() { *m = Message_DialResponse{} } func (m *Message_DialResponse) String() string { return proto.CompactTextString(m) } func (*Message_DialResponse) ProtoMessage() {} +func (*Message_DialResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_autonat_bd0ec7a019b57e9d, []int{0, 2} +} +func (m *Message_DialResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message_DialResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message_DialResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Message_DialResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message_DialResponse.Merge(dst, src) +} +func (m *Message_DialResponse) XXX_Size() int { + return m.Size() +} +func (m *Message_DialResponse) XXX_DiscardUnknown() { + xxx_messageInfo_Message_DialResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_Message_DialResponse proto.InternalMessageInfo func (m *Message_DialResponse) GetStatus() Message_ResponseStatus { if m != nil && m.Status != nil { @@ -209,3 +337,845 @@ func init() { proto.RegisterEnum("autonat.pb.Message_MessageType", Message_MessageType_name, Message_MessageType_value) proto.RegisterEnum("autonat.pb.Message_ResponseStatus", Message_ResponseStatus_name, Message_ResponseStatus_value) } +func (m *Message) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Type != nil { + dAtA[i] = 0x8 + i++ + i = encodeVarintAutonat(dAtA, i, uint64(*m.Type)) + } + if m.Dial != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintAutonat(dAtA, i, uint64(m.Dial.Size())) + n1, err := m.Dial.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n1 + } + if m.DialResponse != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintAutonat(dAtA, i, uint64(m.DialResponse.Size())) + n2, err := m.DialResponse.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n2 + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *Message_PeerInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message_PeerInfo) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Id != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintAutonat(dAtA, i, uint64(len(m.Id))) + i += copy(dAtA[i:], m.Id) + } + if len(m.Addrs) > 0 { + for _, b := range m.Addrs { + dAtA[i] = 0x12 + i++ + i = encodeVarintAutonat(dAtA, i, uint64(len(b))) + i += copy(dAtA[i:], b) + } + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *Message_Dial) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message_Dial) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Peer != nil { + dAtA[i] = 0xa + i++ + i = encodeVarintAutonat(dAtA, i, uint64(m.Peer.Size())) + n3, err := m.Peer.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n3 + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func (m *Message_DialResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message_DialResponse) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Status != nil { + dAtA[i] = 0x8 + i++ + i = encodeVarintAutonat(dAtA, i, uint64(*m.Status)) + } + if m.StatusText != nil { + dAtA[i] = 0x12 + i++ + i = encodeVarintAutonat(dAtA, i, uint64(len(*m.StatusText))) + i += copy(dAtA[i:], *m.StatusText) + } + if m.Addr != nil { + dAtA[i] = 0x1a + i++ + i = encodeVarintAutonat(dAtA, i, uint64(len(m.Addr))) + i += copy(dAtA[i:], m.Addr) + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + +func encodeVarintAutonat(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Message) Size() (n int) { + var l int + _ = l + if m.Type != nil { + n += 1 + sovAutonat(uint64(*m.Type)) + } + if m.Dial != nil { + l = m.Dial.Size() + n += 1 + l + sovAutonat(uint64(l)) + } + if m.DialResponse != nil { + l = m.DialResponse.Size() + n += 1 + l + sovAutonat(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Message_PeerInfo) Size() (n int) { + var l int + _ = l + if m.Id != nil { + l = len(m.Id) + n += 1 + l + sovAutonat(uint64(l)) + } + if len(m.Addrs) > 0 { + for _, b := range m.Addrs { + l = len(b) + n += 1 + l + sovAutonat(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Message_Dial) Size() (n int) { + var l int + _ = l + if m.Peer != nil { + l = m.Peer.Size() + n += 1 + l + sovAutonat(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Message_DialResponse) Size() (n int) { + var l int + _ = l + if m.Status != nil { + n += 1 + sovAutonat(uint64(*m.Status)) + } + if m.StatusText != nil { + l = len(*m.StatusText) + n += 1 + l + sovAutonat(uint64(l)) + } + if m.Addr != nil { + l = len(m.Addr) + n += 1 + l + sovAutonat(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovAutonat(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozAutonat(x uint64) (n int) { + return sovAutonat(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Message) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Message: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Message: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var v Message_MessageType + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (Message_MessageType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Type = &v + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Dial", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Dial == nil { + m.Dial = &Message_Dial{} + } + if err := m.Dial.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DialResponse", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.DialResponse == nil { + m.DialResponse = &Message_DialResponse{} + } + if err := m.DialResponse.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAutonat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAutonat + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Message_PeerInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PeerInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PeerInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = append(m.Id[:0], dAtA[iNdEx:postIndex]...) + if m.Id == nil { + m.Id = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addrs", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addrs = append(m.Addrs, make([]byte, postIndex-iNdEx)) + copy(m.Addrs[len(m.Addrs)-1], dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAutonat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAutonat + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Message_Dial) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Dial: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Dial: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Peer", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Peer == nil { + m.Peer = &Message_PeerInfo{} + } + if err := m.Peer.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAutonat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAutonat + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Message_DialResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: DialResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: DialResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType) + } + var v Message_ResponseStatus + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (Message_ResponseStatus(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Status = &v + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field StatusText", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.StatusText = &s + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addr", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAutonat + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthAutonat + } + postIndex := iNdEx + byteLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addr = append(m.Addr[:0], dAtA[iNdEx:postIndex]...) + if m.Addr == nil { + m.Addr = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAutonat(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthAutonat + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipAutonat(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAutonat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAutonat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAutonat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthAutonat + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowAutonat + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipAutonat(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthAutonat = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowAutonat = fmt.Errorf("proto: integer overflow") +) + +func init() { proto.RegisterFile("autonat.proto", fileDescriptor_autonat_bd0ec7a019b57e9d) } + +var fileDescriptor_autonat_bd0ec7a019b57e9d = []byte{ + // 372 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0xcf, 0x8a, 0xda, 0x50, + 0x14, 0xc6, 0xbd, 0x31, 0xb5, 0xf6, 0x18, 0xc3, 0xed, 0xa1, 0x85, 0x20, 0x25, 0x0d, 0x59, 0x49, + 0x29, 0x22, 0x76, 0x53, 0xba, 0x53, 0x72, 0x0b, 0xd2, 0x56, 0xed, 0x49, 0x5c, 0x87, 0x94, 0xdc, + 0x0e, 0x01, 0x31, 0x21, 0x89, 0x30, 0x6e, 0xe6, 0x89, 0x66, 0x3b, 0xef, 0xe0, 0x72, 0x1e, 0x61, + 0xf0, 0x49, 0x86, 0x5c, 0xa3, 0xa3, 0xe0, 0xac, 0xce, 0x1f, 0x7e, 0xdf, 0x39, 0x1f, 0x1f, 0x74, + 0xa3, 0x4d, 0x99, 0xae, 0xa3, 0x72, 0x90, 0xe5, 0x69, 0x99, 0x22, 0x9c, 0xc6, 0x7f, 0xee, 0x83, + 0x0e, 0x6f, 0xff, 0xc8, 0xa2, 0x88, 0x6e, 0x24, 0x7e, 0x03, 0xbd, 0xdc, 0x66, 0xd2, 0x62, 0x0e, + 0xeb, 0x9b, 0xa3, 0xcf, 0x83, 0x17, 0x6c, 0x50, 0x23, 0xc7, 0x1a, 0x6c, 0x33, 0x49, 0x0a, 0xc6, + 0xaf, 0xa0, 0xc7, 0x49, 0xb4, 0xb2, 0x34, 0x87, 0xf5, 0x3b, 0x23, 0xeb, 0x9a, 0xc8, 0x4b, 0xa2, + 0x15, 0x29, 0x0a, 0x3d, 0x30, 0xaa, 0x4a, 0xb2, 0xc8, 0xd2, 0x75, 0x21, 0xad, 0xa6, 0x52, 0x39, + 0xaf, 0xaa, 0x6a, 0x8e, 0x2e, 0x54, 0xbd, 0x21, 0xb4, 0x17, 0x52, 0xe6, 0xd3, 0xf5, 0xff, 0x14, + 0x4d, 0xd0, 0x92, 0x58, 0x59, 0x36, 0x48, 0x4b, 0x62, 0xfc, 0x00, 0x6f, 0xa2, 0x38, 0xce, 0x0b, + 0x4b, 0x73, 0x9a, 0x7d, 0x83, 0x0e, 0x43, 0xef, 0x3b, 0xe8, 0xd5, 0x3d, 0x1c, 0x82, 0x9e, 0x49, + 0x99, 0x2b, 0xbe, 0x33, 0xfa, 0x74, 0xed, 0xef, 0xf1, 0x32, 0x29, 0xb2, 0x77, 0x07, 0xc6, 0xb9, + 0x13, 0xfc, 0x01, 0xad, 0xa2, 0x8c, 0xca, 0x4d, 0x51, 0xc7, 0xe4, 0x5e, 0xbb, 0x71, 0xa4, 0x7d, + 0x45, 0x52, 0xad, 0x40, 0x1b, 0xe0, 0xd0, 0x05, 0xf2, 0xb6, 0x54, 0x89, 0xbd, 0xa3, 0xb3, 0x0d, + 0x22, 0xe8, 0x95, 0x5d, 0x95, 0x8a, 0x41, 0xaa, 0x77, 0xbf, 0x40, 0xe7, 0x2c, 0x74, 0x6c, 0x83, + 0xee, 0x4d, 0xc7, 0xbf, 0x79, 0x03, 0xdf, 0x43, 0xb7, 0xea, 0x42, 0x12, 0xfe, 0x62, 0x3e, 0xf3, + 0x05, 0x67, 0x6e, 0x02, 0xe6, 0xe5, 0x67, 0x6c, 0x81, 0x36, 0xff, 0xc5, 0x1b, 0xc8, 0xc1, 0x10, + 0xa1, 0xc2, 0x05, 0xd1, 0x9c, 0x78, 0x8c, 0x08, 0x66, 0xbd, 0x21, 0xf1, 0x73, 0xe9, 0x0b, 0x8f, + 0x4b, 0x44, 0xe8, 0x8a, 0x70, 0x32, 0xf6, 0x42, 0x12, 0x7f, 0x97, 0xc2, 0x0f, 0xf8, 0x8e, 0xe1, + 0x47, 0xe0, 0x22, 0x9c, 0xce, 0x02, 0x41, 0xb3, 0x93, 0xfa, 0x5e, 0x9b, 0x18, 0xbb, 0xbd, 0xcd, + 0x1e, 0xf7, 0x36, 0x7b, 0xda, 0xdb, 0xec, 0x39, 0x00, 0x00, 0xff, 0xff, 0x8e, 0xe2, 0x93, 0x4e, + 0x61, 0x02, 0x00, 0x00, +} From 66ca387132faad68d8ecf1f939cc585da5e77c0a Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 11:30:56 +0300 Subject: [PATCH 30/51] svc: construct dialer host without listen addrs --- svc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svc.go b/svc.go index 236f162..32d089e 100644 --- a/svc.go +++ b/svc.go @@ -31,7 +31,7 @@ type AutoNATService struct { // NewAutoNATService creates a new AutoNATService instance attached to a host func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) { - dialer, err := libp2p.New(ctx) + dialer, err := libp2p.New(ctx, libp2p.NoListenAddrs) if err != nil { return nil, err } From 3abf9c7fe4a84e74f72432890bc26d8f1608e894 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 14:21:04 +0300 Subject: [PATCH 31/51] accept libp2p options for the dialer constructor in NewAutoNATService --- svc.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/svc.go b/svc.go index 32d089e..fd0d08f 100644 --- a/svc.go +++ b/svc.go @@ -30,8 +30,9 @@ type AutoNATService struct { } // NewAutoNATService creates a new AutoNATService instance attached to a host -func NewAutoNATService(ctx context.Context, h host.Host) (*AutoNATService, error) { - dialer, err := libp2p.New(ctx, libp2p.NoListenAddrs) +func NewAutoNATService(ctx context.Context, h host.Host, opts ...libp2p.Option) (*AutoNATService, error) { + opts = append(opts, libp2p.NoListenAddrs) + dialer, err := libp2p.New(ctx, opts...) if err != nil { return nil, err } From 3b679e0964c80995d7d5c89ffacfae7cb09c3adc Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 15:57:48 +0300 Subject: [PATCH 32/51] make service dialback timeout configurable; useful for tests --- svc.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/svc.go b/svc.go index fd0d08f..6ec7e7c 100644 --- a/svc.go +++ b/svc.go @@ -18,7 +18,10 @@ import ( const P_CIRCUIT = 290 -var AutoNATServiceResetInterval = 1 * time.Minute +var ( + AutoNATServiceDialTimeout = 42 * time.Second + AutoNATServiceResetInterval = 1 * time.Minute +) // AutoNATService provides NAT autodetection services to other peers type AutoNATService struct { @@ -144,7 +147,7 @@ func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { as.peers[pi.ID] = struct{}{} as.mx.Unlock() - ctx, cancel := context.WithTimeout(as.ctx, 42*time.Second) + ctx, cancel := context.WithTimeout(as.ctx, AutoNATServiceDialTimeout) defer cancel() err := as.dialer.Connect(ctx, pi) From 1cba297e02b57bfce617468890b8d5c9e20b5a83 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 16:10:11 +0300 Subject: [PATCH 33/51] basic service tests --- svc_test.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 svc_test.go diff --git a/svc_test.go b/svc_test.go new file mode 100644 index 0000000..8cc7f66 --- /dev/null +++ b/svc_test.go @@ -0,0 +1,128 @@ +package autonat + +import ( + "context" + "net" + "testing" + "time" + + libp2p "github.com/libp2p/go-libp2p" + host "github.com/libp2p/go-libp2p-host" + pstore "github.com/libp2p/go-libp2p-peerstore" +) + +func makeAutoNATService(ctx context.Context, t *testing.T) (host.Host, *AutoNATService) { + h, err := libp2p.New(ctx) + if err != nil { + t.Fatal(err) + } + + as, err := NewAutoNATService(ctx, h) + if err != nil { + t.Fatal(err) + } + + return h, as +} + +func makeAutoNATClient(ctx context.Context, t *testing.T) (host.Host, AutoNATClient) { + h, err := libp2p.New(ctx) + if err != nil { + t.Fatal(err) + } + + cli := NewAutoNATClient(h) + return h, cli +} + +func connect(t *testing.T, a, b host.Host) { + pinfo := pstore.PeerInfo{ID: a.ID(), Addrs: a.Addrs()} + err := b.Connect(context.Background(), pinfo) + if err != nil { + t.Fatal(err) + } +} + +// Note: these tests assume that the host has only private inet addresses! +func TestAutoNATServiceDialError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + save := AutoNATServiceDialTimeout + AutoNATServiceDialTimeout = 1 * time.Second + + hs, _ := makeAutoNATService(ctx, t) + hc, ac := makeAutoNATClient(ctx, t) + connect(t, hs, hc) + + _, err := ac.Dial(ctx, hs.ID()) + if err == nil { + t.Fatal("Dial back succeeded unexpectedly!") + } + + if !IsDialError(err) { + t.Fatal(err) + } + + AutoNATServiceDialTimeout = save +} + +func TestAutoNATServiceDialSuccess(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + save := private4 + private4 = []*net.IPNet{} + + hs, _ := makeAutoNATService(ctx, t) + hc, ac := makeAutoNATClient(ctx, t) + connect(t, hs, hc) + + _, err := ac.Dial(ctx, hs.ID()) + if err != nil { + t.Fatalf("Dial back failed: %s", err.Error()) + } + + private4 = save +} + +func TestAutoNATServiceDialRateLimiter(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + save1 := AutoNATServiceDialTimeout + AutoNATServiceDialTimeout = 1 * time.Second + save2 := AutoNATServiceResetInterval + AutoNATServiceResetInterval = 1 * time.Second + save3 := private4 + private4 = []*net.IPNet{} + + hs, _ := makeAutoNATService(ctx, t) + hc, ac := makeAutoNATClient(ctx, t) + connect(t, hs, hc) + + _, err := ac.Dial(ctx, hs.ID()) + if err != nil { + t.Fatal(err) + } + + _, err = ac.Dial(ctx, hs.ID()) + if err == nil { + t.Fatal("Dial back succeeded unexpectedly!") + } + + if !IsDialRefused(err) { + t.Fatal(err) + } + + time.Sleep(2 * time.Second) + + _, err = ac.Dial(ctx, hs.ID()) + if err != nil { + t.Fatal(err) + } + + AutoNATServiceDialTimeout = save1 + AutoNATServiceResetInterval = save2 + private4 = save3 +} From dd7c7a91c97c54d8f1a043343fdeb447a0eb0535 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 16:30:28 +0300 Subject: [PATCH 34/51] Makefile and travis build file --- .travis.yml | 22 ++++++++++++++++++++++ Makefile | 7 +++++++ 2 files changed, 29 insertions(+) create mode 100644 .travis.yml create mode 100644 Makefile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e31ed20 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,22 @@ +os: + - linux + +sudo: false + +language: go + +go: + - 1.9.x + +install: + - make deps + +script: + - bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh) + +cache: + directories: + - $GOPATH/src/gx + +notifications: + email: false diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3c64b37 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +gx: + go get -u github.com/whyrusleeping/gx + go get -u github.com/whyrusleeping/gx-go + +deps: gx + gx --verbose install --global + gx-go rewrite From 9ff7df3bb8d8e28719607c696cd6b0e9ef7e77d2 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 8 Sep 2018 17:01:21 +0300 Subject: [PATCH 35/51] test for ambient autonat functionality --- autonat_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 autonat_test.go diff --git a/autonat_test.go b/autonat_test.go new file mode 100644 index 0000000..019a590 --- /dev/null +++ b/autonat_test.go @@ -0,0 +1,84 @@ +package autonat + +import ( + "context" + "net" + "testing" + "time" + + libp2p "github.com/libp2p/go-libp2p" + host "github.com/libp2p/go-libp2p-host" +) + +func makeAutoNAT(ctx context.Context, t *testing.T) (host.Host, AutoNAT) { + h, err := libp2p.New(ctx) + if err != nil { + t.Fatal(err) + } + + a := NewAutoNAT(ctx, h) + + return h, a +} + +// Note: these tests assume the host has only private inet addresses! +func TestAutoNATPrivate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + save1 := AutoNATBootDelay + AutoNATBootDelay = 1 * time.Second + save2 := AutoNATRefreshInterval + AutoNATRefreshInterval = 1 * time.Second + + hs, _ := makeAutoNATService(ctx, t) + hc, an := makeAutoNAT(ctx, t) + + status := an.Status() + if status != NATStatusUnknown { + t.Fatalf("unexpected NAT status: %d", status) + } + + connect(t, hs, hc) + time.Sleep(2 * time.Second) + + status = an.Status() + if status != NATStatusPrivate { + t.Fatalf("unexpected NAT status: %d", status) + } + + AutoNATBootDelay = save1 + AutoNATRefreshInterval = save2 +} + +func TestAutoNATPublic(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + save1 := AutoNATBootDelay + AutoNATBootDelay = 1 * time.Second + save2 := AutoNATRefreshInterval + AutoNATRefreshInterval = 1 * time.Second + save3 := private4 + private4 = []*net.IPNet{} + + hs, _ := makeAutoNATService(ctx, t) + hc, an := makeAutoNAT(ctx, t) + + status := an.Status() + if status != NATStatusUnknown { + t.Fatalf("unexpected NAT status: %d", status) + } + + connect(t, hs, hc) + time.Sleep(2 * time.Second) + + status = an.Status() + if status != NATStatusPublic { + t.Fatalf("unexpected NAT status: %d", status) + } + + AutoNATBootDelay = save1 + AutoNATRefreshInterval = save2 + private4 = save3 +} From 0fdf1b07cb70ddad302088a2217ea4687fb731ac Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 29 Sep 2018 11:09:48 +0300 Subject: [PATCH 36/51] address review comments --- autonat.go | 15 ++++++++++----- client.go | 7 ++++--- svc.go | 2 +- svc_test.go | 10 +++++----- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/autonat.go b/autonat.go index bdf1e8d..037a094 100644 --- a/autonat.go +++ b/autonat.go @@ -8,6 +8,7 @@ import ( "time" host "github.com/libp2p/go-libp2p-host" + inet "github.com/libp2p/go-libp2p-net" peer "github.com/libp2p/go-libp2p-peer" ma "github.com/multiformats/go-multiaddr" ) @@ -29,8 +30,7 @@ const ( var ( AutoNATBootDelay = 15 * time.Second AutoNATRefreshInterval = 15 * time.Minute - - AutoNATRequestTimeout = 60 * time.Second + AutoNATRequestTimeout = 60 * time.Second ) // AutoNAT is the interface for ambient NAT autodiscovery @@ -86,7 +86,12 @@ func (as *AmbientAutoNAT) PublicAddr() (ma.Multiaddr, error) { func (as *AmbientAutoNAT) background() { // wait a bit for the node to come online and establish some connections // before starting autodetection - time.Sleep(AutoNATBootDelay) + select { + case <-time.After(AutoNATBootDelay): + case <-as.ctx.Done(): + return + } + for { as.autodetect() select { @@ -109,7 +114,7 @@ func (as *AmbientAutoNAT) autodetect() { for _, p := range peers { ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) - a, err := cli.Dial(ctx, p) + a, err := cli.DialBack(ctx, p) cancel() switch { @@ -148,7 +153,7 @@ func (as *AmbientAutoNAT) getPeers() []peer.ID { peers := make([]peer.ID, 0, len(as.peers)) for p := range as.peers { - if len(as.host.Network().ConnsToPeer(p)) > 0 { + if as.host.Network().Connectedness(p) == inet.Connected { peers = append(peers, p) } } diff --git a/client.go b/client.go index 812ce47..468bc68 100644 --- a/client.go +++ b/client.go @@ -16,8 +16,9 @@ import ( // AutoNATClient is a stateless client interface to AutoNAT peers type AutoNATClient interface { - // Dial requests from a peer providing AutoNAT services to test dial back - Dial(ctx context.Context, p peer.ID) (ma.Multiaddr, error) + // DialBack requests from a peer providing AutoNAT services to test dial back + // and report the address on a successful connection. + DialBack(ctx context.Context, p peer.ID) (ma.Multiaddr, error) } // AutoNATError is the class of errors signalled by AutoNAT services @@ -35,7 +36,7 @@ type client struct { h host.Host } -func (c *client) Dial(ctx context.Context, p peer.ID) (ma.Multiaddr, error) { +func (c *client) DialBack(ctx context.Context, p peer.ID) (ma.Multiaddr, error) { s, err := c.h.NewStream(ctx, p, AutoNATProto) if err != nil { return nil, err diff --git a/svc.go b/svc.go index 6ec7e7c..b25ead6 100644 --- a/svc.go +++ b/svc.go @@ -166,7 +166,7 @@ func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { } ra := conns[0].RemoteMultiaddr() - conns[0].Close() + as.dialer.Network().ClosePeer(pi.ID) return newDialResponseOK(ra) } diff --git a/svc_test.go b/svc_test.go index 8cc7f66..77d482b 100644 --- a/svc_test.go +++ b/svc_test.go @@ -55,7 +55,7 @@ func TestAutoNATServiceDialError(t *testing.T) { hc, ac := makeAutoNATClient(ctx, t) connect(t, hs, hc) - _, err := ac.Dial(ctx, hs.ID()) + _, err := ac.DialBack(ctx, hs.ID()) if err == nil { t.Fatal("Dial back succeeded unexpectedly!") } @@ -78,7 +78,7 @@ func TestAutoNATServiceDialSuccess(t *testing.T) { hc, ac := makeAutoNATClient(ctx, t) connect(t, hs, hc) - _, err := ac.Dial(ctx, hs.ID()) + _, err := ac.DialBack(ctx, hs.ID()) if err != nil { t.Fatalf("Dial back failed: %s", err.Error()) } @@ -101,12 +101,12 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) { hc, ac := makeAutoNATClient(ctx, t) connect(t, hs, hc) - _, err := ac.Dial(ctx, hs.ID()) + _, err := ac.DialBack(ctx, hs.ID()) if err != nil { t.Fatal(err) } - _, err = ac.Dial(ctx, hs.ID()) + _, err = ac.DialBack(ctx, hs.ID()) if err == nil { t.Fatal("Dial back succeeded unexpectedly!") } @@ -117,7 +117,7 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) { time.Sleep(2 * time.Second) - _, err = ac.Dial(ctx, hs.ID()) + _, err = ac.DialBack(ctx, hs.ID()) if err != nil { t.Fatal(err) } From 46d352fc09280b21c7aea7dc02cf9da41f63dbc0 Mon Sep 17 00:00:00 2001 From: vyzo Date: Sat, 29 Sep 2018 11:38:53 +0300 Subject: [PATCH 37/51] use the protocol list by identify, don't emit chatter on every connection --- notify.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/notify.go b/notify.go index 4e2bc9d..30ad9cd 100644 --- a/notify.go +++ b/notify.go @@ -1,8 +1,9 @@ package autonat import ( + "time" + inet "github.com/libp2p/go-libp2p-net" - peer "github.com/libp2p/go-libp2p-peer" ma "github.com/multiformats/go-multiaddr" ) @@ -14,18 +15,25 @@ func (as *AmbientAutoNAT) OpenedStream(net inet.Network, s inet.Stream) {} func (as *AmbientAutoNAT) ClosedStream(net inet.Network, s inet.Stream) {} func (as *AmbientAutoNAT) Connected(net inet.Network, c inet.Conn) { - go func(p peer.ID) { - s, err := as.host.NewStream(as.ctx, p, AutoNATProto) + p := c.RemotePeer() + + go func() { + // add some delay for identify + time.Sleep(250 * time.Millisecond) + + protos, err := as.host.Peerstore().SupportsProtocols(p, AutoNATProto) if err != nil { + log.Debugf("error retrieving supported protocols for peer %s: %s", p, err) return } - s.Close() - log.Infof("Discovered AutoNAT peer %s", p.Pretty()) - as.mx.Lock() - as.peers[p] = struct{}{} - as.mx.Unlock() - }(c.RemotePeer()) + if len(protos) > 0 { + log.Infof("Discovered AutoNAT peer %s", p.Pretty()) + as.mx.Lock() + as.peers[p] = struct{}{} + as.mx.Unlock() + } + }() } func (as *AmbientAutoNAT) Disconnected(net inet.Network, c inet.Conn) {} From 0a4e2150cb6c92c0bb634ec500693d5f274811cf Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Oct 2018 16:15:58 +0300 Subject: [PATCH 38/51] add observed address to the dialback set --- svc.go | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/svc.go b/svc.go index b25ead6..479629f 100644 --- a/svc.go +++ b/svc.go @@ -78,7 +78,7 @@ func (as *AutoNATService) handleStream(s inet.Stream) { return } - dr := as.handleDial(pid, req.GetDial().GetPeer()) + dr := as.handleDial(pid, s.Conn().RemoteMultiaddr(), req.GetDial().GetPeer()) res.Type = pb.Message_DIAL_RESPONSE.Enum() res.DialResponse = dr @@ -90,7 +90,7 @@ func (as *AutoNATService) handleStream(s inet.Stream) { } } -func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse { +func (as *AutoNATService) handleDial(p peer.ID, obsaddr ma.Multiaddr, mpi *pb.Message_PeerInfo) *pb.Message_DialResponse { if mpi == nil { return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info") } @@ -108,6 +108,14 @@ func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Me } addrs := make([]ma.Multiaddr, 0) + seen := make(map[string]struct{}) + + // add observed addr to the list of addresses to dial + if !as.skipDial(obsaddr) { + addrs = append(addrs, obsaddr) + seen[obsaddr.String()] = struct{}{} + } + for _, maddr := range mpi.GetAddrs() { addr, err := ma.NewMultiaddrBytes(maddr) if err != nil { @@ -115,18 +123,18 @@ func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Me continue } - // skip relay addresses - _, err = addr.ValueForProtocol(P_CIRCUIT) - if err == nil { + if as.skipDial(addr) { continue } - // skip private network (unroutable) addresses - if !isPublicAddr(addr) { + str := addr.String() + _, ok := seen[str] + if ok { continue } addrs = append(addrs, addr) + seen[str] = struct{}{} } if len(addrs) == 0 { @@ -136,6 +144,21 @@ func (as *AutoNATService) handleDial(p peer.ID, mpi *pb.Message_PeerInfo) *pb.Me return as.doDial(pstore.PeerInfo{ID: p, Addrs: addrs}) } +func (as *AutoNATService) skipDial(addr ma.Multiaddr) bool { + // skip relay addresses + _, err := addr.ValueForProtocol(P_CIRCUIT) + if err == nil { + return true + } + + // skip private network (unroutable) addresses + if !isPublicAddr(addr) { + return true + } + + return false +} + func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { // rate limit check as.mx.Lock() From 91c209cf7b200447d0bf49828120a46b100f7217 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Oct 2018 16:22:58 +0300 Subject: [PATCH 39/51] ensure test hosts are only on loopback --- autonat_test.go | 2 +- svc_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/autonat_test.go b/autonat_test.go index 019a590..570c4a2 100644 --- a/autonat_test.go +++ b/autonat_test.go @@ -11,7 +11,7 @@ import ( ) func makeAutoNAT(ctx context.Context, t *testing.T) (host.Host, AutoNAT) { - h, err := libp2p.New(ctx) + h, err := libp2p.New(ctx, libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) if err != nil { t.Fatal(err) } diff --git a/svc_test.go b/svc_test.go index 77d482b..ba2395b 100644 --- a/svc_test.go +++ b/svc_test.go @@ -12,7 +12,7 @@ import ( ) func makeAutoNATService(ctx context.Context, t *testing.T) (host.Host, *AutoNATService) { - h, err := libp2p.New(ctx) + h, err := libp2p.New(ctx, libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) if err != nil { t.Fatal(err) } @@ -26,7 +26,7 @@ func makeAutoNATService(ctx context.Context, t *testing.T) (host.Host, *AutoNATS } func makeAutoNATClient(ctx context.Context, t *testing.T) (host.Host, AutoNATClient) { - h, err := libp2p.New(ctx) + h, err := libp2p.New(ctx, libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) if err != nil { t.Fatal(err) } From 00d2fea533b83be5af0c4a03541dc7949baff9a1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Oct 2018 16:28:21 +0300 Subject: [PATCH 40/51] add /libp2p prefix in protocol string --- proto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto.go b/proto.go index 7d28d4b..c9768cb 100644 --- a/proto.go +++ b/proto.go @@ -8,7 +8,7 @@ import ( ma "github.com/multiformats/go-multiaddr" ) -const AutoNATProto = "/autonat/1.0.0" +const AutoNATProto = "/libp2p/autonat/1.0.0" var log = logging.Logger("autonat") From 8ea9f1b346487d560c88ce84bc06543a952a75d1 Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Oct 2018 19:41:08 +0300 Subject: [PATCH 41/51] configurable throttle for service rate limiter --- svc.go | 15 +++++++++------ svc_test.go | 7 +++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/svc.go b/svc.go index 479629f..95c66bd 100644 --- a/svc.go +++ b/svc.go @@ -21,6 +21,8 @@ const P_CIRCUIT = 290 var ( AutoNATServiceDialTimeout = 42 * time.Second AutoNATServiceResetInterval = 1 * time.Minute + + AutoNATServiceThrottle = 3 ) // AutoNATService provides NAT autodetection services to other peers @@ -28,8 +30,9 @@ type AutoNATService struct { ctx context.Context dialer host.Host + // rate limiter mx sync.Mutex - peers map[peer.ID]struct{} + peers map[peer.ID]int } // NewAutoNATService creates a new AutoNATService instance attached to a host @@ -43,7 +46,7 @@ func NewAutoNATService(ctx context.Context, h host.Host, opts ...libp2p.Option) as := &AutoNATService{ ctx: ctx, dialer: dialer, - peers: make(map[peer.ID]struct{}), + peers: make(map[peer.ID]int), } h.SetStreamHandler(AutoNATProto, as.handleStream) @@ -162,12 +165,12 @@ func (as *AutoNATService) skipDial(addr ma.Multiaddr) bool { func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { // rate limit check as.mx.Lock() - _, ok := as.peers[pi.ID] - if ok { + count := as.peers[pi.ID] + if count >= AutoNATServiceThrottle { as.mx.Unlock() return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials") } - as.peers[pi.ID] = struct{}{} + as.peers[pi.ID] = count + 1 as.mx.Unlock() ctx, cancel := context.WithTimeout(as.ctx, AutoNATServiceDialTimeout) @@ -201,7 +204,7 @@ func (as *AutoNATService) resetPeers() { select { case <-ticker.C: as.mx.Lock() - as.peers = make(map[peer.ID]struct{}) + as.peers = make(map[peer.ID]int) as.mx.Unlock() case <-as.ctx.Done(): diff --git a/svc_test.go b/svc_test.go index ba2395b..cfa4068 100644 --- a/svc_test.go +++ b/svc_test.go @@ -94,7 +94,9 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) { AutoNATServiceDialTimeout = 1 * time.Second save2 := AutoNATServiceResetInterval AutoNATServiceResetInterval = 1 * time.Second - save3 := private4 + save3 := AutoNATServiceThrottle + AutoNATServiceThrottle = 1 + save4 := private4 private4 = []*net.IPNet{} hs, _ := makeAutoNATService(ctx, t) @@ -124,5 +126,6 @@ func TestAutoNATServiceDialRateLimiter(t *testing.T) { AutoNATServiceDialTimeout = save1 AutoNATServiceResetInterval = save2 - private4 = save3 + AutoNATServiceThrottle = save3 + private4 = save4 } From d9a0d1ae721d42a2277abe6cfcc33a656e1ce5cb Mon Sep 17 00:00:00 2001 From: vyzo Date: Wed, 3 Oct 2018 19:52:42 +0300 Subject: [PATCH 42/51] call AutoNATService.peers something else (reqs) --- svc.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/svc.go b/svc.go index 95c66bd..4d395d2 100644 --- a/svc.go +++ b/svc.go @@ -31,8 +31,8 @@ type AutoNATService struct { dialer host.Host // rate limiter - mx sync.Mutex - peers map[peer.ID]int + mx sync.Mutex + reqs map[peer.ID]int } // NewAutoNATService creates a new AutoNATService instance attached to a host @@ -46,11 +46,11 @@ func NewAutoNATService(ctx context.Context, h host.Host, opts ...libp2p.Option) as := &AutoNATService{ ctx: ctx, dialer: dialer, - peers: make(map[peer.ID]int), + reqs: make(map[peer.ID]int), } h.SetStreamHandler(AutoNATProto, as.handleStream) - go as.resetPeers() + go as.resetRateLimiter() return as, nil } @@ -165,12 +165,12 @@ func (as *AutoNATService) skipDial(addr ma.Multiaddr) bool { func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { // rate limit check as.mx.Lock() - count := as.peers[pi.ID] + count := as.reqs[pi.ID] if count >= AutoNATServiceThrottle { as.mx.Unlock() return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials") } - as.peers[pi.ID] = count + 1 + as.reqs[pi.ID] = count + 1 as.mx.Unlock() ctx, cancel := context.WithTimeout(as.ctx, AutoNATServiceDialTimeout) @@ -196,7 +196,7 @@ func (as *AutoNATService) doDial(pi pstore.PeerInfo) *pb.Message_DialResponse { return newDialResponseOK(ra) } -func (as *AutoNATService) resetPeers() { +func (as *AutoNATService) resetRateLimiter() { ticker := time.NewTicker(AutoNATServiceResetInterval) defer ticker.Stop() @@ -204,7 +204,7 @@ func (as *AutoNATService) resetPeers() { select { case <-ticker.C: as.mx.Lock() - as.peers = make(map[peer.ID]int) + as.reqs = make(map[peer.ID]int) as.mx.Unlock() case <-as.ctx.Done(): From aadb8dbcb5d0a8a3a006f546255b4cad871b6d94 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 4 Oct 2018 13:37:51 +0300 Subject: [PATCH 43/51] use more peer dial errors for increased confidence in NATPrivate state 3 times is enemy action; this is more resilient to transient dial errors. --- autonat.go | 45 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/autonat.go b/autonat.go index 037a094..e006900 100644 --- a/autonat.go +++ b/autonat.go @@ -29,6 +29,7 @@ const ( var ( AutoNATBootDelay = 15 * time.Second + AutoNATRetryInterval = 60 * time.Second AutoNATRefreshInterval = 15 * time.Minute AutoNATRequestTimeout = 60 * time.Second ) @@ -47,10 +48,11 @@ type AmbientAutoNAT struct { ctx context.Context host host.Host - mx sync.Mutex - peers map[peer.ID]struct{} - status NATStatus - addr ma.Multiaddr + mx sync.Mutex + peers map[peer.ID]struct{} + status NATStatus + addr ma.Multiaddr + confidence int } // NewAutoNAT creates a new ambient NAT autodiscovery instance attached to a host @@ -94,8 +96,14 @@ func (as *AmbientAutoNAT) background() { for { as.autodetect() + + delay := AutoNATRefreshInterval + if as.status == NATStatusUnknown { + delay = AutoNATRetryInterval + } + select { - case <-time.After(AutoNATRefreshInterval): + case <-time.After(delay): case <-as.ctx.Done(): return } @@ -111,6 +119,7 @@ func (as *AmbientAutoNAT) autodetect() { } cli := NewAutoNATClient(as.host) + failures := 0 for _, p := range peers { ctx, cancel := context.WithTimeout(as.ctx, AutoNATRequestTimeout) @@ -123,15 +132,21 @@ func (as *AmbientAutoNAT) autodetect() { as.mx.Lock() as.addr = a as.status = NATStatusPublic + as.confidence = 0 as.mx.Unlock() return case IsDialError(err): - log.Debugf("NAT status is private; dial error through %s: %s", p.Pretty(), err.Error()) - as.mx.Lock() - as.status = NATStatusPrivate - as.mx.Unlock() - return + log.Debugf("dial error through %s: %s", p.Pretty(), err.Error()) + failures++ + if failures >= 3 || as.confidence >= 3 { // 3 times is enemy action + log.Debugf("NAT status is private") + as.mx.Lock() + as.status = NATStatusPrivate + as.confidence = 3 + as.mx.Unlock() + return + } default: log.Debugf("Error dialing through %s: %s", p.Pretty(), err.Error()) @@ -139,7 +154,15 @@ func (as *AmbientAutoNAT) autodetect() { } as.mx.Lock() - as.status = NATStatusUnknown + if failures > 0 { + as.status = NATStatusPrivate + as.confidence++ + log.Debugf("NAT status is private") + } else { + as.status = NATStatusUnknown + as.confidence = 0 + log.Debugf("NAT status is unknown") + } as.mx.Unlock() } From d7f55b0524da51340ab00fc38f4b06b34b87f01c Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 4 Oct 2018 13:46:39 +0300 Subject: [PATCH 44/51] use more peers if less than 3 are connected --- autonat.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/autonat.go b/autonat.go index e006900..4f766ce 100644 --- a/autonat.go +++ b/autonat.go @@ -29,7 +29,7 @@ const ( var ( AutoNATBootDelay = 15 * time.Second - AutoNATRetryInterval = 60 * time.Second + AutoNATRetryInterval = 90 * time.Second AutoNATRefreshInterval = 15 * time.Minute AutoNATRequestTimeout = 60 * time.Second ) @@ -174,23 +174,24 @@ func (as *AmbientAutoNAT) getPeers() []peer.ID { return nil } - peers := make([]peer.ID, 0, len(as.peers)) + var connected, others []peer.ID + for p := range as.peers { if as.host.Network().Connectedness(p) == inet.Connected { - peers = append(peers, p) + connected = append(connected, p) + } else { + others = append(others, p) } } - if len(peers) == 0 { - // we don't have any open connections, try any autonat peer that we know about - for p := range as.peers { - peers = append(peers, p) - } - } + shufflePeers(connected) - shufflePeers(peers) - - return peers + if len(connected) < 3 { + shufflePeers(others) + return append(connected, others...) + } else { + return connected + } } func shufflePeers(peers []peer.ID) { From 852f4e0e3e985779747d2b271b6b69e872f7ccd3 Mon Sep 17 00:00:00 2001 From: vyzo Date: Thu, 4 Oct 2018 16:57:02 +0300 Subject: [PATCH 45/51] adjust AutoNATRetryInterval in autonat tests --- autonat_test.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/autonat_test.go b/autonat_test.go index 570c4a2..ac438f8 100644 --- a/autonat_test.go +++ b/autonat_test.go @@ -30,6 +30,8 @@ func TestAutoNATPrivate(t *testing.T) { AutoNATBootDelay = 1 * time.Second save2 := AutoNATRefreshInterval AutoNATRefreshInterval = 1 * time.Second + save3 := AutoNATRetryInterval + AutoNATRetryInterval = 1 * time.Second hs, _ := makeAutoNATService(ctx, t) hc, an := makeAutoNAT(ctx, t) @@ -49,6 +51,7 @@ func TestAutoNATPrivate(t *testing.T) { AutoNATBootDelay = save1 AutoNATRefreshInterval = save2 + AutoNATRetryInterval = save3 } func TestAutoNATPublic(t *testing.T) { @@ -59,7 +62,9 @@ func TestAutoNATPublic(t *testing.T) { AutoNATBootDelay = 1 * time.Second save2 := AutoNATRefreshInterval AutoNATRefreshInterval = 1 * time.Second - save3 := private4 + save3 := AutoNATRetryInterval + AutoNATRetryInterval = 1 * time.Second + save4 := private4 private4 = []*net.IPNet{} hs, _ := makeAutoNATService(ctx, t) @@ -80,5 +85,6 @@ func TestAutoNATPublic(t *testing.T) { AutoNATBootDelay = save1 AutoNATRefreshInterval = save2 - private4 = save3 + AutoNATRetryInterval = save3 + private4 = save4 } From 9c8ee52a7de167da1ba67b3e3572b8b5ba7e648f Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 12 Oct 2018 18:06:04 +0300 Subject: [PATCH 46/51] increase identify delay to 500ms --- notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notify.go b/notify.go index 30ad9cd..63ed443 100644 --- a/notify.go +++ b/notify.go @@ -19,7 +19,7 @@ func (as *AmbientAutoNAT) Connected(net inet.Network, c inet.Conn) { go func() { // add some delay for identify - time.Sleep(250 * time.Millisecond) + time.Sleep(500 * time.Millisecond) protos, err := as.host.Peerstore().SupportsProtocols(p, AutoNATProto) if err != nil { From b2c65b06cfb0941e5504d6f660b084ee0b136e81 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 12 Oct 2018 18:07:41 +0300 Subject: [PATCH 47/51] jenkins file --- ci/Jenkinsfile | 1 + 1 file changed, 1 insertion(+) create mode 100644 ci/Jenkinsfile diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile new file mode 100644 index 0000000..b2067e6 --- /dev/null +++ b/ci/Jenkinsfile @@ -0,0 +1 @@ +golang() From 8d2e2ae97697317f6d0eb32c2e7b8069fad05f17 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 12 Oct 2018 18:11:22 +0300 Subject: [PATCH 48/51] add README --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..57ef719 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# go-libp2p-discovery + +[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io) +[![](https://img.shields.io/badge/project-libp2p-blue.svg?style=flat-square)](http://libp2p.io/) +[![](https://img.shields.io/badge/freenode-%23ipfs-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs) +[![standard-readme compliant](https://img.shields.io/badge/standard--readme-OK-green.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme) + +> Ambient NAT discovery + +This package provides an ambient NAT autodiscovery service. +It allows peers to figure out their NAT dialability situation by using test dial backs through peers providing the AutoNAT service. + +## Documenation + +See https://godoc.org/github.com/libp2p/go-libp2p-discovery. + +## Contribute + +Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/go-libp2p-discovery/issues)! + +This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). + +## License + +MIT From 9ef3734a5b08774ed43cfaea955a8f18ccae74d7 Mon Sep 17 00:00:00 2001 From: vyzo Date: Fri, 12 Oct 2018 19:22:59 +0300 Subject: [PATCH 49/51] reduce depgraph to just go-libp2p, update to 6.0.19 --- package.json | 45 ++------------------------------------------- 1 file changed, 2 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 9154209..dc59c8b 100644 --- a/package.json +++ b/package.json @@ -7,50 +7,9 @@ "gxDependencies": [ { "author": "whyrusleeping", - "hash": "QmfH9FKYv3Jp1xiyL8sPchGBUBg6JA6XviwajAo3qgnT3B", - "name": "go-libp2p-host", - "version": "3.0.8" - }, - { - "author": "whyrusleeping", - "hash": "QmQsErDt8Qgw1XrsXf2BpEzDgGWtB1YLsTAARBup5b6B9W", - "name": "go-libp2p-peer", - "version": "2.3.7" - }, - { - "author": "whyrusleeping", - "hash": "QmeKD8YT7887Xu6Z86iZmpYNxrLogJexqxEugSmaf14k64", - "name": "go-libp2p-peerstore", - "version": "1.4.24" - }, - { - "author": "whyrusleeping", - "hash": "QmQSbtGXCyNrj34LWL8EgXyNNYDZ8r3SwQcpW5pPxVhLnM", - "name": "go-libp2p-net", - "version": "3.0.8" - }, - { - "author": "multiformats", - "hash": "QmYmsdtJ3HsodkePE3eU3TsCaP2YvPZJ4LoXnNkDE5Tpt7", - "name": "go-multiaddr", - "version": "1.3.0" - }, - { - "author": "whyrusleeping", - "hash": "QmdxUuburamoF6zF9qjeQC4WYcWGbWuRmdLacMEsW8ioD8", - "name": "gogo-protobuf", - "version": "0.0.0" - }, - { - "author": "whyrusleeping", - "hash": "Qmf1u2efhjXYtuyP8SMHYtw4dCkbghnniex2PSp7baA7FP", + "hash": "QmPL3AKtiaQyYpchZceXBZhZ3MSnoGqJvLZrc7fzDTTQdJ", "name": "go-libp2p", - "version": "6.0.11" - }, - { - "hash": "QmRREK2CAZ5Re2Bd9zZFG6FeYDppUWt5cMgsoUEp3ktgSr", - "name": "go-log", - "version": "1.5.5" + "version": "6.0.19" } ], "gxVersion": "0.12.1", From 6a3a9cbab7396dbca42e9ef214435e3a6fab3af6 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 12:12:10 +0300 Subject: [PATCH 50/51] Add configurable Identify delay, with default value of 5 secs --- autonat_test.go | 31 +++++++++---------------------- notify.go | 4 +++- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/autonat_test.go b/autonat_test.go index ac438f8..6d31d46 100644 --- a/autonat_test.go +++ b/autonat_test.go @@ -10,6 +10,13 @@ import ( host "github.com/libp2p/go-libp2p-host" ) +func init() { + AutoNATBootDelay = 1 * time.Second + AutoNATRefreshInterval = 1 * time.Second + AutoNATRetryInterval = 1 * time.Second + AutoNATIdentifyDelay = 100 * time.Millisecond +} + func makeAutoNAT(ctx context.Context, t *testing.T) (host.Host, AutoNAT) { h, err := libp2p.New(ctx, libp2p.ListenAddrStrings("/ip4/127.0.0.1/tcp/0")) if err != nil { @@ -26,13 +33,6 @@ func TestAutoNATPrivate(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - save1 := AutoNATBootDelay - AutoNATBootDelay = 1 * time.Second - save2 := AutoNATRefreshInterval - AutoNATRefreshInterval = 1 * time.Second - save3 := AutoNATRetryInterval - AutoNATRetryInterval = 1 * time.Second - hs, _ := makeAutoNATService(ctx, t) hc, an := makeAutoNAT(ctx, t) @@ -48,23 +48,13 @@ func TestAutoNATPrivate(t *testing.T) { if status != NATStatusPrivate { t.Fatalf("unexpected NAT status: %d", status) } - - AutoNATBootDelay = save1 - AutoNATRefreshInterval = save2 - AutoNATRetryInterval = save3 } func TestAutoNATPublic(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - save1 := AutoNATBootDelay - AutoNATBootDelay = 1 * time.Second - save2 := AutoNATRefreshInterval - AutoNATRefreshInterval = 1 * time.Second - save3 := AutoNATRetryInterval - AutoNATRetryInterval = 1 * time.Second - save4 := private4 + save := private4 private4 = []*net.IPNet{} hs, _ := makeAutoNATService(ctx, t) @@ -83,8 +73,5 @@ func TestAutoNATPublic(t *testing.T) { t.Fatalf("unexpected NAT status: %d", status) } - AutoNATBootDelay = save1 - AutoNATRefreshInterval = save2 - AutoNATRetryInterval = save3 - private4 = save4 + private4 = save } diff --git a/notify.go b/notify.go index 63ed443..ebe709b 100644 --- a/notify.go +++ b/notify.go @@ -9,6 +9,8 @@ import ( var _ inet.Notifiee = (*AmbientAutoNAT)(nil) +var AutoNATIdentifyDelay = 5 * time.Second + func (as *AmbientAutoNAT) Listen(net inet.Network, a ma.Multiaddr) {} func (as *AmbientAutoNAT) ListenClose(net inet.Network, a ma.Multiaddr) {} func (as *AmbientAutoNAT) OpenedStream(net inet.Network, s inet.Stream) {} @@ -19,7 +21,7 @@ func (as *AmbientAutoNAT) Connected(net inet.Network, c inet.Conn) { go func() { // add some delay for identify - time.Sleep(500 * time.Millisecond) + time.Sleep(AutoNATIdentifyDelay) protos, err := as.host.Peerstore().SupportsProtocols(p, AutoNATProto) if err != nil { From 67bccae5b1f06f47c773ea4ea2c5e03ad6f50695 Mon Sep 17 00:00:00 2001 From: vyzo Date: Tue, 16 Oct 2018 12:48:28 +0300 Subject: [PATCH 51/51] add docstring for confidence --- autonat.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/autonat.go b/autonat.go index 4f766ce..1eace01 100644 --- a/autonat.go +++ b/autonat.go @@ -48,10 +48,15 @@ type AmbientAutoNAT struct { ctx context.Context host host.Host - mx sync.Mutex - peers map[peer.ID]struct{} - status NATStatus - addr ma.Multiaddr + mx sync.Mutex + peers map[peer.ID]struct{} + status NATStatus + addr ma.Multiaddr + // Reflects the confidence on of the NATStatus being private, as a single + // dialback may fail for reasons unrelated to NAT. + // If it is <3, then multiple autoNAT peers may be contacted for dialback + // If only a single autoNAT peer is known, then the confidence increases + // for each failure until it reaches 3. confidence int }