github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/net/swarm/addr/addr.go (about) 1 package addrutil 2 3 import ( 4 "fmt" 5 6 eventlog "github.com/ipfs/go-ipfs/thirdparty/eventlog" 7 8 ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr" 9 manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net" 10 context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context" 11 ) 12 13 var log = eventlog.Logger("p2p/net/swarm/addr") 14 15 // SupportedTransportStrings is the list of supported transports for the swarm. 16 // These are strings of encapsulated multiaddr protocols. E.g.: 17 // /ip4/tcp 18 var SupportedTransportStrings = []string{ 19 "/ip4/tcp", 20 "/ip6/tcp", 21 // "/ip4/udp/utp", disabled because the lib is broken 22 // "/ip6/udp/utp", disabled because the lib is broken 23 // "/ip4/udp/udt", disabled because the lib doesnt work on arm 24 // "/ip6/udp/udt", disabled because the lib doesnt work on arm 25 } 26 27 // SupportedTransportProtocols is the list of supported transports for the swarm. 28 // These are []ma.Protocol lists. Populated at runtime from SupportedTransportStrings 29 var SupportedTransportProtocols = [][]ma.Protocol{} 30 31 func init() { 32 // initialize SupportedTransportProtocols 33 transports := make([][]ma.Protocol, len(SupportedTransportStrings)) 34 for _, s := range SupportedTransportStrings { 35 t, err := ma.ProtocolsWithString(s) 36 if err != nil { 37 panic(err) // important to fix this in the codebase 38 } 39 transports = append(transports, t) 40 } 41 SupportedTransportProtocols = transports 42 } 43 44 // FilterAddrs is a filter that removes certain addresses, according to filter. 45 // if filter returns true, the address is kept. 46 func FilterAddrs(a []ma.Multiaddr, filter func(ma.Multiaddr) bool) []ma.Multiaddr { 47 b := make([]ma.Multiaddr, 0, len(a)) 48 for _, addr := range a { 49 if filter(addr) { 50 b = append(b, addr) 51 } 52 } 53 return b 54 } 55 56 // FilterUsableAddrs removes certain addresses 57 // from a list. the addresses removed are those known NOT 58 // to work with our network. Namely, addresses with UTP. 59 func FilterUsableAddrs(a []ma.Multiaddr) []ma.Multiaddr { 60 return FilterAddrs(a, func(m ma.Multiaddr) bool { 61 return AddrUsable(m, false) 62 }) 63 } 64 65 // AddrOverNonLocalIP returns whether the addr uses a non-local ip link 66 func AddrOverNonLocalIP(a ma.Multiaddr) bool { 67 split := ma.Split(a) 68 if len(split) < 1 { 69 return false 70 } 71 if manet.IsIP6LinkLocal(split[0]) { 72 return false 73 } 74 return true 75 } 76 77 // AddrUsable returns whether our network can use this addr. 78 // We only use the transports in SupportedTransportStrings, 79 // and we do not link local addresses. Loopback is ok 80 // as we need to be able to connect to multiple ipfs nodes 81 // in the same machine. 82 func AddrUsable(a ma.Multiaddr, partial bool) bool { 83 if a == nil { 84 return false 85 } 86 87 if !AddrOverNonLocalIP(a) { 88 return false 89 } 90 91 // test the address protocol list is in SupportedTransportProtocols 92 matches := func(supported, test []ma.Protocol) bool { 93 if len(test) > len(supported) { 94 return false 95 } 96 97 // when partial, it's ok if test < supported. 98 if !partial && len(supported) != len(test) { 99 return false 100 } 101 102 for i := range test { 103 if supported[i].Code != test[i].Code { 104 return false 105 } 106 } 107 return true 108 } 109 110 transport := a.Protocols() 111 for _, supported := range SupportedTransportProtocols { 112 if matches(supported, transport) { 113 return true 114 } 115 } 116 117 return false 118 } 119 120 // ResolveUnspecifiedAddress expands an unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to 121 // use the known local interfaces. If ifaceAddr is nil, we request interface addresses 122 // from the network stack. (this is so you can provide a cached value if resolving many addrs) 123 func ResolveUnspecifiedAddress(resolve ma.Multiaddr, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) { 124 // split address into its components 125 split := ma.Split(resolve) 126 127 // if first component (ip) is not unspecified, use it as is. 128 if !manet.IsIPUnspecified(split[0]) { 129 return []ma.Multiaddr{resolve}, nil 130 } 131 132 out := make([]ma.Multiaddr, 0, len(ifaceAddrs)) 133 for _, ia := range ifaceAddrs { 134 // must match the first protocol to be resolve. 135 if ia.Protocols()[0].Code != resolve.Protocols()[0].Code { 136 continue 137 } 138 139 split[0] = ia 140 joined := ma.Join(split...) 141 out = append(out, joined) 142 log.Debug("adding resolved addr:", resolve, joined, out) 143 } 144 if len(out) < 1 { 145 return nil, fmt.Errorf("failed to resolve: %s", resolve) 146 } 147 return out, nil 148 } 149 150 // ResolveUnspecifiedAddresses expands unspecified ip addresses (/ip4/0.0.0.0, /ip6/::) to 151 // use the known local interfaces. 152 func ResolveUnspecifiedAddresses(unspecAddrs, ifaceAddrs []ma.Multiaddr) ([]ma.Multiaddr, error) { 153 154 // todo optimize: only fetch these if we have a "any" addr. 155 if len(ifaceAddrs) < 1 { 156 var err error 157 ifaceAddrs, err = InterfaceAddresses() 158 if err != nil { 159 return nil, err 160 } 161 // log.Debug("InterfaceAddresses:", ifaceAddrs) 162 } 163 164 var outputAddrs []ma.Multiaddr 165 for _, a := range unspecAddrs { 166 // unspecified? 167 resolved, err := ResolveUnspecifiedAddress(a, ifaceAddrs) 168 if err != nil { 169 continue // optimistic. if we cant resolve anything, we'll know at the bottom. 170 } 171 // log.Debug("resolved:", a, resolved) 172 outputAddrs = append(outputAddrs, resolved...) 173 } 174 175 if len(outputAddrs) < 1 { 176 return nil, fmt.Errorf("failed to specify addrs: %s", unspecAddrs) 177 } 178 179 log.Event(context.TODO(), "interfaceListenAddresses", func() eventlog.Loggable { 180 var addrs []string 181 for _, addr := range outputAddrs { 182 addrs = append(addrs, addr.String()) 183 } 184 return eventlog.Metadata{"addresses": addrs} 185 }()) 186 187 log.Debug("ResolveUnspecifiedAddresses:", unspecAddrs, ifaceAddrs, outputAddrs) 188 return outputAddrs, nil 189 } 190 191 // InterfaceAddresses returns a list of addresses associated with local machine 192 // Note: we do not return link local addresses. IP loopback is ok, because we 193 // may be connecting to other nodes in the same machine. 194 func InterfaceAddresses() ([]ma.Multiaddr, error) { 195 maddrs, err := manet.InterfaceMultiaddrs() 196 if err != nil { 197 return nil, err 198 } 199 log.Debug("InterfaceAddresses: from manet:", maddrs) 200 201 var out []ma.Multiaddr 202 for _, a := range maddrs { 203 if !AddrUsable(a, true) { // partial 204 // log.Debug("InterfaceAddresses: skipping unusable:", a) 205 continue 206 } 207 208 out = append(out, a) 209 } 210 211 log.Debug("InterfaceAddresses: usable:", out) 212 return out, nil 213 } 214 215 // AddrInList returns whether or not an address is part of a list. 216 // this is useful to check if NAT is happening (or other bugs?) 217 func AddrInList(addr ma.Multiaddr, list []ma.Multiaddr) bool { 218 for _, addr2 := range list { 219 if addr.Equal(addr2) { 220 return true 221 } 222 } 223 return false 224 } 225 226 // AddrIsShareableOnWAN returns whether the given address should be shareable on the 227 // wide area network (wide internet). 228 func AddrIsShareableOnWAN(addr ma.Multiaddr) bool { 229 s := ma.Split(addr) 230 if len(s) < 1 { 231 return false 232 } 233 a := s[0] 234 if manet.IsIPLoopback(a) || manet.IsIP6LinkLocal(a) || manet.IsIPUnspecified(a) { 235 return false 236 } 237 return manet.IsThinWaist(a) 238 } 239 240 // WANShareableAddrs filters addresses based on whether they're shareable on WAN 241 func WANShareableAddrs(inp []ma.Multiaddr) []ma.Multiaddr { 242 return FilterAddrs(inp, AddrIsShareableOnWAN) 243 } 244 245 // Subtract filters out all addrs in b from a 246 func Subtract(a, b []ma.Multiaddr) []ma.Multiaddr { 247 return FilterAddrs(a, func(m ma.Multiaddr) bool { 248 for _, bb := range b { 249 if m.Equal(bb) { 250 return false 251 } 252 } 253 return true 254 }) 255 } 256 257 // CheckNATWarning checks if our observed addresses differ. if so, 258 // informs the user that certain things might not work yet 259 func CheckNATWarning(observed, expected ma.Multiaddr, listen []ma.Multiaddr) { 260 if observed.Equal(expected) { 261 return 262 } 263 264 if !AddrInList(observed, listen) { // probably a nat 265 log.Warningf(natWarning, observed, listen) 266 } 267 } 268 269 const natWarning = `Remote peer observed our address to be: %s 270 The local addresses are: %s 271 Thus, connection is going through NAT, and other connections may fail. 272 273 IPFS NAT traversal is still under development. Please bug us on github or irc to fix this. 274 Baby steps: http://jbenet.static.s3.amazonaws.com/271dfcf/baby-steps.gif 275 `