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  `