github.com/ethersphere/bee/v2@v2.2.0/pkg/p2p/libp2p/static_resolver.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package libp2p
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"net"
    11  	"strings"
    12  
    13  	libp2ppeer "github.com/libp2p/go-libp2p/core/peer"
    14  	ma "github.com/multiformats/go-multiaddr"
    15  )
    16  
    17  type staticAddressResolver struct {
    18  	multiProto string
    19  	port       string
    20  }
    21  
    22  func newStaticAddressResolver(addr string, lookupIP func(host string) ([]net.IP, error)) (*staticAddressResolver, error) {
    23  	host, port, err := net.SplitHostPort(addr)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  
    28  	var multiProto string
    29  	if host != "" {
    30  		multiProto, err = getMultiProto(host, lookupIP)
    31  		if err != nil {
    32  			return nil, err
    33  		}
    34  	}
    35  
    36  	return &staticAddressResolver{
    37  		multiProto: multiProto,
    38  		port:       port,
    39  	}, nil
    40  }
    41  
    42  func (r *staticAddressResolver) Resolve(observedAddress ma.Multiaddr) (ma.Multiaddr, error) {
    43  	observableAddrInfo, err := libp2ppeer.AddrInfoFromP2pAddr(observedAddress)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	if len(observableAddrInfo.Addrs) < 1 {
    49  		return nil, errors.New("invalid observed address")
    50  	}
    51  
    52  	observedAddrSplit := strings.Split(observableAddrInfo.Addrs[0].String(), "/")
    53  
    54  	// if address is not in a form of '/ipversion/ip/protocol/port/...` don't compare to addresses and return it
    55  	if len(observedAddrSplit) < 5 {
    56  		return observedAddress, nil
    57  	}
    58  
    59  	var multiProto string
    60  	if r.multiProto != "" {
    61  		multiProto = r.multiProto
    62  	} else {
    63  		multiProto = strings.Join(observedAddrSplit[:3], "/")
    64  	}
    65  
    66  	var port string
    67  	if r.port != "" {
    68  		port = r.port
    69  	} else {
    70  		port = observedAddrSplit[4]
    71  	}
    72  	a, err := ma.NewMultiaddr(multiProto + "/" + observedAddrSplit[3] + "/" + port)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	return buildUnderlayAddress(a, observableAddrInfo.ID)
    78  }
    79  
    80  func getMultiProto(host string, lookupIP func(host string) ([]net.IP, error)) (string, error) {
    81  	if host == "" {
    82  		return "", nil
    83  	}
    84  	ip := net.ParseIP(host)
    85  	if ip != nil {
    86  		if ip.To4() == nil {
    87  			return "/ip6/" + ip.String(), nil
    88  		}
    89  		return "/ip4/" + ip.String(), nil
    90  	}
    91  	ips, err := lookupIP(host)
    92  	if err != nil {
    93  		return "", fmt.Errorf("invalid IP or Domain Name %q", host)
    94  	}
    95  	ipv4, ipv6 := ipsClassifier(ips)
    96  	if ipv4 {
    97  		if ipv6 {
    98  			return "/dns/" + host, nil
    99  		}
   100  		return "/dns4/" + host, nil
   101  	}
   102  	return "/dns6/" + host, nil
   103  }
   104  
   105  func ipsClassifier(ips []net.IP) (ipv4, ipv6 bool) {
   106  	for _, ip := range ips {
   107  		if ip.To4() != nil {
   108  			ipv4 = true
   109  		} else {
   110  			ipv6 = true
   111  		}
   112  		if ipv4 && ipv6 {
   113  			return
   114  		}
   115  	}
   116  	return
   117  }