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 }