mirror of
https://github.com/FlipsideCrypto/dcrd.git
synced 2026-02-06 10:56:47 +00:00
multi: add automatic network address discovery.
This discovers the network address(es) of the daemon through connected outbound peers. The address(es) discovered are advertised to subsequent connecting peers.
This commit is contained in:
parent
25c14e046a
commit
77a14a9ead
@ -1013,19 +1013,44 @@ func (a *AddrManager) AddLocalAddress(na *wire.NetAddress, priority AddressPrior
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasLocalAddress asserts if the manager has the provided local address.
|
||||
func (a *AddrManager) HasLocalAddress(na *wire.NetAddress) bool {
|
||||
key := NetAddressKey(na)
|
||||
a.lamtx.Lock()
|
||||
_, ok := a.localAddresses[key]
|
||||
a.lamtx.Unlock()
|
||||
return ok
|
||||
}
|
||||
|
||||
const (
|
||||
// Unreachable represents a publicly unreachable connection state
|
||||
// between two addresses.
|
||||
Unreachable = 0
|
||||
|
||||
// Default represents the default connection state between
|
||||
// two addresses.
|
||||
Default = iota
|
||||
|
||||
// Teredo represents a connection state between two RFC4380 addresses.
|
||||
Teredo
|
||||
|
||||
// Ipv6Weak represents a weak IPV6 connection state between two
|
||||
// addresses.
|
||||
Ipv6Weak
|
||||
|
||||
// Ipv4 represents an IPV4 connection state between two addreses.
|
||||
Ipv4
|
||||
|
||||
// Ipv6Strong represents a connection state between two IPV6 addresses.
|
||||
Ipv6Strong
|
||||
|
||||
// Private reprsents a connection state connect between two Tor addresses.
|
||||
Private
|
||||
)
|
||||
|
||||
// getReachabilityFrom returns the relative reachability of the provided local
|
||||
// address to the provided remote address.
|
||||
func getReachabilityFrom(localAddr, remoteAddr *wire.NetAddress) int {
|
||||
const (
|
||||
Unreachable = 0
|
||||
Default = iota
|
||||
Teredo
|
||||
Ipv6Weak
|
||||
Ipv4
|
||||
Ipv6Strong
|
||||
Private
|
||||
)
|
||||
|
||||
if !IsRoutable(remoteAddr) {
|
||||
return Unreachable
|
||||
}
|
||||
@ -1130,6 +1155,15 @@ func (a *AddrManager) GetBestLocalAddress(remoteAddr *wire.NetAddress) *wire.Net
|
||||
return bestAddress
|
||||
}
|
||||
|
||||
// IsPeerNaValid asserts if the the provided local address is routable
|
||||
// and reachable from the peer that suggested it.
|
||||
func (a *AddrManager) IsPeerNaValid(localAddr, remoteAddr *wire.NetAddress) bool {
|
||||
net := getNetwork(localAddr)
|
||||
reach := getReachabilityFrom(localAddr, remoteAddr)
|
||||
return (net == IPv4Address && reach == Ipv4) || (net == IPv6Address &&
|
||||
(reach == Ipv6Weak || reach == Ipv6Strong || reach == Teredo))
|
||||
}
|
||||
|
||||
// New returns a new Decred address manager.
|
||||
// Use Start to begin processing asynchronous address updates.
|
||||
// The address manager uses lookupFunc for necessary DNS lookups.
|
||||
|
||||
@ -118,6 +118,33 @@ func isOnionCatTor(na *wire.NetAddress) bool {
|
||||
return onionCatNet.Contains(na.IP)
|
||||
}
|
||||
|
||||
// NetworkAddress type is used to classify a network address.
|
||||
type NetworkAddress int
|
||||
|
||||
const (
|
||||
LocalAddress NetworkAddress = iota
|
||||
IPv4Address
|
||||
IPv6Address
|
||||
OnionAddress
|
||||
)
|
||||
|
||||
// getNetwork returns the network address type of the provided network address.
|
||||
func getNetwork(na *wire.NetAddress) NetworkAddress {
|
||||
switch {
|
||||
case isLocal(na):
|
||||
return LocalAddress
|
||||
|
||||
case isIPv4(na):
|
||||
return IPv4Address
|
||||
|
||||
case isOnionCatTor(na):
|
||||
return OnionAddress
|
||||
|
||||
default:
|
||||
return IPv6Address
|
||||
}
|
||||
}
|
||||
|
||||
// isRFC1918 returns whether or not the passed address is part of the IPv4
|
||||
// private network address space as defined by RFC1918 (10.0.0.0/8,
|
||||
// 172.16.0.0/12, or 192.168.0.0/16).
|
||||
|
||||
@ -129,6 +129,7 @@ type config struct {
|
||||
OnionProxyUser string `long:"onionuser" description:"Username for onion proxy server"`
|
||||
OnionProxyPass string `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"`
|
||||
NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"`
|
||||
NoDiscoverIP bool `long:"nodiscoverip" description:"Disable automatic network address discovery"`
|
||||
TorIsolation bool `long:"torisolation" description:"Enable Tor stream isolation by randomizing user credentials for each connection."`
|
||||
TestNet bool `long:"testnet" description:"Use the test network"`
|
||||
SimNet bool `long:"simnet" description:"Use the simulation test network"`
|
||||
|
||||
121
server.go
121
server.go
@ -129,6 +129,11 @@ type peerState struct {
|
||||
persistentPeers map[int32]*serverPeer
|
||||
banned map[string]time.Time
|
||||
outboundGroups map[string]int
|
||||
|
||||
// suggestions represents public network address suggestions from outbound
|
||||
// peers.
|
||||
suggestions map[addrmgr.NetworkAddress]map[string]int32
|
||||
suggestionsMtx sync.Mutex
|
||||
}
|
||||
|
||||
// ConnectionsWithIP returns the number of connections with the given IP.
|
||||
@ -178,6 +183,46 @@ func (ps *peerState) forAllPeers(closure func(sp *serverPeer)) {
|
||||
ps.forAllOutboundPeers(closure)
|
||||
}
|
||||
|
||||
// ResolveLocalAddress picks the best suggested network address from available
|
||||
// options, per the network interface key provided. The best suggestion, if
|
||||
// found, is added as a local address.
|
||||
func (ps *peerState) ResolveLocalAddress(netKey addrmgr.NetworkAddress, addrMgr *addrmgr.AddrManager, services wire.ServiceFlag, port uint16) {
|
||||
ps.suggestionsMtx.Lock()
|
||||
count := len(ps.suggestions[netKey])
|
||||
if count == 0 {
|
||||
ps.suggestionsMtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
var bestSuggestion string
|
||||
var bestTally int32
|
||||
for suggestion, tally := range ps.suggestions[netKey] {
|
||||
switch {
|
||||
case bestSuggestion == "", tally > bestTally:
|
||||
bestSuggestion = suggestion
|
||||
bestTally = tally
|
||||
}
|
||||
}
|
||||
ps.suggestionsMtx.Unlock()
|
||||
|
||||
// A valid best address suggestion must have at least two outbound peers
|
||||
// concluding on the same result.
|
||||
if bestTally < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
na, err := addrMgr.HostToNetAddress(bestSuggestion, port, services)
|
||||
if err != nil {
|
||||
amgrLog.Errorf("unable to generate network address using host %v: "+
|
||||
"%v", bestSuggestion, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !addrMgr.HasLocalAddress(na) {
|
||||
addrMgr.AddLocalAddress(na, addrmgr.ManualPrio)
|
||||
}
|
||||
}
|
||||
|
||||
// server provides a Decred server for handling communications to and from
|
||||
// Decred peers.
|
||||
type server struct {
|
||||
@ -254,6 +299,10 @@ type serverPeer struct {
|
||||
// The following chans are used to sync blockmanager and server.
|
||||
txProcessed chan struct{}
|
||||
blockProcessed chan struct{}
|
||||
|
||||
// peerNa is network address of the peer connected to.
|
||||
peerNa *wire.NetAddress
|
||||
peerNaMtx sync.Mutex
|
||||
}
|
||||
|
||||
// newServerPeer returns a new serverPeer instance. The peer needs to be set by
|
||||
@ -442,6 +491,12 @@ func (sp *serverPeer) OnVersion(p *peer.Peer, msg *wire.MsgVersion) *wire.MsgRej
|
||||
addrManager.Good(remoteAddr)
|
||||
}
|
||||
|
||||
if !sp.Inbound() {
|
||||
sp.peerNaMtx.Lock()
|
||||
sp.peerNa = &msg.AddrYou
|
||||
sp.peerNaMtx.Unlock()
|
||||
}
|
||||
|
||||
// Choose whether or not to relay transactions.
|
||||
sp.setDisableRelayTx(msg.DisableRelayTx)
|
||||
|
||||
@ -1361,6 +1416,68 @@ func (s *server) handleAddPeerMsg(state *peerState, sp *serverPeer) bool {
|
||||
} else {
|
||||
state.outboundPeers[sp.ID()] = sp
|
||||
}
|
||||
|
||||
// Fetch the suggested public ip from the outbound peer if
|
||||
// there are no prevailing conditions to disable automatic
|
||||
// network address discovery.
|
||||
//
|
||||
// The conditions to disable automatic network address
|
||||
// discovery are:
|
||||
// - If there is a proxy set (--proxy, --onion).
|
||||
// - If automatic network address discovery is explicitly
|
||||
// disabled (--nodiscoverip).
|
||||
// - If there is an external ip explicitly set (--externalip).
|
||||
// - If listening has been disabled (--nolisten, listen
|
||||
// disabled because of --connect, etc).
|
||||
// - If Universal Plug and Play is enabled (--upnp).
|
||||
// - If the active network is simnet or regnet.
|
||||
if (cfg.Proxy != "" || cfg.OnionProxy != "") ||
|
||||
cfg.NoDiscoverIP || len(cfg.ExternalIPs) > 0 ||
|
||||
(cfg.DisableListen || len(cfg.Listeners) == 0) || cfg.Upnp ||
|
||||
activeNetParams.Name == simNetParams.Name ||
|
||||
activeNetParams.Name == regNetParams.Name {
|
||||
return true
|
||||
}
|
||||
|
||||
sp.peerNaMtx.Lock()
|
||||
na := sp.peerNa
|
||||
sp.peerNaMtx.Unlock()
|
||||
|
||||
if na == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if !s.addrManager.IsPeerNaValid(na, sp.NA()) {
|
||||
return true
|
||||
}
|
||||
|
||||
port, err := strconv.ParseUint(activeNetParams.DefaultPort, 10, 16)
|
||||
if err != nil {
|
||||
srvrLog.Errorf("unabled to parse active network port: %v", err)
|
||||
return true
|
||||
}
|
||||
|
||||
id := na.IP.String()
|
||||
switch {
|
||||
case na.IP.To4() != nil:
|
||||
state.suggestionsMtx.Lock()
|
||||
state.suggestions[addrmgr.IPv4Address][id]++
|
||||
state.suggestionsMtx.Unlock()
|
||||
|
||||
state.ResolveLocalAddress(addrmgr.IPv4Address, s.addrManager,
|
||||
s.services, uint16(port))
|
||||
|
||||
case na.IP.To16() != nil:
|
||||
state.suggestionsMtx.Lock()
|
||||
state.suggestions[addrmgr.IPv6Address][id]++
|
||||
state.suggestionsMtx.Unlock()
|
||||
|
||||
state.ResolveLocalAddress(addrmgr.IPv6Address, s.addrManager,
|
||||
s.services, uint16(port))
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
@ -1769,6 +1886,10 @@ func (s *server) peerHandler() {
|
||||
outboundPeers: make(map[int32]*serverPeer),
|
||||
banned: make(map[string]time.Time),
|
||||
outboundGroups: make(map[string]int),
|
||||
suggestions: map[addrmgr.NetworkAddress]map[string]int32{
|
||||
addrmgr.IPv4Address: make(map[string]int32),
|
||||
addrmgr.IPv6Address: make(map[string]int32),
|
||||
},
|
||||
}
|
||||
|
||||
if !cfg.DisableDNSSeed {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user