github.com/NebulousLabs/Sia@v1.3.7/modules/netaddress.go (about) 1 package modules 2 3 import ( 4 "errors" 5 "net" 6 "strconv" 7 "strings" 8 9 "github.com/NebulousLabs/Sia/build" 10 ) 11 12 // MaxEncodedNetAddressLength is the maximum length of a NetAddress encoded 13 // with the encode package. 266 was chosen because the maximum length for the 14 // hostname is 254 + 1 for the separating colon + 5 for the port + 8 byte 15 // string length prefix. 16 const MaxEncodedNetAddressLength = 266 17 18 // A NetAddress contains the information needed to contact a peer. 19 type NetAddress string 20 21 // Host removes the port from a NetAddress, returning just the host. If the 22 // address is not of the form "host:port" the empty string is returned. The 23 // port will still be returned for invalid NetAddresses (e.g. "unqualified:0" 24 // will return "unqualified"), but in general you should only call Host on 25 // valid addresses. 26 func (na NetAddress) Host() string { 27 host, _, err := net.SplitHostPort(string(na)) 28 // 'host' is not always the empty string if an error is returned. 29 if err != nil { 30 return "" 31 } 32 return host 33 } 34 35 // Port returns the NetAddress object's port number. If the address is not of 36 // the form "host:port" the empty string is returned. The port will still be 37 // returned for invalid NetAddresses (e.g. "localhost:0" will return "0"), but 38 // in general you should only call Port on valid addresses. 39 func (na NetAddress) Port() string { 40 _, port, err := net.SplitHostPort(string(na)) 41 // 'port' will not always be the empty string if an error is returned. 42 if err != nil { 43 return "" 44 } 45 return port 46 } 47 48 // IsLoopback returns true for IP addresses that are on the same machine. 49 func (na NetAddress) IsLoopback() bool { 50 host, _, err := net.SplitHostPort(string(na)) 51 if err != nil { 52 return false 53 } 54 if host == "localhost" { 55 return true 56 } 57 if ip := net.ParseIP(host); ip != nil && ip.IsLoopback() { 58 return true 59 } 60 return false 61 } 62 63 // IsLocal returns true if the input IP address belongs to a local address 64 // range such as 192.168.x.x or 127.x.x.x 65 func (na NetAddress) IsLocal() bool { 66 // Loopback counts as private. 67 if na.IsLoopback() { 68 return true 69 } 70 71 // Grab the IP address of the net address. If there is an error parsing, 72 // return false, as it's not a private ip address range. 73 ip := net.ParseIP(na.Host()) 74 if ip == nil { 75 return false 76 } 77 78 // Determine whether or not the ip is in a CIDR that is considered to be 79 // local. 80 localCIDRs := []string{ 81 "10.0.0.0/8", 82 "172.16.0.0/12", 83 "192.168.0.0/16", 84 "fd00::/8", 85 } 86 for _, cidr := range localCIDRs { 87 _, ipnet, _ := net.ParseCIDR(cidr) 88 if ipnet.Contains(ip) { 89 return true 90 } 91 } 92 return false 93 } 94 95 // IsValid is an extension to IsStdValid that also forbids the loopback 96 // address. IsValid is being phased out in favor of allowing the loopback 97 // address but verifying through other means that the connection is not to 98 // yourself (which is the original reason that the loopback address was 99 // banned). 100 func (na NetAddress) IsValid() error { 101 // Check the loopback address. 102 if na.IsLoopback() && build.Release != "testing" { 103 return errors.New("host is a loopback address") 104 } 105 return na.IsStdValid() 106 } 107 108 // IsStdValid returns an error if the NetAddress is invalid. A valid NetAddress 109 // is of the form "host:port", such that "host" is either a valid IPv4/IPv6 110 // address or a valid hostname, and "port" is an integer in the range 111 // [1,65535]. Valid IPv4 addresses, IPv6 addresses, and hostnames are detailed 112 // in RFCs 791, 2460, and 952, respectively. 113 func (na NetAddress) IsStdValid() error { 114 // Verify the port number. 115 host, port, err := net.SplitHostPort(string(na)) 116 if err != nil { 117 return err 118 } 119 portInt, err := strconv.Atoi(port) 120 if err != nil { 121 return errors.New("port is not an integer") 122 } else if portInt < 1 || portInt > 65535 { 123 return errors.New("port is invalid") 124 } 125 126 // Loopback addresses don't always pass the requirements below, and 127 // therefore must be checked separately. 128 if na.IsLoopback() { 129 return nil 130 } 131 132 // First try to parse host as an IP address; if that fails, assume it is a 133 // hostname. 134 if ip := net.ParseIP(host); ip != nil { 135 if ip.IsUnspecified() { 136 return errors.New("host is the unspecified address") 137 } 138 } else { 139 // Hostnames can have a trailing dot (which indicates that the hostname is 140 // fully qualified), but we ignore it for validation purposes. 141 if strings.HasSuffix(host, ".") { 142 host = host[:len(host)-1] 143 } 144 if len(host) < 1 || len(host) > 253 { 145 return errors.New("invalid hostname length") 146 } 147 labels := strings.Split(host, ".") 148 if len(labels) == 1 { 149 return errors.New("unqualified hostname") 150 } 151 for _, label := range labels { 152 if len(label) < 1 || len(label) > 63 { 153 return errors.New("hostname contains label with invalid length") 154 } 155 if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") { 156 return errors.New("hostname contains label that starts or ends with a hyphen") 157 } 158 for _, r := range strings.ToLower(label) { 159 isLetter := 'a' <= r && r <= 'z' 160 isNumber := '0' <= r && r <= '9' 161 isHyphen := r == '-' 162 if !(isLetter || isNumber || isHyphen) { 163 return errors.New("host contains invalid characters") 164 } 165 } 166 } 167 } 168 169 return nil 170 }