mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-30 18:35:18 +00:00
166 lines
3.5 KiB
Go
166 lines
3.5 KiB
Go
|
package autorelay
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
|
||
|
ma "github.com/multiformats/go-multiaddr"
|
||
|
manet "github.com/multiformats/go-multiaddr/net"
|
||
|
)
|
||
|
|
||
|
// This function cleans up a relay's address set to remove private addresses and curtail
|
||
|
// addrsplosion.
|
||
|
func cleanupAddressSet(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||
|
var public, private []ma.Multiaddr
|
||
|
|
||
|
for _, a := range addrs {
|
||
|
if isRelayAddr(a) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if manet.IsPublicAddr(a) || isDNSAddr(a) {
|
||
|
public = append(public, a)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// discard unroutable addrs
|
||
|
if manet.IsPrivateAddr(a) {
|
||
|
private = append(private, a)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !hasAddrsplosion(public) {
|
||
|
return public
|
||
|
}
|
||
|
|
||
|
return sanitizeAddrsplodedSet(public, private)
|
||
|
}
|
||
|
|
||
|
func isRelayAddr(a ma.Multiaddr) bool {
|
||
|
isRelay := false
|
||
|
|
||
|
ma.ForEach(a, func(c ma.Component) bool {
|
||
|
switch c.Protocol().Code {
|
||
|
case ma.P_CIRCUIT:
|
||
|
isRelay = true
|
||
|
return false
|
||
|
default:
|
||
|
return true
|
||
|
}
|
||
|
})
|
||
|
|
||
|
return isRelay
|
||
|
}
|
||
|
|
||
|
func isDNSAddr(a ma.Multiaddr) bool {
|
||
|
if first, _ := ma.SplitFirst(a); first != nil {
|
||
|
switch first.Protocol().Code {
|
||
|
case ma.P_DNS4, ma.P_DNS6, ma.P_DNSADDR:
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// we have addrsplosion if for some protocol we advertise multiple ports on
|
||
|
// the same base address.
|
||
|
func hasAddrsplosion(addrs []ma.Multiaddr) bool {
|
||
|
aset := make(map[string]int)
|
||
|
|
||
|
for _, a := range addrs {
|
||
|
key, port := addrKeyAndPort(a)
|
||
|
xport, ok := aset[key]
|
||
|
if ok && port != xport {
|
||
|
return true
|
||
|
}
|
||
|
aset[key] = port
|
||
|
}
|
||
|
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func addrKeyAndPort(a ma.Multiaddr) (string, int) {
|
||
|
var (
|
||
|
key string
|
||
|
port int
|
||
|
)
|
||
|
|
||
|
ma.ForEach(a, func(c ma.Component) bool {
|
||
|
switch c.Protocol().Code {
|
||
|
case ma.P_TCP, ma.P_UDP:
|
||
|
port = int(binary.BigEndian.Uint16(c.RawValue()))
|
||
|
key += "/" + c.Protocol().Name
|
||
|
default:
|
||
|
val := c.Value()
|
||
|
if val == "" {
|
||
|
val = c.Protocol().Name
|
||
|
}
|
||
|
key += "/" + val
|
||
|
}
|
||
|
return true
|
||
|
})
|
||
|
|
||
|
return key, port
|
||
|
}
|
||
|
|
||
|
// clean up addrsplosion
|
||
|
// the following heuristic is used:
|
||
|
// - for each base address/protocol combination, if there are multiple ports advertised then
|
||
|
// only accept the default port if present.
|
||
|
// - If the default port is not present, we check for non-standard ports by tracking
|
||
|
// private port bindings if present.
|
||
|
// - If there is no default or private port binding, then we can't infer the correct
|
||
|
// port and give up and return all addrs (for that base address)
|
||
|
func sanitizeAddrsplodedSet(public, private []ma.Multiaddr) []ma.Multiaddr {
|
||
|
type portAndAddr struct {
|
||
|
addr ma.Multiaddr
|
||
|
port int
|
||
|
}
|
||
|
|
||
|
privports := make(map[int]struct{})
|
||
|
pubaddrs := make(map[string][]portAndAddr)
|
||
|
|
||
|
for _, a := range private {
|
||
|
_, port := addrKeyAndPort(a)
|
||
|
privports[port] = struct{}{}
|
||
|
}
|
||
|
|
||
|
for _, a := range public {
|
||
|
key, port := addrKeyAndPort(a)
|
||
|
pubaddrs[key] = append(pubaddrs[key], portAndAddr{addr: a, port: port})
|
||
|
}
|
||
|
|
||
|
var result []ma.Multiaddr
|
||
|
for _, pas := range pubaddrs {
|
||
|
if len(pas) == 1 {
|
||
|
// it's not addrsploded
|
||
|
result = append(result, pas[0].addr)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
haveAddr := false
|
||
|
for _, pa := range pas {
|
||
|
if _, ok := privports[pa.port]; ok {
|
||
|
// it matches a privately bound port, use it
|
||
|
result = append(result, pa.addr)
|
||
|
haveAddr = true
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if pa.port == 4001 || pa.port == 4002 {
|
||
|
// it's a default port, use it
|
||
|
result = append(result, pa.addr)
|
||
|
haveAddr = true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !haveAddr {
|
||
|
// we weren't able to select a port; bite the bullet and use them all
|
||
|
for _, pa := range pas {
|
||
|
result = append(result, pa.addr)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
}
|