Skip to content

Commit

Permalink
contractor: fix ipv4 ipv6 check
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisSchinnerl committed Nov 14, 2024
1 parent b207f4c commit 0c04587
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 30 deletions.
48 changes: 33 additions & 15 deletions autopilot/contractor/hostset.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,42 +31,55 @@ func newHostSet(l *zap.SugaredLogger) *hostSet {
}
}

func (hs *hostSet) resolveHostIP(host api.Host) []string {
func (hs *hostSet) resolveHostIP(host api.Host) ([]string, error) {
resolvedAddresses := hs.resolvedAddresses[host.PublicKey]
if len(resolvedAddresses) > 0 {
return resolvedAddresses
return resolvedAddresses, nil
}
// resolve host IP
// NOTE: we ignore errors here since failing to resolve an address is either
// 1. not the host's faul, so we give it the benefit of the doubt
// 2. the host is unreachable of incorrectly announced, in which case the scans will fail
//
resolvedAddresses, _, _ = utils.ResolveHostIP(context.Background(), host.NetAddress)
for _, addr := range host.V2SiamuxAddresses {
v2Addr, _, _ := utils.ResolveHostIP(context.Background(), addr)
resolvedAddresses = append(resolvedAddresses, v2Addr...)
var hostAddrs []string
if host.NetAddress != "" {
hostAddrs = append(hostAddrs, host.NetAddress)
}
hostAddrs = append(hostAddrs, host.V2SiamuxAddresses...)
resolvedAddresses, _, err := utils.ResolveHostIPs(context.Background(), hostAddrs)
if err != nil {
return nil, err
}

// update cache
hs.resolvedAddresses[host.PublicKey] = resolvedAddresses
return resolvedAddresses
return resolvedAddresses, nil
}

func (hs *hostSet) HasRedundantIP(host api.Host) bool {
resolvedAddresses := hs.resolveHostIP(host)
logger := hs.logger.Named("hasRedundantIP").
With("hostKey", host.PublicKey).
With("netAddress", host.NetAddress).
With("v2SiamuxAddresses", host.V2SiamuxAddresses)

resolvedAddresses, err := hs.resolveHostIP(host)
if errors.Is(err, utils.ErrHostTooManyAddresses) {
logger.Errorf("host has more than 2 subnets, treating its IP %v as redundant", host.PublicKey, utils.ErrHostTooManyAddresses)
return true
} else if err != nil {
logger.With(zap.Error(err)).Error("failed to resolve host ip - treating it as redundant")
return true
}

subnets, err := utils.AddressesToSubnets(resolvedAddresses)
if err != nil {
hs.logger.Errorf("failed to parse host %v subnets: %v", host.PublicKey, err)
logger.With(zap.Error(err)).Errorf("failed to parse host subnets")
return true
}
// validate host subnets
if len(subnets) == 0 {
hs.logger.Errorf("host %v has no subnet, treating its IP %v as redundant", host.PublicKey, host.NetAddress)
return true
} else if len(subnets) > 2 {
hs.logger.Errorf("host %v has more than 2 subnets, treating its IP %v as redundant", host.PublicKey, errHostTooManySubnets)
return true
logger.Warnf("host has no subnets")
return false
}

// check if we know about this subnet
Expand All @@ -85,7 +98,12 @@ func (hs *hostSet) HasRedundantIP(host api.Host) bool {
}

func (hs *hostSet) Add(host api.Host) {
subnets, err := utils.AddressesToSubnets(hs.resolveHostIP(host))
addresses, err := hs.resolveHostIP(host)
if err != nil {
hs.logger.Errorf("failed to resolve host %v addresses: %v", host.PublicKey, err)
return
}
subnets, err := utils.AddressesToSubnets(addresses)
if err != nil {
hs.logger.Errorf("failed to parse host %v subnets: %v", host.PublicKey, err)
return
Expand Down
6 changes: 3 additions & 3 deletions autopilot/contractor/hostset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func TestHostSet(t *testing.T) {
NetAddress: "",
V2SiamuxAddresses: []string{},
}
if !hs.HasRedundantIP(host1) {
t.Fatalf("Expected host with no subnets to be considered redundant")
if hs.HasRedundantIP(host1) {
t.Fatalf("Expected host with no subnets to not be redundant")
}

// Host with more than 2 subnets
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestHostSet(t *testing.T) {
host5 := api.Host{
PublicKey: types.GeneratePrivateKey().PublicKey(),
NetAddress: "",
V2SiamuxAddresses: []string{"5.5.5.5:5555", "6.6.6.6:6666"},
V2SiamuxAddresses: []string{"5.5.5.5:5555", "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:5555"},
}
if hs.HasRedundantIP(host5) {
t.Fatal("Expected host with two valid subnets to not be considered redundant")
Expand Down
9 changes: 7 additions & 2 deletions bus/bus.go
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ func (b *Bus) renewContract(ctx context.Context, cs consensus.State, gp api.Goug
return newRevision, contractPrice, fundAmount, nil
}

func (b *Bus) scanHost(ctx context.Context, timeout time.Duration, hostKey types.PublicKey, hostIP string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) {
func (b *Bus) scanHost(ctx context.Context, timeout time.Duration, hostKey types.PublicKey, hostIP string, v3Addrs []string) (rhpv2.HostSettings, rhpv3.HostPriceTable, time.Duration, error) {
logger := b.logger.With("host", hostKey).With("hostIP", hostIP).With("timeout", timeout)

// prepare a helper to create a context for scanning
Expand Down Expand Up @@ -927,7 +927,12 @@ func (b *Bus) scanHost(ctx context.Context, timeout time.Duration, hostKey types

// resolve host ip, don't scan if the host is on a private network or if it
// resolves to more than two addresses of the same type
_, private, err := utils.ResolveHostIP(ctx, hostIP)
var hostIPs []string
if hostIP != "" {
hostIPs = append(hostIPs, hostIP)
}
hostIPs = append(hostIPs, v3Addrs...)
_, private, err := utils.ResolveHostIPs(ctx, hostIPs)
if errors.Is(err, utils.ErrHostTooManyAddresses) {
return rhpv2.HostSettings{}, rhpv3.HostPriceTable{}, 0, err
} else if private && !b.allowPrivateIPs {
Expand Down
2 changes: 1 addition & 1 deletion bus/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ func (b *Bus) hostsScanHandlerPOST(jc jape.Context) {

// scan host
var errStr string
settings, priceTable, elapsed, err := b.scanHost(jc.Request.Context(), time.Duration(rsr.Timeout), hk, h.NetAddress)
settings, priceTable, elapsed, err := b.scanHost(jc.Request.Context(), time.Duration(rsr.Timeout), hk, h.NetAddress, h.V2SiamuxAddresses)
if err != nil {
errStr = err.Error()
}
Expand Down
28 changes: 19 additions & 9 deletions internal/utils/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,29 @@ func AddressesToSubnets(resolvedAddresses []string) ([]string, error) {
return subnets, nil
}

func ResolveHostIP(ctx context.Context, hostIP string) (ips []string, private bool, _ error) {
// resolve host address
host, _, err := net.SplitHostPort(hostIP)
if err != nil {
return nil, false, err
func ResolveHostIPs(ctx context.Context, hostIPs []string) (ips []string, private bool, _ error) {
// resolve host addresses and deduplicate IPs
addrMap := make(map[string]net.IPAddr)
for _, hostIP := range hostIPs {
host, _, err := net.SplitHostPort(hostIP)
if err != nil {
return nil, false, err
}
ips, err := (&net.Resolver{}).LookupIPAddr(ctx, host)
if err != nil {
return nil, false, err
}
for _, ip := range ips {
addrMap[ip.String()] = ip
}
}
addrs, err := (&net.Resolver{}).LookupIPAddr(ctx, host)
if err != nil {
return nil, false, err
var addrs []net.IPAddr
for _, addr := range addrMap {
addrs = append(addrs, addr)
}

// filter out hosts associated with more than two addresses or two of the same type
if len(addrs) > 2 || (len(addrs) == 2) && (len(addrs[0].IP) == len(addrs[1].IP)) {
if len(addrs) > 2 || (len(addrs) == 2) && (len(addrs[0].IP.String()) == len(addrs[1].IP.String())) {
return nil, false, fmt.Errorf("%w: %+v", ErrHostTooManyAddresses, addrs)
}

Expand Down

0 comments on commit 0c04587

Please sign in to comment.