github.com/status-im/status-go@v1.1.0/server/ips.go (about)

     1  package server
     2  
     3  import (
     4  	"net"
     5  
     6  	"go.uber.org/zap"
     7  
     8  	"github.com/status-im/status-go/common"
     9  	"github.com/status-im/status-go/logutils"
    10  )
    11  
    12  var (
    13  	LocalHostIP = net.IP{127, 0, 0, 1}
    14  	Localhost   = "Localhost"
    15  )
    16  
    17  func GetOutboundIP() (net.IP, error) {
    18  	conn, err := net.Dial("udp", "255.255.255.255:8080")
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  	defer conn.Close()
    23  
    24  	localAddr := conn.LocalAddr().(*net.UDPAddr)
    25  
    26  	return localAddr.IP, nil
    27  }
    28  
    29  // addrToIPNet casts addr to IPNet.
    30  // Returns nil if addr is not of IPNet type.
    31  func addrToIPNet(addr net.Addr) *net.IPNet {
    32  	switch v := addr.(type) {
    33  	case *net.IPNet:
    34  		return v
    35  	default:
    36  		return nil
    37  	}
    38  }
    39  
    40  // filterAddressesForPairingServer filters private unicast addresses.
    41  // ips is a 2-dimensional array, where each sub-array is a list of IP
    42  // addresses for a single network interface.
    43  func filterAddressesForPairingServer(ips [][]net.IP) []net.IP {
    44  	var result = map[string]net.IP{}
    45  
    46  	for _, niIps := range ips {
    47  		var ipv4, ipv6 []net.IP
    48  
    49  		for _, ip := range niIps {
    50  
    51  			// Only take private global unicast addrs
    52  			if !ip.IsGlobalUnicast() || !ip.IsPrivate() {
    53  				continue
    54  			}
    55  
    56  			if v := ip.To4(); v != nil {
    57  				ipv4 = append(ipv4, ip)
    58  			} else {
    59  				ipv6 = append(ipv6, ip)
    60  			}
    61  		}
    62  
    63  		// Prefer IPv4 over IPv6 for shorter connection string
    64  		if len(ipv4) == 0 {
    65  			for _, ip := range ipv6 {
    66  				result[ip.String()] = ip
    67  			}
    68  		} else {
    69  			for _, ip := range ipv4 {
    70  				result[ip.String()] = ip
    71  			}
    72  		}
    73  	}
    74  
    75  	var out []net.IP
    76  	for _, v := range result {
    77  		out = append(out, v)
    78  	}
    79  
    80  	return out
    81  }
    82  
    83  // getAndroidLocalIP uses the net dial default ip as the standard Android IP address
    84  // patches https://github.com/status-im/status-mobile/issues/17156
    85  // more work required for a more robust implementation, see https://github.com/wlynxg/anet
    86  func getAndroidLocalIP() ([][]net.IP, error) {
    87  	ip, err := GetOutboundIP()
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  
    92  	return [][]net.IP{{ip}}, nil
    93  }
    94  
    95  // getLocalAddresses returns an array of all addresses
    96  // of all available network interfaces.
    97  func getLocalAddresses() ([][]net.IP, error) {
    98  	// TODO until we can resolve Android errors when calling net.Interfaces() just return the outbound local address.
    99  	//  Sorry Android
   100  	if common.OperatingSystemIs(common.AndroidPlatform) {
   101  		return getAndroidLocalIP()
   102  	}
   103  
   104  	nis, err := net.Interfaces()
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  
   109  	var ips [][]net.IP
   110  
   111  	for _, ni := range nis {
   112  		var niIps []net.IP
   113  
   114  		addrs, err := ni.Addrs()
   115  		if err != nil {
   116  			logutils.ZapLogger().Warn("failed to get addresses of network interface",
   117  				zap.String("networkInterface", ni.Name),
   118  				zap.Error(err))
   119  			continue
   120  		}
   121  
   122  		for _, addr := range addrs {
   123  			var ip net.IP
   124  			if ipNet := addrToIPNet(addr); ipNet == nil {
   125  				continue
   126  			} else {
   127  				ip = ipNet.IP
   128  			}
   129  			niIps = append(niIps, ip)
   130  		}
   131  
   132  		if len(niIps) > 0 {
   133  			ips = append(ips, niIps)
   134  		}
   135  	}
   136  
   137  	return ips, nil
   138  }
   139  
   140  // GetLocalAddressesForPairingServer is a high-level func
   141  // that returns a list of addresses to be used by local pairing server.
   142  func GetLocalAddressesForPairingServer() ([]net.IP, error) {
   143  	ips, err := getLocalAddresses()
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  	return filterAddressesForPairingServer(ips), nil
   148  }
   149  
   150  // findReachableAddresses returns a filtered remoteIps array,
   151  // in which each IP matches one or more of given localNets.
   152  func findReachableAddresses(remoteIPs []net.IP, localNets []net.IPNet) []net.IP {
   153  	var result []net.IP
   154  	for _, localNet := range localNets {
   155  		for _, remoteIP := range remoteIPs {
   156  			if localNet.Contains(remoteIP) {
   157  				result = append(result, remoteIP)
   158  			}
   159  		}
   160  	}
   161  	return result
   162  }
   163  
   164  // getAllAvailableNetworks collects all networks
   165  // from available network interfaces.
   166  func getAllAvailableNetworks() ([]net.IPNet, error) {
   167  	var localNets []net.IPNet
   168  
   169  	nis, err := net.Interfaces()
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	for _, ni := range nis {
   175  		addrs, err := ni.Addrs()
   176  		if err != nil {
   177  			logutils.ZapLogger().Warn("failed to get addresses of network interface",
   178  				zap.String("networkInterface", ni.Name),
   179  				zap.Error(err))
   180  			continue
   181  		}
   182  
   183  		for _, localAddr := range addrs {
   184  			localNets = append(localNets, *addrToIPNet(localAddr))
   185  		}
   186  	}
   187  	return localNets, nil
   188  }
   189  
   190  // FindReachableAddressesForPairingClient is a high-level func
   191  // that returns a reachable server's address to be used by local pairing client.
   192  func FindReachableAddressesForPairingClient(serverIps []net.IP) ([]net.IP, error) {
   193  	// TODO until we can resolve Android errors when calling net.Interfaces() just noop. Sorry Android
   194  	if common.OperatingSystemIs(common.AndroidPlatform) {
   195  		return serverIps, nil
   196  	}
   197  
   198  	nets, err := getAllAvailableNetworks()
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	return findReachableAddresses(serverIps, nets), nil
   203  }