github.com/dubbogo/gost@v1.14.0/net/net.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package gxnet
    19  
    20  import (
    21  	"log"
    22  	"net"
    23  	"strconv"
    24  	"strings"
    25  )
    26  
    27  import (
    28  	perrors "github.com/pkg/errors"
    29  )
    30  
    31  var privateBlocks []*net.IPNet
    32  
    33  const (
    34  	// Ipv4SplitCharacter use for slipt Ipv4
    35  	Ipv4SplitCharacter = "."
    36  	// Ipv6SplitCharacter use for slipt Ipv6
    37  	Ipv6SplitCharacter = ":"
    38  )
    39  
    40  func init() {
    41  	for _, b := range []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"} {
    42  		if _, block, err := net.ParseCIDR(b); err == nil {
    43  			privateBlocks = append(privateBlocks, block)
    44  		}
    45  	}
    46  }
    47  
    48  // GetLocalIP get local ip
    49  func GetLocalIP() (string, error) {
    50  	faces, err := net.Interfaces()
    51  	if err != nil {
    52  		return "", perrors.WithStack(err)
    53  	}
    54  
    55  	var addr net.IP
    56  	for _, face := range faces {
    57  		if !isValidNetworkInterface(face) {
    58  			continue
    59  		}
    60  
    61  		addrs, err := face.Addrs()
    62  		if err != nil {
    63  			return "", perrors.WithStack(err)
    64  		}
    65  
    66  		if ipv4, ok := getValidIPv4(addrs); ok {
    67  			addr = ipv4
    68  			if isPrivateIP(ipv4) {
    69  				return ipv4.String(), nil
    70  			}
    71  		}
    72  	}
    73  
    74  	if addr == nil {
    75  		return "", perrors.Errorf("can not get local IP")
    76  	}
    77  
    78  	return addr.String(), nil
    79  }
    80  
    81  func isPrivateIP(ip net.IP) bool {
    82  	for _, priv := range privateBlocks {
    83  		if priv.Contains(ip) {
    84  			return true
    85  		}
    86  	}
    87  	return false
    88  }
    89  
    90  func getValidIPv4(addrs []net.Addr) (net.IP, bool) {
    91  	for _, addr := range addrs {
    92  		var ip net.IP
    93  
    94  		switch v := addr.(type) {
    95  		case *net.IPNet:
    96  			ip = v.IP
    97  		case *net.IPAddr:
    98  			ip = v.IP
    99  		}
   100  
   101  		if ip == nil || ip.IsLoopback() {
   102  			continue
   103  		}
   104  
   105  		ip = ip.To4()
   106  		if ip == nil {
   107  			// not an valid ipv4 address
   108  			continue
   109  		}
   110  
   111  		return ip, true
   112  	}
   113  	return nil, false
   114  }
   115  
   116  func isValidNetworkInterface(face net.Interface) bool {
   117  	if face.Flags&net.FlagUp == 0 {
   118  		// interface down
   119  		return false
   120  	}
   121  
   122  	if face.Flags&net.FlagLoopback != 0 {
   123  		// loopback interface
   124  		return false
   125  	}
   126  
   127  	if strings.Contains(strings.ToLower(face.Name), "docker") {
   128  		return false
   129  	}
   130  
   131  	return true
   132  }
   133  
   134  // IsSameAddr refer from https://github.com/facebookarchive/grace/blob/master/gracenet/net.go#L180
   135  func IsSameAddr(addr1, addr2 net.Addr) bool {
   136  	if addr1.Network() != addr2.Network() {
   137  		return false
   138  	}
   139  
   140  	addr1s := addr1.String()
   141  	addr2s := addr2.String()
   142  	if addr1s == addr2s {
   143  		return true
   144  	}
   145  
   146  	// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
   147  	// scenario is common when listening on localhost.
   148  	const ipv6prefix = "[::]"
   149  	addr1s = strings.TrimPrefix(addr1s, ipv6prefix)
   150  	addr2s = strings.TrimPrefix(addr2s, ipv6prefix)
   151  	const ipv4prefix = "0.0.0.0"
   152  	addr1s = strings.TrimPrefix(addr1s, ipv4prefix)
   153  	addr2s = strings.TrimPrefix(addr2s, ipv4prefix)
   154  	return addr1s == addr2s
   155  }
   156  
   157  // ListenOnTCPRandomPort a tcp server listening on a random port by tcp protocol
   158  func ListenOnTCPRandomPort(ip string) (*net.TCPListener, error) {
   159  	localAddr := net.TCPAddr{
   160  		IP:   net.IPv4zero,
   161  		Port: 0,
   162  	}
   163  	if len(ip) > 0 {
   164  		localAddr.IP = net.ParseIP(ip)
   165  	}
   166  
   167  	// on some containers, u can not bind an random port by the following clause.
   168  	// listener, err := net.Listen("tcp", ":0")
   169  
   170  	return net.ListenTCP("tcp4", &localAddr)
   171  }
   172  
   173  // ListenOnUDPRandomPort an udp endpoint listening on a random port
   174  func ListenOnUDPRandomPort(ip string) (*net.UDPConn, error) {
   175  	localAddr := net.UDPAddr{
   176  		IP:   net.IPv4zero,
   177  		Port: 0,
   178  	}
   179  	if len(ip) > 0 {
   180  		localAddr.IP = net.ParseIP(ip)
   181  	}
   182  
   183  	return net.ListenUDP("udp4", &localAddr)
   184  }
   185  
   186  // MatchIP is used to determine whether @pattern and @host:@port match, It's supports subnet/range
   187  func MatchIP(pattern, host, port string) bool {
   188  	// if the pattern is subnet format, it will not be allowed to config port param in pattern.
   189  	if strings.Contains(pattern, "/") {
   190  		_, subnet, _ := net.ParseCIDR(pattern)
   191  		return subnet != nil && subnet.Contains(net.ParseIP(host))
   192  	}
   193  	return matchIPRange(pattern, host, port)
   194  }
   195  
   196  func matchIPRange(pattern, host, port string) bool {
   197  	if pattern == "" || host == "" {
   198  		log.Print("Illegal Argument pattern or hostName. Pattern:" + pattern + ", Host:" + host)
   199  		return false
   200  	}
   201  
   202  	pattern = strings.TrimSpace(pattern)
   203  	if "*.*.*.*" == pattern || "*" == pattern {
   204  		return true
   205  	}
   206  
   207  	isIpv4 := true
   208  	ip4 := net.ParseIP(host).To4()
   209  
   210  	if ip4 == nil {
   211  		isIpv4 = false
   212  	}
   213  
   214  	hostAndPort := getPatternHostAndPort(pattern, isIpv4)
   215  	if hostAndPort[1] != "" && hostAndPort[1] != port {
   216  		return false
   217  	}
   218  
   219  	pattern = hostAndPort[0]
   220  	splitCharacter := Ipv4SplitCharacter
   221  	if !isIpv4 {
   222  		splitCharacter = Ipv6SplitCharacter
   223  	}
   224  
   225  	mask := strings.Split(pattern, splitCharacter)
   226  	// check format of pattern
   227  	if err := checkHostPattern(pattern, mask, isIpv4); err != nil {
   228  		log.Printf("gost/net check host pattern error: %s", err.Error())
   229  		return false
   230  	}
   231  
   232  	if pattern == host {
   233  		return true
   234  	}
   235  
   236  	// short name condition
   237  	if !ipPatternContains(pattern) {
   238  		return pattern == host
   239  	}
   240  
   241  	ipAddress := strings.Split(host, splitCharacter)
   242  	for i := 0; i < len(mask); i++ {
   243  		if "*" == mask[i] || mask[i] == ipAddress[i] {
   244  			continue
   245  		} else if strings.Contains(mask[i], "-") {
   246  			rangeNumStrs := strings.Split(mask[i], "-")
   247  			if len(rangeNumStrs) != 2 {
   248  				log.Print("There is wrong format of ip Address: " + mask[i])
   249  				return false
   250  			}
   251  			min := getNumOfIPSegment(rangeNumStrs[0], isIpv4)
   252  			max := getNumOfIPSegment(rangeNumStrs[1], isIpv4)
   253  			ip := getNumOfIPSegment(ipAddress[i], isIpv4)
   254  			if ip < min || ip > max {
   255  				return false
   256  			}
   257  		} else if "0" == ipAddress[i] && "0" == mask[i] || "00" == mask[i] || "000" == mask[i] || "0000" == mask[i] {
   258  			continue
   259  		} else if mask[i] != ipAddress[i] {
   260  			return false
   261  		}
   262  	}
   263  	return true
   264  }
   265  
   266  func ipPatternContains(pattern string) bool {
   267  	return strings.Contains(pattern, "*") || strings.Contains(pattern, "-")
   268  }
   269  
   270  func checkHostPattern(pattern string, mask []string, isIpv4 bool) error {
   271  	if !isIpv4 {
   272  		if len(mask) != 8 && ipPatternContains(pattern) {
   273  			return perrors.New("If you config ip expression that contains '*' or '-', please fill qualified ip pattern like 234e:0:4567:0:0:0:3d:*. ")
   274  		}
   275  		if len(mask) != 8 && !strings.Contains(pattern, "::") {
   276  			return perrors.New("The host is ipv6, but the pattern is not ipv6 pattern : " + pattern)
   277  		}
   278  	} else {
   279  		if len(mask) != 4 {
   280  			return perrors.New("The host is ipv4, but the pattern is not ipv4 pattern : " + pattern)
   281  		}
   282  	}
   283  	return nil
   284  }
   285  
   286  func getPatternHostAndPort(pattern string, isIpv4 bool) []string {
   287  	result := make([]string, 2)
   288  	if strings.HasPrefix(pattern, "[") && strings.Contains(pattern, "]:") {
   289  		end := strings.Index(pattern, "]:")
   290  		result[0] = pattern[1:end]
   291  		result[1] = pattern[end+2:]
   292  	} else if strings.HasPrefix(pattern, "[") && strings.HasSuffix(pattern, "]") {
   293  		result[0] = pattern[1 : len(pattern)-1]
   294  		result[1] = ""
   295  	} else if isIpv4 && strings.Contains(pattern, ":") {
   296  		end := strings.Index(pattern, ":")
   297  		result[0] = pattern[:end]
   298  		result[1] = pattern[end+1:]
   299  	} else {
   300  		result[0] = pattern
   301  	}
   302  	return result
   303  }
   304  
   305  func getNumOfIPSegment(ipSegment string, isIpv4 bool) int {
   306  	if isIpv4 {
   307  		ipSeg, _ := strconv.Atoi(ipSegment)
   308  		return ipSeg
   309  	}
   310  	ipSeg, _ := strconv.ParseInt(ipSegment, 0, 16)
   311  	return int(ipSeg)
   312  }
   313  
   314  // HostAddress composes an ip:port style address. It's opposite function is net.SplitHostPort.
   315  func HostAddress(host string, port int) string {
   316  	return net.JoinHostPort(host, strconv.Itoa(port))
   317  }
   318  
   319  // WSHostAddress return a ws hostAddress
   320  func WSHostAddress(host string, port int, path string) string {
   321  	return "ws://" + net.JoinHostPort(host, strconv.Itoa(port)) + path
   322  }
   323  
   324  // WSSHostAddress return a wss hostAddress
   325  func WSSHostAddress(host string, port int, path string) string {
   326  	return "wss://" + net.JoinHostPort(host, strconv.Itoa(port)) + path
   327  }
   328  
   329  // HostAddress2 return a hostAddress
   330  func HostAddress2(host string, port string) string {
   331  	return net.JoinHostPort(host, port)
   332  }
   333  
   334  // WSHostAddress2 return a ws hostAddress
   335  func WSHostAddress2(host string, port string, path string) string {
   336  	return "ws://" + net.JoinHostPort(host, port) + path
   337  }
   338  
   339  // WSSHostAddress2 return a wss hostAddress
   340  func WSSHostAddress2(host string, port string, path string) string {
   341  	return "wss://" + net.JoinHostPort(host, port) + path
   342  }
   343  
   344  // HostPort return host, port, err
   345  func HostPort(addr string) (string, string, error) {
   346  	return net.SplitHostPort(addr)
   347  }