go.dedis.ch/onet/v4@v4.0.0-pre1/network/address.go (about) 1 package network 2 3 import ( 4 "net" 5 "regexp" 6 "strconv" 7 "strings" 8 ) 9 10 // ConnType represents the type of a Connection. 11 // The supported types are defined as constants of type ConnType. 12 type ConnType string 13 14 // Address contains the ConnType and the actual network address. It is used to connect 15 // to a remote host with a Conn and to listen by a Listener. 16 // A network address holds an IP address and the port number joined 17 // by a colon. 18 // It doesn't support IPv6 yet. 19 type Address string 20 21 var lookupHost = net.LookupHost 22 23 const ( 24 // PlainTCP is an unencrypted TCP connection. 25 PlainTCP ConnType = "tcp" 26 // TLS is a TLS encrypted connection over TCP. 27 TLS = "tls" 28 // Local is a channel based connection type. 29 Local = "local" 30 // InvalidConnType is an invalid connection type. 31 InvalidConnType = "wrong" 32 ) 33 34 // typeAddressSep is the separator between the type of the connection and the actual 35 // IP address. 36 const typeAddressSep = "://" 37 38 // connType converts a string to a ConnType. In case of failure, 39 // it returns InvalidConnType. 40 func connType(t string) ConnType { 41 ct := ConnType(t) 42 types := []ConnType{PlainTCP, TLS, Local} 43 for _, t := range types { 44 if t == ct { 45 return ct 46 } 47 } 48 return InvalidConnType 49 } 50 51 // ConnType returns the connection type from the address. 52 // It returns InvalidConnType if the address is not valid or if the 53 // connection type is not known. 54 func (a Address) ConnType() ConnType { 55 if !a.Valid() { 56 return InvalidConnType 57 } 58 vals := strings.Split(string(a), typeAddressSep) 59 return connType(vals[0]) 60 } 61 62 // IsHostname returns true if the address is defined by a VALID DNS name 63 func (a Address) IsHostname() bool { 64 host := a.Host() 65 66 // validHostname(host) would be enough with the current implementation. 67 // However, if we will include IDNs as valid hostnames, an IP address in the form 68 // *.*.*.* would be a valid hostname too. This is why ParseIP is used as well. 69 return validHostname(host) && net.ParseIP(host) == nil 70 } 71 72 // NetworkAddress returns the network address part of the address, which is 73 // the host and the port joined by a colon. 74 // It returns an empty string the address is not valid 75 func (a Address) NetworkAddress() string { 76 if !a.Valid() { 77 return "" 78 } 79 vals := strings.Split(string(a), typeAddressSep) 80 return vals[1] 81 } 82 83 // NetworkAddressResolved returns the network address of the address, but resolved. 84 // That is: the hostname resolved and the port joined by a colon. 85 // It returns an empty string if the address is not valid. 86 func (a Address) NetworkAddressResolved() string { 87 if !a.Valid() { 88 return "" 89 } 90 ipAddress := a.Resolve() 91 port := a.Port() 92 return net.JoinHostPort(ipAddress, port) 93 } 94 95 // Resolve returns the IP address associated to the hostname that represents the address a. 96 // If a is defined by an IP address (*.*.*.*) or if the hostname is not valid, the empty string 97 // is returned 98 func (a Address) Resolve() string { 99 if !a.Valid() { 100 return "" 101 } 102 host := a.Host() 103 // Ipv6 not handled properly yet 104 if host == "[::]" { 105 return "::" 106 } 107 // If the address is defined by an IP address, return it 108 if net.ParseIP(host) != nil { 109 return host 110 } 111 112 if !a.IsHostname() { 113 return "" 114 } 115 116 ipAddress, err := lookupHost(host) 117 if err != nil { 118 return "" 119 } 120 121 return ipAddress[0] 122 } 123 124 // validHostname returns true if the hostname is well formed or false otherwise. 125 // A hostname is well formed if the following conditions are met: 126 // - each label contains from 1 to 63 characters 127 // - the entire hostname (including the delimiting dots, but not a trailing dot) 128 // has a maximum of 253 ASCII characters 129 // - labels have only ASCII letters 'a' through 'z' (case-insensitive), the digits 130 // '0' through '9', and the hyphen (-). No other symbol is permitted 131 // - labels cannot start with a hyphen 132 // - labels cannot end with a hyphen 133 // - the last label is alphabetic 134 // More information about the definition of the TLD (the last label of a hostname) on: 135 // https://github.com/dedis/cothority/issues/620 136 // This method assumes that only the host part is passed as parameter 137 // This function is integrated in the Valid() function 138 // 139 // For easier integration and testing, this function also returns true if the 140 // string doesn't have any '.' in it. 141 func validHostname(s string) bool { 142 if len(s) == 0 { 143 return false 144 } 145 146 s = strings.ToLower(s) 147 148 maxLength := 253 149 if s[len(s)-1] == '.' { 150 maxLength = 254 151 s = s[:len(s)-1] // remove the last dot --> easier computations 152 } 153 154 if len(s) > maxLength { 155 return false 156 } 157 158 labels := strings.Split(s, ".") 159 160 for _, element := range labels { 161 if len(element) < 1 || len(element) > 63 { 162 return false 163 } 164 } 165 166 valid, _ := regexp.MatchString("^(([a-z0-9]|[a-z0-9][a-z0-9\\-]*[a-z0-9])\\.)*([a-z]+)$", s) 167 if !valid { 168 if strings.Count(s, ".") == 0 { 169 return true 170 } 171 } 172 return valid 173 } 174 175 // Valid returns true if the address is well formed or false otherwise. 176 // An address is well formed if it is of the form: ConnType://NetworkAddress. 177 // ConnType must be one of the constants defined in this file, 178 // NetworkAddress must contain the IP address + Port number. 179 // The IP address is validated by net.ParseIP & the port must be included in the 180 // range [0;65536]. For example, "tls://192.168.1.10:5678". 181 func (a Address) Valid() bool { 182 vals := strings.Split(string(a), typeAddressSep) 183 if len(vals) != 2 { 184 return false 185 } 186 if connType(vals[0]) == InvalidConnType { 187 return false 188 } 189 190 ip, port, e := net.SplitHostPort(vals[1]) 191 if e != nil { 192 return false 193 } 194 195 p, err := strconv.Atoi(port) 196 if err != nil || p < 0 || p > 65535 { 197 return false 198 } 199 200 // If hostname is missing then they mean "all IPs", and that's valid. 201 if len(ip) == 0 { 202 return true 203 } 204 if net.ParseIP(ip) == nil { 205 // if the Host is NOT in the form of *.*.*.* , check whether it has a valid DNS name 206 // This includes "localhost", which is NOT recognized by net.ParseIP 207 return validHostname(ip) 208 } 209 return true 210 } 211 212 // String returns the address as a string. 213 func (a Address) String() string { 214 return string(a) 215 } 216 217 // Host returns the host part of the address. 218 // ex: "tcp://127.0.0.1:2000" => "127.0.0.1" 219 // In case of an error, it returns an empty string. 220 func (a Address) Host() string { 221 na := a.NetworkAddress() 222 if na == "" { 223 return "" 224 } 225 h, _, e := net.SplitHostPort(a.NetworkAddress()) 226 if e != nil { 227 return "" 228 } 229 return h 230 } 231 232 // Port will return the port part of the Address. In the 233 // case of an invalid address or an invalid port, it 234 // will return "". 235 func (a Address) Port() string { 236 na := a.NetworkAddress() 237 if na == "" { 238 return "" 239 } 240 _, p, e := net.SplitHostPort(na) 241 if e != nil { 242 return "" 243 } 244 return p 245 246 } 247 248 // Public returns true if the address is a public and valid one 249 // or false otherwise. 250 // Specifically it checks if it is a private address by checking 251 // 192.168.**,10.***,127.***,172.16-31.**,169.254.**,^::1,^fd.{0,2}: 252 func (a Address) Public() bool { 253 private, err := regexp.MatchString("(^127\\.)|(^10\\.)|"+ 254 "(^172\\.1[6-9]\\.)|(^172\\.2[0-9]\\.)|"+ 255 "(^172\\.3[0-1]\\.)|(^192\\.168\\.)|(^169\\.254)|"+ 256 "(^\\[::1\\])|(^\\[fd.{0,2}:)", a.NetworkAddressResolved()) 257 if err != nil { 258 return false 259 } 260 return !private && a.Valid() 261 } 262 263 // NewAddress takes a connection type and the raw address. It returns a 264 // correctly formatted address, which will be of type t. 265 // It doesn't do any checking of ConnType or network. 266 func NewAddress(t ConnType, network string) Address { 267 return Address(string(t) + typeAddressSep + network) 268 }