github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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 // A NetAddress contains the information needed to contact a peer. 13 type NetAddress string 14 15 // Host removes the port from a NetAddress, returning just the host. If the 16 // address is not of the form "host:port" the empty string is returned. The 17 // port will still be returned for invalid NetAddresses (e.g. "unqualified:0" 18 // will return "unqualified"), but in general you should only call Host on 19 // valid addresses. 20 func (na NetAddress) Host() string { 21 host, _, err := net.SplitHostPort(string(na)) 22 // 'host' is not always the empty string if an error is returned. 23 if err != nil { 24 return "" 25 } 26 return host 27 } 28 29 // Port returns the NetAddress object's port number. If the address is not of 30 // the form "host:port" the empty string is returned. The port will still be 31 // returned for invalid NetAddresses (e.g. "localhost:0" will return "0"), but 32 // in general you should only call Port on valid addresses. 33 func (na NetAddress) Port() string { 34 _, port, err := net.SplitHostPort(string(na)) 35 // 'port' will not always be the empty string if an error is returned. 36 if err != nil { 37 return "" 38 } 39 return port 40 } 41 42 // isLoopback returns true for ip addresses that are on the same machine. 43 func (na NetAddress) isLoopback() bool { 44 host, _, err := net.SplitHostPort(string(na)) 45 if err != nil { 46 return false 47 } 48 if host == "localhost" { 49 return true 50 } 51 if ip := net.ParseIP(host); ip != nil && ip.IsLoopback() { 52 return true 53 } 54 return false 55 } 56 57 // IsValid returns an error if the NetAddress is invalid. A valid NetAddress 58 // is of the form "host:port", such that "host" is either a valid IPv4/IPv6 59 // address or a valid hostname, and "port" is an integer in the range 60 // [1,65535]. Furthermore, "host" may not be a loopback address (except during 61 // testing). Valid IPv4 addresses, IPv6 addresses, and hostnames are detailed 62 // in RFCs 791, 2460, and 952, respectively. 63 func (na NetAddress) IsValid() error { 64 host, port, err := net.SplitHostPort(string(na)) 65 if err != nil { 66 return err 67 } 68 69 portInt, err := strconv.Atoi(port) 70 if err != nil { 71 return errors.New("port is not an integer") 72 } else if portInt < 1 || portInt > 65535 { 73 return errors.New("port is invalid") 74 } 75 76 // This check must come after the valid port check so that a host such as 77 // "localhost:badport" will fail. 78 if na.isLoopback() { 79 if build.Release == "testing" { 80 return nil 81 } 82 return errors.New("host is a loopback address") 83 } 84 85 // First try to parse host as an IP address; if that fails, assume it is a 86 // hostname. 87 if ip := net.ParseIP(host); ip != nil { 88 if ip.IsUnspecified() { 89 return errors.New("host is the unspecified address") 90 } 91 } else { 92 // Hostnames can have a trailing dot (which indicates that the hostname is 93 // fully qualified), but we ignore it for validation purposes. 94 if strings.HasSuffix(host, ".") { 95 host = host[:len(host)-1] 96 } 97 if len(host) < 1 || len(host) > 253 { 98 return errors.New("invalid hostname length") 99 } 100 labels := strings.Split(host, ".") 101 if len(labels) == 1 { 102 return errors.New("unqualified hostname") 103 } 104 for _, label := range labels { 105 if len(label) < 1 || len(label) > 63 { 106 return errors.New("hostname contains label with invalid length") 107 } 108 if strings.HasPrefix(label, "-") || strings.HasSuffix(label, "-") { 109 return errors.New("hostname contains label that starts or ends with a hyphen") 110 } 111 for _, r := range strings.ToLower(label) { 112 isLetter := 'a' <= r && r <= 'z' 113 isNumber := '0' <= r && r <= '9' 114 isHyphen := r == '-' 115 if !(isLetter || isNumber || isHyphen) { 116 return errors.New("host contains invalid characters") 117 } 118 } 119 } 120 } 121 122 return nil 123 }