github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/network/hostport.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package network 5 6 import ( 7 "net" 8 "sort" 9 "strconv" 10 11 "github.com/juju/errors" 12 "github.com/juju/utils/set" 13 ) 14 15 // HostPort associates an address with a port. 16 type HostPort struct { 17 Address 18 Port int 19 } 20 21 // NetAddr returns the host-port as an address 22 // suitable for calling net.Dial. 23 func (hp HostPort) NetAddr() string { 24 return net.JoinHostPort(hp.Value, strconv.Itoa(hp.Port)) 25 } 26 27 // String implements Stringer. 28 func (hp HostPort) String() string { 29 return hp.NetAddr() 30 } 31 32 // GoString implements fmt.GoStringer. 33 func (hp HostPort) GoString() string { 34 return hp.String() 35 } 36 37 // AddressesWithPort returns the given addresses all 38 // associated with the given port. 39 func AddressesWithPort(addrs []Address, port int) []HostPort { 40 hps := make([]HostPort, len(addrs)) 41 for i, addr := range addrs { 42 hps[i] = HostPort{ 43 Address: addr, 44 Port: port, 45 } 46 } 47 return hps 48 } 49 50 // NewHostPorts creates a list of HostPorts from each given string 51 // address and port. 52 func NewHostPorts(port int, addresses ...string) []HostPort { 53 hps := make([]HostPort, len(addresses)) 54 for i, addr := range addresses { 55 hps[i] = HostPort{ 56 Address: NewAddress(addr), 57 Port: port, 58 } 59 } 60 return hps 61 } 62 63 // ParseHostPorts creates a list of HostPorts parsing each given 64 // string containing address:port. An error is returned if any string 65 // cannot be parsed as HostPort. 66 func ParseHostPorts(hostPorts ...string) ([]HostPort, error) { 67 hps := make([]HostPort, len(hostPorts)) 68 for i, hp := range hostPorts { 69 hostport, err := ParseHostPort(hp) 70 if err != nil { 71 return nil, errors.Trace(err) 72 } 73 hps[i] = *hostport 74 } 75 return hps, nil 76 } 77 78 // ParseHostPort converts a string containing a single host and port 79 // value to a HostPort. 80 func ParseHostPort(hp string) (*HostPort, error) { 81 host, port, err := net.SplitHostPort(hp) 82 if err != nil { 83 return nil, errors.Annotatef(err, "cannot parse %q as address:port", hp) 84 } 85 numPort, err := strconv.Atoi(port) 86 if err != nil { 87 return nil, errors.Annotatef(err, "cannot parse %q port", hp) 88 } 89 return &HostPort{ 90 Address: NewAddress(host), 91 Port: numPort, 92 }, nil 93 } 94 95 // HostsWithoutPort strips the port from each HostPort, returning just 96 // the addresses. 97 func HostsWithoutPort(hps []HostPort) []Address { 98 addrs := make([]Address, len(hps)) 99 for i, hp := range hps { 100 addrs[i] = hp.Address 101 } 102 return addrs 103 } 104 105 type hostPortsPreferringIPv4Slice []HostPort 106 107 func (hp hostPortsPreferringIPv4Slice) Len() int { return len(hp) } 108 func (hp hostPortsPreferringIPv4Slice) Swap(i, j int) { hp[i], hp[j] = hp[j], hp[i] } 109 func (hp hostPortsPreferringIPv4Slice) Less(i, j int) bool { 110 hp1 := hp[i] 111 hp2 := hp[j] 112 order1 := hp1.sortOrder() 113 order2 := hp2.sortOrder() 114 if order1 == order2 { 115 if hp1.Address.Value == hp2.Address.Value { 116 return hp1.Port < hp2.Port 117 } 118 return hp1.Address.Value < hp2.Address.Value 119 } 120 return order1 < order2 121 } 122 123 // SortHostPorts sorts the given HostPort slice according to the sortOrder of 124 // each HostPort's embedded Address. See Address.sortOrder() for more info. 125 func SortHostPorts(hps []HostPort) { 126 sort.Sort(hostPortsPreferringIPv4Slice(hps)) 127 } 128 129 var netLookupIP = net.LookupIP 130 131 // ResolveOrDropHostnames tries to resolve each address of type 132 // HostName (except for "localhost" - it's kept unchanged) using the 133 // local resolver. If successful, each IP address corresponding to the 134 // hostname is inserted in the same order. If not successful, a debug 135 // log is added and the hostname is removed from the list. Duplicated 136 // addresses after the resolving is done are removed. 137 func ResolveOrDropHostnames(hps []HostPort) []HostPort { 138 uniqueAddrs := set.NewStrings() 139 result := make([]HostPort, 0, len(hps)) 140 for _, hp := range hps { 141 val := hp.Value 142 if uniqueAddrs.Contains(val) { 143 continue 144 } 145 // localhost is special - do not resolve it, because it can be 146 // used both as an IPv4 or IPv6 endpoint (e.g. in IPv6-only 147 // networks). 148 if hp.Type != HostName || hp.Value == "localhost" { 149 result = append(result, hp) 150 uniqueAddrs.Add(val) 151 continue 152 } 153 ips, err := netLookupIP(val) 154 if err != nil { 155 logger.Debugf("removing unresolvable address %q: %v", val, err) 156 continue 157 } 158 for _, ip := range ips { 159 if ip == nil { 160 continue 161 } 162 addr := NewAddress(ip.String()) 163 if !uniqueAddrs.Contains(addr.Value) { 164 result = append(result, HostPort{Address: addr, Port: hp.Port}) 165 uniqueAddrs.Add(addr.Value) 166 } 167 } 168 } 169 return result 170 } 171 172 // FilterUnusableHostPorts returns a copy of the given HostPorts after 173 // removing any addresses unlikely to be usable (ScopeMachineLocal or 174 // ScopeLinkLocal). 175 func FilterUnusableHostPorts(hps []HostPort) []HostPort { 176 filtered := make([]HostPort, 0, len(hps)) 177 for _, hp := range hps { 178 switch hp.Scope { 179 case ScopeMachineLocal, ScopeLinkLocal: 180 continue 181 } 182 filtered = append(filtered, hp) 183 } 184 return filtered 185 } 186 187 // DropDuplicatedHostPorts removes any HostPorts duplicates from the 188 // given slice and returns the result. 189 func DropDuplicatedHostPorts(hps []HostPort) []HostPort { 190 uniqueHPs := set.NewStrings() 191 var result []HostPort 192 for _, hp := range hps { 193 if !uniqueHPs.Contains(hp.NetAddr()) { 194 uniqueHPs.Add(hp.NetAddr()) 195 result = append(result, hp) 196 } 197 } 198 return result 199 } 200 201 // HostPortsToStrings converts each HostPort to string calling its 202 // NetAddr() method. 203 func HostPortsToStrings(hps []HostPort) []string { 204 result := make([]string, len(hps)) 205 for i, hp := range hps { 206 result[i] = hp.NetAddr() 207 } 208 return result 209 } 210 211 // CollapseHostPorts returns a flattened list of HostPorts keeping the 212 // same order they appear in serversHostPorts. 213 func CollapseHostPorts(serversHostPorts [][]HostPort) []HostPort { 214 var collapsed []HostPort 215 for _, hps := range serversHostPorts { 216 collapsed = append(collapsed, hps...) 217 } 218 return collapsed 219 } 220 221 // EnsureFirstHostPort scans the given list of HostPorts and if 222 // "first" is found, it moved to index 0. Otherwise, if "first" is not 223 // in the list, it's inserted at index 0. 224 func EnsureFirstHostPort(first HostPort, hps []HostPort) []HostPort { 225 var result []HostPort 226 found := false 227 for _, hp := range hps { 228 if hp.NetAddr() == first.NetAddr() && !found { 229 // Found, so skip it. 230 found = true 231 continue 232 } 233 result = append(result, hp) 234 } 235 // Insert it at the top. 236 result = append([]HostPort{first}, result...) 237 return result 238 }