diff --git a/discovery/rendezvous.go b/discovery/rendezvous.go index 0dfc424ab..2583669bf 100644 --- a/discovery/rendezvous.go +++ b/discovery/rendezvous.go @@ -104,7 +104,7 @@ func (r *Rendezvous) Stop() error { return nil } -func (r *Rendezvous) MakeRecord() (record enr.Record, err error) { +func (r *Rendezvous) MakeRecord(srv *ma.Multiaddr) (record enr.Record, err error) { r.recordMu.Lock() defer r.recordMu.Unlock() if r.record != nil { @@ -116,7 +116,27 @@ func (r *Rendezvous) MakeRecord() (record enr.Record, err error) { if r.identity == nil { return record, errIdentityIsNil } - record.Set(enr.IP(r.node.IP())) + + ip := r.node.IP() + if srv != nil && (ip.IsLoopback() || IsPrivate(ip)) { // If AdvertiseAddr is not specified, 127.0.0.1 might be returned + ctx, cancel := context.WithTimeout(r.rootCtx, requestTimeout) + defer cancel() + + ipAddr, err := r.client.RemoteIp(ctx, *srv) + if err != nil { + log.Error("could not obtain the external ip address", "err", err) + } else { + parsedIP := net.ParseIP(ipAddr) + if parsedIP != nil { + ip = parsedIP + log.Info("node's external IP address", "ipAddr", ipAddr) + } else { + log.Error("invalid ip address obtained from rendezvous server", "ipaddr", ipAddr, "err", err) + } + } + } + + record.Set(enr.IP(ip)) record.Set(enr.TCP(r.node.TCP())) record.Set(enr.UDP(r.node.UDP())) // public key is added to ENR when ENR is signed @@ -127,8 +147,7 @@ func (r *Rendezvous) MakeRecord() (record enr.Record, err error) { return record, nil } -func (r *Rendezvous) register(topic string, record enr.Record) error { - srv := r.servers[rand.Intn(len(r.servers))] // nolint: gosec +func (r *Rendezvous) register(srv ma.Multiaddr, topic string, record enr.Record) error { ctx, cancel := context.WithTimeout(r.rootCtx, requestTimeout) defer cancel() @@ -146,7 +165,8 @@ func (r *Rendezvous) register(topic string, record enr.Record) error { // Register renews registration in the specified server. func (r *Rendezvous) Register(topic string, stop chan struct{}) error { - record, err := r.MakeRecord() + srv := r.getRandomServer() + record, err := r.MakeRecord(&srv) if err != nil { return err } @@ -155,7 +175,7 @@ func (r *Rendezvous) Register(topic string, stop chan struct{}) error { ticker := time.NewTicker(r.registrationPeriod / 2) defer ticker.Stop() - if err := r.register(topic, record); err == context.Canceled { + if err := r.register(srv, topic, record); err == context.Canceled { return err } @@ -164,7 +184,7 @@ func (r *Rendezvous) Register(topic string, stop chan struct{}) error { case <-stop: return nil case <-ticker.C: - if err := r.register(topic, record); err == context.Canceled { + if err := r.register(r.getRandomServer(), topic, record); err == context.Canceled { return err } else if err == errDiscoveryIsStopped { return nil @@ -230,6 +250,10 @@ func (r *Rendezvous) Discover( } } +func (r *Rendezvous) getRandomServer() ma.Multiaddr { + return r.servers[rand.Intn(len(r.servers))] // nolint: gosec +} + func enrToNode(record enr.Record) (*discv5.Node, error) { var ( key enode.Secp256k1 @@ -253,3 +277,27 @@ func enrToNode(record enr.Record) (*discv5.Node, error) { _ = record.Load(&uport) return discv5.NewNode(nodeID, net.IP(ip), uint16(uport), uint16(tport)), nil } + +// IsPrivate reports whether ip is a private address, according to +// RFC 1918 (IPv4 addresses) and RFC 4193 (IPv6 addresses). +// Copied/Adapted from https://go-review.googlesource.com/c/go/+/272668/11/src/net/ip.go +// Copyright (c) The Go Authors. All rights reserved. +// @TODO: once Go 1.17 is released in Q42021, remove this function as it will become part of the language +func IsPrivate(ip net.IP) bool { + if ip4 := ip.To4(); ip4 != nil { + // Following RFC 4193, Section 3. Local IPv6 Unicast Addresses which says: + // The Internet Assigned Numbers Authority (IANA) has reserved the + // following three blocks of the IPv4 address space for private internets: + // 10.0.0.0 - 10.255.255.255 (10/8 prefix) + // 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) + // 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) + return ip4[0] == 10 || + (ip4[0] == 172 && ip4[1]&0xf0 == 16) || + (ip4[0] == 192 && ip4[1] == 168) + } + // Following RFC 4193, Section 3. Private Address Space which says: + // The Internet Assigned Numbers Authority (IANA) has reserved the + // following block of the IPv6 address space for local internets: + // FC00:: - FDFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF (FC00::/7 prefix) + return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc +} diff --git a/discovery/rendezvous_test.go b/discovery/rendezvous_test.go index 7251f72b1..43a6621bb 100644 --- a/discovery/rendezvous_test.go +++ b/discovery/rendezvous_test.go @@ -68,7 +68,7 @@ func TestMakeRecordReturnsCachedRecord(t *testing.T) { record := enr.Record{} require.NoError(t, enode.SignV4(&record, identity)) c := NewRendezvousWithENR(nil, record) - rst, err := c.MakeRecord() + rst, err := c.MakeRecord(nil) require.NoError(t, err) require.NotNil(t, enode.V4ID{}.NodeAddr(&rst)) require.Equal(t, enode.V4ID{}.NodeAddr(&record), enode.V4ID{}.NodeAddr(&rst)) @@ -79,7 +79,7 @@ func TestRendezvousRegisterAndDiscoverExitGracefully(t *testing.T) { require.NoError(t, err) require.NoError(t, r.Start()) require.NoError(t, r.Stop()) - require.EqualError(t, errDiscoveryIsStopped, r.register("", enr.Record{}).Error()) + require.EqualError(t, errDiscoveryIsStopped, r.register(r.getRandomServer(), "", enr.Record{}).Error()) _, err = r.discoverRequest(nil, "") require.EqualError(t, errDiscoveryIsStopped, err.Error()) } diff --git a/go.mod b/go.mod index e88a6ad7d..e8f91ec37 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/status-im/go-wakurelay-pubsub v0.4.3-0.20210729162817-adc68830282a github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf github.com/status-im/migrate/v4 v4.6.2-status.2 - github.com/status-im/rendezvous v1.3.2 + github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c github.com/status-im/status-go/extkeys v1.1.2 github.com/status-im/tcp-shaker v0.0.0-20191114194237-215893130501 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 79f3f1cae..37eec0d45 100644 --- a/go.sum +++ b/go.sum @@ -1113,8 +1113,8 @@ github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf h1:0w1mjNUqEHoL github.com/status-im/markdown v0.0.0-20201022101546-c0cbdd5763bf/go.mod h1:9yR8woqkJIHs3sf9pEjYvaGfmhsXR1leEMAX6+Z5y+M= github.com/status-im/migrate/v4 v4.6.2-status.2 h1:SdC+sMDl/aI7vUlwD2qj2p7KsK4T60IS9z4/rYCCbI8= github.com/status-im/migrate/v4 v4.6.2-status.2/go.mod h1:c/kc90n47GZu/58nnz1OMLTf7uE4Da4gZP5qmU+A/v8= -github.com/status-im/rendezvous v1.3.2 h1:eLTQ1EEg/qTsFDM6lwIf7tAFqiD3zEHnbWKaS3fvd/E= -github.com/status-im/rendezvous v1.3.2/go.mod h1:CK8B3kCbx3QrE0V64aAocU8oh9KRktoKSU0sqiF6MwI= +github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c h1:l83ciN0A5LW7c6NMa/j0x2mH1vsv3hbAAUTEwZw6qF4= +github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c/go.mod h1:CK8B3kCbx3QrE0V64aAocU8oh9KRktoKSU0sqiF6MwI= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088 h1:ClCAP2FPCvl8hGMhbUx/tq/sOu2wibztAa5jAvQEe4Q= github.com/status-im/resize v0.0.0-20201215164250-7c6d9f0d3088/go.mod h1:+92j1tN27DypDeBFxkg0uzkqfh1bNHTZe3Bv2PjvxpM= github.com/status-im/status-go/extkeys v1.1.2 h1:FSjARgDathJ3rIapJt851LsIXP9Oyuu2M2jPJKuzloU= diff --git a/params/config.go b/params/config.go index 3a28a6ae8..a6f516585 100644 --- a/params/config.go +++ b/params/config.go @@ -340,6 +340,9 @@ type NodeConfig struct { // AdvertiseAddr is a public IP address the node wants to be found with. // It is especially useful when using floating IPs attached to a server. + // This configuration value is used by rendezvous protocol, and it's optional + // If no value is specified, it will attempt to determine the node's external + // IP address. A value can be specified in case the returned address is incorrect AdvertiseAddr string // Name sets the instance name of the node. It must not contain the / character. diff --git a/vendor/github.com/status-im/rendezvous/client.go b/vendor/github.com/status-im/rendezvous/client.go index b6edb6e30..217d2ba87 100644 --- a/vendor/github.com/status-im/rendezvous/client.go +++ b/vendor/github.com/status-im/rendezvous/client.go @@ -115,12 +115,46 @@ func (c Client) Discover(ctx context.Context, srv ma.Multiaddr, topic string, li return val.Records, nil } +func (c Client) RemoteIp(ctx context.Context, srv ma.Multiaddr) (value string, err error) { + s, err := c.newStream(ctx, srv) + if err != nil { + return + } + defer s.Close() + + if err = rlp.Encode(s, protocol.REMOTEIP); err != nil { + return + } + + rs := rlp.NewStream(s, 0) + typ, err := rs.Uint() + if err != nil { + return + } + if protocol.MessageType(typ) != protocol.REMOTEIP_RESPONSE { + err = fmt.Errorf("expected %v as response, but got %v", protocol.REMOTEIP_RESPONSE, typ) + return + } + var val protocol.RemoteIpResponse + if err = rs.Decode(&val); err != nil { + return + } + if val.Status != protocol.OK { + err = fmt.Errorf("remoteip request failed. status code %v", val.Status) + return + } + logger.Debug("received response to remoteip request", "status", val.Status, "ip", val.IP) + value = val.IP + + return +} + func (c Client) newStream(ctx context.Context, srv ma.Multiaddr) (rw network.Stream, err error) { pid, err := srv.ValueForProtocol(ethv4.P_ETHv4) if err != nil { return } - peerid, err := peer.IDB58Decode(pid) + peerid, err := peer.Decode(pid) if err != nil { return } diff --git a/vendor/github.com/status-im/rendezvous/protocol/protocol.go b/vendor/github.com/status-im/rendezvous/protocol/protocol.go index 20560c7fd..1549622b4 100644 --- a/vendor/github.com/status-im/rendezvous/protocol/protocol.go +++ b/vendor/github.com/status-im/rendezvous/protocol/protocol.go @@ -14,6 +14,8 @@ const ( REGISTER_RESPONSE DISCOVER DISCOVER_RESPONSE + REMOTEIP + REMOTEIP_RESPONSE OK ResponseStatus = 0 E_INVALID_NAMESPACE ResponseStatus = 100 @@ -46,3 +48,11 @@ type DiscoverResponse struct { Message string Records []enr.Record } + +type RemoteIp struct { +} + +type RemoteIpResponse struct { + Status ResponseStatus + IP string +} diff --git a/vendor/github.com/status-im/rendezvous/server/server.go b/vendor/github.com/status-im/rendezvous/server/server.go index a392833c8..995f598e6 100644 --- a/vendor/github.com/status-im/rendezvous/server/server.go +++ b/vendor/github.com/status-im/rendezvous/server/server.go @@ -16,6 +16,7 @@ import ( "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/host" "github.com/libp2p/go-libp2p-core/network" + "github.com/multiformats/go-multiaddr" ma "github.com/multiformats/go-multiaddr" "github.com/status-im/rendezvous/protocol" ) @@ -132,7 +133,7 @@ func (srv *Server) startListener() error { return } s.SetReadDeadline(time.Now().Add(srv.readTimeout)) - resptype, resp, err := srv.msgParser(protocol.MessageType(typ), rs) + resptype, resp, err := srv.msgParser(s, protocol.MessageType(typ), rs) if err == io.EOF { return } @@ -198,7 +199,7 @@ type Decoder interface { Decode(val interface{}) error } -func (srv *Server) msgParser(typ protocol.MessageType, d Decoder) (resptype protocol.MessageType, resp interface{}, err error) { +func (srv *Server) msgParser(s network.Stream, typ protocol.MessageType, d Decoder) (resptype protocol.MessageType, resp interface{}, err error) { switch typ { case protocol.REGISTER: var msg protocol.Register @@ -229,6 +230,14 @@ func (srv *Server) msgParser(typ protocol.MessageType, d Decoder) (resptype prot metrics.ObserveDiscoveryDuration(time.Since(start).Seconds(), msg.Topic) metrics.ObserveDiscoverSize(float64(len(records)), msg.Topic) return resptype, protocol.DiscoverResponse{Status: protocol.OK, Records: records}, nil + case protocol.REMOTEIP: + resptype = protocol.REMOTEIP_RESPONSE + ip, err := s.Conn().RemoteMultiaddr().ValueForProtocol(multiaddr.P_IP4) + if err != nil { + metrics.CountError("remoteip") + return resptype, protocol.RemoteIpResponse{Status: protocol.E_INTERNAL_ERROR}, err + } + return resptype, protocol.RemoteIpResponse{Status: protocol.OK, IP: ip}, nil default: metrics.CountError("unknown") // don't send the response diff --git a/vendor/modules.txt b/vendor/modules.txt index 72baadbcc..e35f5b246 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -451,7 +451,7 @@ github.com/status-im/migrate/v4/database/postgres github.com/status-im/migrate/v4/database/sqlcipher github.com/status-im/migrate/v4/internal/url github.com/status-im/migrate/v4/source/go_bindata -# github.com/status-im/rendezvous v1.3.2 +# github.com/status-im/rendezvous v1.3.1-0.20210824184947-7c79c858170c github.com/status-im/rendezvous github.com/status-im/rendezvous/protocol github.com/status-im/rendezvous/server