github.com/kaydxh/golang@v0.0.131/go/net/ip.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package net
    23  
    24  import (
    25  	"context"
    26  	"errors"
    27  	"fmt"
    28  	"net"
    29  	"os"
    30  	"strconv"
    31  
    32  	hash_ "github.com/kaydxh/golang/go/encoding/hash"
    33  	os_ "github.com/kaydxh/golang/go/os"
    34  )
    35  
    36  // This code is borrowed from https://github.com/uber/tchannel-go/blob/dev/localip.go
    37  
    38  // scoreAddr scores how likely the given addr is to be a remote address and returns the
    39  // IP to use when listening. Any address which receives a negative score should not be used.
    40  // Scores are calculated as:
    41  // -1 for any unknown IP addresses.
    42  // +300 for IPv4 addresses
    43  // +100 for non-local addresses, extra +100 for "up" interaces.
    44  func scoreAddr(iface net.Interface, addr net.Addr) (int, net.IP) {
    45  	var ip net.IP
    46  	if netAddr, ok := addr.(*net.IPNet); ok {
    47  		ip = netAddr.IP
    48  	} else if netIP, ok := addr.(*net.IPAddr); ok {
    49  		ip = netIP.IP
    50  	} else {
    51  		return -1, nil
    52  	}
    53  
    54  	var score int
    55  	if ip.To4() != nil {
    56  		score += 300
    57  	}
    58  	if iface.Flags&net.FlagLoopback == 0 && !ip.IsLoopback() {
    59  		score += 100
    60  		if iface.Flags&net.FlagUp != 0 {
    61  			score += 100
    62  		}
    63  	}
    64  
    65  	return score, ip
    66  }
    67  
    68  // HostIP tries to find an IP that can be used by other machines to reach this machine.
    69  func GetHostIP() (net.IP, error) {
    70  	interfaces, err := net.Interfaces()
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	bestScore := -1
    76  	var bestIP net.IP
    77  	// Select the highest scoring IP as the best IP.
    78  	for _, iface := range interfaces {
    79  		addrs, err := iface.Addrs()
    80  		if err != nil {
    81  			// Skip this interface if there is an error.
    82  			continue
    83  		}
    84  
    85  		for _, addr := range addrs {
    86  			score, ip := scoreAddr(iface, addr)
    87  			if score > bestScore {
    88  				bestScore = score
    89  				bestIP = ip
    90  			}
    91  		}
    92  	}
    93  
    94  	if bestScore == -1 {
    95  		return nil, errors.New("no addresses to listen on")
    96  	}
    97  
    98  	return bestIP, nil
    99  }
   100  
   101  func GetLocalFirstIP() (string, error) {
   102  	ips, err := GetLocalIPs()
   103  	if err != nil {
   104  		return "", err
   105  	}
   106  	if len(ips) == 0 {
   107  		return "", fmt.Errorf("no valid ip")
   108  	}
   109  
   110  	return ips[0], nil
   111  }
   112  
   113  func GetLocalIPs() ([]string, error) {
   114  	localAddrs, err := GetLocalAddrs()
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	var ips []string
   120  	for _, addr := range localAddrs {
   121  		if addr.IP.To4() != nil && !addr.IP.IsLoopback() {
   122  			ips = append(ips, addr.IP.String())
   123  		}
   124  
   125  	}
   126  
   127  	return ips, nil
   128  }
   129  
   130  func GetLocalAddrs() ([]*net.IPNet, error) {
   131  	var localAddrs []*net.IPNet
   132  	addrs, err := net.InterfaceAddrs()
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	for _, addr := range addrs {
   138  		ipNet, ok := addr.(*net.IPNet)
   139  		if ok {
   140  			localAddrs = append(localAddrs, ipNet)
   141  		}
   142  	}
   143  
   144  	return localAddrs, nil
   145  }
   146  
   147  // IsIPv4 returns if netIP is IPv4.
   148  func IsIPv4(netIP net.IP) bool {
   149  	return netIP != nil && netIP.To4() != nil
   150  }
   151  
   152  func IsIPv4String(ip string) bool {
   153  	netIP := ParseIP(ip)
   154  	return IsIPv4(netIP)
   155  }
   156  
   157  func LookupHostIPv4(host string) (addrs []string, err error) {
   158  	return LookupHostIPv4WithContext(context.Background(), host)
   159  }
   160  
   161  func LookupHostIPv4WithContext(ctx context.Context, host string) (addrs []string, err error) {
   162  	ips, err := net.DefaultResolver.LookupHost(ctx, host)
   163  	if err != nil {
   164  		return
   165  	}
   166  
   167  	for _, ip := range ips {
   168  		if IsIPv4String(ip) {
   169  			addrs = append(addrs, ip)
   170  		}
   171  	}
   172  
   173  	return addrs, err
   174  }
   175  
   176  // SplitHostIntPort split host and integral port
   177  func SplitHostIntPort(s string) (string, int, error) {
   178  	host, port, err := net.SplitHostPort(s)
   179  	if err != nil {
   180  		return "", 0, err
   181  	}
   182  	portInt, err := strconv.Atoi(port)
   183  	if err != nil {
   184  		return "", 0, err
   185  	}
   186  	return host, portInt, err
   187  }
   188  
   189  // parseTarget takes the user input target string and default port, returns formatted host and port info.
   190  // If target doesn't specify a port, set the port to be the defaultPort.
   191  // If target is in IPv6 format and host-name is enclosed in square brackets, brackets
   192  // are stripped when setting the host.
   193  // examples:
   194  // target: "www.google.com" defaultPort: "443" returns host: "www.google.com", port: "443"
   195  // target: "ipv4-host:80" defaultPort: "443" returns host: "ipv4-host", port: "80"
   196  // target: "[ipv6-host]" defaultPort: "443" returns host: "ipv6-host", port: "443"
   197  // target: ":80" defaultPort: "443" returns host: "localhost", port: "80"
   198  func ParseTarget(target, defaultPort string) (host, port string, err error) {
   199  	if target == "" {
   200  		return "", "", fmt.Errorf("target is empty")
   201  	}
   202  	if ip := net.ParseIP(target); ip != nil {
   203  		// target is an IPv4 or IPv6(without brackets) address
   204  		return target, defaultPort, nil
   205  	}
   206  	if host, port, err = net.SplitHostPort(target); err == nil {
   207  		if port == "" {
   208  			// If the port field is empty (target ends with colon), e.g. "[::1]:", this is an error.
   209  			return "", "", fmt.Errorf("missing port after port-separator colon")
   210  		}
   211  		// target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port
   212  		if host == "" {
   213  			// Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed.
   214  			host = "localhost"
   215  		}
   216  		return host, port, nil
   217  	}
   218  	if host, port, err = net.SplitHostPort(target + ":" + defaultPort); err == nil {
   219  		// target doesn't have port
   220  		return host, port, nil
   221  	}
   222  	return "", "", fmt.Errorf("invalid target address %v, error info: %v", target, err)
   223  }
   224  
   225  // 1 get k8s pod name
   226  // 2 get ip:pid
   227  func GetServerName() string {
   228  	serverName := fmt.Sprintf("%v:%v", os.Getenv("POD_NAMESPACE"), os.Getenv("POD_NAME"))
   229  	if serverName == ":" {
   230  		ip, _ := GetHostIP()
   231  		pid := os_.GetProcId()
   232  		return fmt.Sprintf("%s:%v", ip.String(), pid)
   233  	}
   234  	return serverName
   235  }
   236  
   237  func GetServerId() uint32 {
   238  	return hash_.HashCode(GetServerName())
   239  }