github.com/gofiber/fiber/v2@v2.47.0/utils/ips.go (about)

     1  package utils
     2  
     3  import (
     4  	"net"
     5  )
     6  
     7  // IsIPv4 works the same way as net.ParseIP,
     8  // but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations.
     9  func IsIPv4(s string) bool {
    10  	for i := 0; i < net.IPv4len; i++ {
    11  		if len(s) == 0 {
    12  			return false
    13  		}
    14  
    15  		if i > 0 {
    16  			if s[0] != '.' {
    17  				return false
    18  			}
    19  			s = s[1:]
    20  		}
    21  
    22  		n, ci := 0, 0
    23  
    24  		for ci = 0; ci < len(s) && '0' <= s[ci] && s[ci] <= '9'; ci++ {
    25  			n = n*10 + int(s[ci]-'0')
    26  			if n >= 0xFF {
    27  				return false
    28  			}
    29  		}
    30  
    31  		if ci == 0 || (ci > 1 && s[0] == '0') {
    32  			return false
    33  		}
    34  
    35  		s = s[ci:]
    36  	}
    37  
    38  	return len(s) == 0
    39  }
    40  
    41  // IsIPv6 works the same way as net.ParseIP,
    42  // but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations.
    43  func IsIPv6(s string) bool {
    44  	ellipsis := -1 // position of ellipsis in ip
    45  
    46  	// Might have leading ellipsis
    47  	if len(s) >= 2 && s[0] == ':' && s[1] == ':' {
    48  		ellipsis = 0
    49  		s = s[2:]
    50  		// Might be only ellipsis
    51  		if len(s) == 0 {
    52  			return true
    53  		}
    54  	}
    55  
    56  	// Loop, parsing hex numbers followed by colon.
    57  	i := 0
    58  	for i < net.IPv6len {
    59  		// Hex number.
    60  		n, ci := 0, 0
    61  
    62  		for ci = 0; ci < len(s); ci++ {
    63  			if '0' <= s[ci] && s[ci] <= '9' {
    64  				n *= 16
    65  				n += int(s[ci] - '0')
    66  			} else if 'a' <= s[ci] && s[ci] <= 'f' {
    67  				n *= 16
    68  				n += int(s[ci]-'a') + 10
    69  			} else if 'A' <= s[ci] && s[ci] <= 'F' {
    70  				n *= 16
    71  				n += int(s[ci]-'A') + 10
    72  			} else {
    73  				break
    74  			}
    75  			if n > 0xFFFF {
    76  				return false
    77  			}
    78  		}
    79  		if ci == 0 || n > 0xFFFF {
    80  			return false
    81  		}
    82  
    83  		if ci < len(s) && s[ci] == '.' {
    84  			if ellipsis < 0 && i != net.IPv6len-net.IPv4len {
    85  				return false
    86  			}
    87  			if i+net.IPv4len > net.IPv6len {
    88  				return false
    89  			}
    90  
    91  			if !IsIPv4(s) {
    92  				return false
    93  			}
    94  
    95  			s = ""
    96  			i += net.IPv4len
    97  			break
    98  		}
    99  
   100  		// Save this 16-bit chunk.
   101  		i += 2
   102  
   103  		// Stop at end of string.
   104  		s = s[ci:]
   105  		if len(s) == 0 {
   106  			break
   107  		}
   108  
   109  		// Otherwise must be followed by colon and more.
   110  		if s[0] != ':' || len(s) == 1 {
   111  			return false
   112  		}
   113  		s = s[1:]
   114  
   115  		// Look for ellipsis.
   116  		if s[0] == ':' {
   117  			if ellipsis >= 0 { // already have one
   118  				return false
   119  			}
   120  			ellipsis = i
   121  			s = s[1:]
   122  			if len(s) == 0 { // can be at end
   123  				break
   124  			}
   125  		}
   126  	}
   127  
   128  	// Must have used entire string.
   129  	if len(s) != 0 {
   130  		return false
   131  	}
   132  
   133  	// If didn't parse enough, expand ellipsis.
   134  	if i < net.IPv6len {
   135  		if ellipsis < 0 {
   136  			return false
   137  		}
   138  	} else if ellipsis >= 0 {
   139  		// Ellipsis must represent at least one 0 group.
   140  		return false
   141  	}
   142  	return true
   143  }