github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/p2p/net/conn/dial.go (about)

     1  package conn
     2  
     3  import (
     4  	"fmt"
     5  	"math/rand"
     6  	"net"
     7  	"strings"
     8  	"syscall"
     9  
    10  	ma "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    11  	manet "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr-net"
    12  	reuseport "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-reuseport"
    13  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    14  	lgbl "github.com/ipfs/go-ipfs/util/eventlog/loggables"
    15  
    16  	addrutil "github.com/ipfs/go-ipfs/p2p/net/swarm/addr"
    17  	peer "github.com/ipfs/go-ipfs/p2p/peer"
    18  )
    19  
    20  // String returns the string rep of d.
    21  func (d *Dialer) String() string {
    22  	return fmt.Sprintf("<Dialer %s %s ...>", d.LocalPeer, d.LocalAddrs[0])
    23  }
    24  
    25  // Dial connects to a peer over a particular address
    26  // Ensures raddr is part of peer.Addresses()
    27  // Example: d.DialAddr(ctx, peer.Addresses()[0], peer)
    28  func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) {
    29  	logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)
    30  	logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not.
    31  	defer log.EventBegin(ctx, "connDial", logdial).Done()
    32  
    33  	var connOut Conn
    34  	var errOut error
    35  	done := make(chan struct{})
    36  
    37  	// do it async to ensure we respect don contexteone
    38  	go func() {
    39  		defer func() {
    40  			select {
    41  			case done <- struct{}{}:
    42  			case <-ctx.Done():
    43  			}
    44  		}()
    45  
    46  		maconn, err := d.rawConnDial(ctx, raddr, remote)
    47  		if err != nil {
    48  			errOut = err
    49  			return
    50  		}
    51  
    52  		if d.Wrapper != nil {
    53  			maconn = d.Wrapper(maconn)
    54  		}
    55  
    56  		c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn)
    57  		if err != nil {
    58  			maconn.Close()
    59  			errOut = err
    60  			return
    61  		}
    62  
    63  		if d.PrivateKey == nil || EncryptConnections == false {
    64  			log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr)
    65  			connOut = c
    66  			return
    67  		}
    68  
    69  		c2, err := newSecureConn(ctx, d.PrivateKey, c)
    70  		if err != nil {
    71  			errOut = err
    72  			c.Close()
    73  			return
    74  		}
    75  
    76  		connOut = c2
    77  	}()
    78  
    79  	select {
    80  	case <-ctx.Done():
    81  		logdial["error"] = ctx.Err()
    82  		logdial["dial"] = "failure"
    83  		return nil, ctx.Err()
    84  	case <-done:
    85  		// whew, finished.
    86  	}
    87  
    88  	if errOut != nil {
    89  		logdial["error"] = errOut
    90  		logdial["dial"] = "failure"
    91  		return nil, errOut
    92  	}
    93  
    94  	logdial["dial"] = "success"
    95  	return connOut, nil
    96  }
    97  
    98  // rawConnDial dials the underlying net.Conn + manet.Conns
    99  func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (manet.Conn, error) {
   100  
   101  	// before doing anything, check we're going to be able to dial.
   102  	// we may not support the given address.
   103  	if _, _, err := manet.DialArgs(raddr); err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") {
   108  		log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr))
   109  		return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr)
   110  	}
   111  
   112  	// get local addr to use.
   113  	laddr := pickLocalAddr(d.LocalAddrs, raddr)
   114  	logdial := lgbl.Dial("conn", d.LocalPeer, remote, laddr, raddr)
   115  	defer log.EventBegin(ctx, "connDialRawConn", logdial).Done()
   116  
   117  	// make a copy of the manet.Dialer, we may need to change its timeout.
   118  	madialer := d.Dialer
   119  
   120  	if laddr != nil && reuseportIsAvailable() {
   121  		// we're perhaps going to dial twice. half the timeout, so we can afford to.
   122  		// otherwise our context would expire right after the first dial.
   123  		madialer.Dialer.Timeout = (madialer.Dialer.Timeout / 2)
   124  
   125  		// dial using reuseport.Dialer, because we're probably reusing addrs.
   126  		// this is optimistic, as the reuseDial may fail to bind the port.
   127  		rpev := log.EventBegin(ctx, "connDialReusePort", logdial)
   128  		if nconn, retry, reuseErr := reuseDial(madialer.Dialer, laddr, raddr); reuseErr == nil {
   129  			// if it worked, wrap the raw net.Conn with our manet.Conn
   130  			logdial["reuseport"] = "success"
   131  			rpev.Done()
   132  			return manet.WrapNetConn(nconn)
   133  		} else if !retry {
   134  			// reuseDial is sure this is a legitimate dial failure, not a reuseport failure.
   135  			logdial["reuseport"] = "failure"
   136  			logdial["error"] = reuseErr
   137  			rpev.Done()
   138  			return nil, reuseErr
   139  		} else {
   140  			// this is a failure to reuse port. log it.
   141  			logdial["reuseport"] = "retry"
   142  			logdial["error"] = reuseErr
   143  			rpev.Done()
   144  		}
   145  	}
   146  
   147  	defer log.EventBegin(ctx, "connDialManet", logdial).Done()
   148  	return madialer.Dial(raddr)
   149  }
   150  
   151  func reuseDial(dialer net.Dialer, laddr, raddr ma.Multiaddr) (conn net.Conn, retry bool, err error) {
   152  	if laddr == nil {
   153  		// if we're given no local address no sense in using reuseport to dial, dial out as usual.
   154  		return nil, true, reuseport.ErrReuseFailed
   155  	}
   156  
   157  	// give reuse.Dialer the manet.Dialer's Dialer.
   158  	// (wow, Dialer should've so been an interface...)
   159  	rd := reuseport.Dialer{dialer}
   160  
   161  	// get the local net.Addr manually
   162  	rd.D.LocalAddr, err = manet.ToNetAddr(laddr)
   163  	if err != nil {
   164  		return nil, true, err // something wrong with laddr. retry without.
   165  	}
   166  
   167  	// get the raddr dial args for rd.dial
   168  	network, netraddr, err := manet.DialArgs(raddr)
   169  	if err != nil {
   170  		return nil, true, err // something wrong with laddr. retry without.
   171  	}
   172  
   173  	// rd.Dial gets us a net.Conn with SO_REUSEPORT and SO_REUSEADDR set.
   174  	conn, err = rd.Dial(network, netraddr)
   175  	return conn, reuseErrShouldRetry(err), err // hey! it worked!
   176  }
   177  
   178  // reuseErrShouldRetry diagnoses whether to retry after a reuse error.
   179  // if we failed to bind, we should retry. if bind worked and this is a
   180  // real dial error (remote end didnt answer) then we should not retry.
   181  func reuseErrShouldRetry(err error) bool {
   182  	if err == nil {
   183  		return false // hey, it worked! no need to retry.
   184  	}
   185  
   186  	// if it's a network timeout error, it's a legitimate failure.
   187  	if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
   188  		return false
   189  	}
   190  
   191  	errno, ok := err.(syscall.Errno)
   192  	if !ok { // not an errno? who knows what this is. retry.
   193  		return true
   194  	}
   195  
   196  	switch errno {
   197  	case syscall.EADDRINUSE, syscall.EADDRNOTAVAIL:
   198  		return true // failure to bind. retry.
   199  	case syscall.ECONNREFUSED:
   200  		return false // real dial error
   201  	default:
   202  		return true // optimistically default to retry.
   203  	}
   204  }
   205  
   206  func pickLocalAddr(laddrs []ma.Multiaddr, raddr ma.Multiaddr) (laddr ma.Multiaddr) {
   207  	if len(laddrs) < 1 {
   208  		return nil
   209  	}
   210  
   211  	// make sure that we ONLY use local addrs that match the remote addr.
   212  	laddrs = manet.AddrMatch(raddr, laddrs)
   213  	if len(laddrs) < 1 {
   214  		return nil
   215  	}
   216  
   217  	// make sure that we ONLY use local addrs that CAN dial the remote addr.
   218  	// filter out all the local addrs that aren't capable
   219  	raddrIPLayer := ma.Split(raddr)[0]
   220  	raddrIsLoopback := manet.IsIPLoopback(raddrIPLayer)
   221  	raddrIsLinkLocal := manet.IsIP6LinkLocal(raddrIPLayer)
   222  	laddrs = addrutil.FilterAddrs(laddrs, func(a ma.Multiaddr) bool {
   223  		laddrIPLayer := ma.Split(a)[0]
   224  		laddrIsLoopback := manet.IsIPLoopback(laddrIPLayer)
   225  		laddrIsLinkLocal := manet.IsIP6LinkLocal(laddrIPLayer)
   226  		if laddrIsLoopback { // our loopback addrs can only dial loopbacks.
   227  			return raddrIsLoopback
   228  		}
   229  		if laddrIsLinkLocal {
   230  			return raddrIsLinkLocal // out linklocal addrs can only dial link locals.
   231  		}
   232  		return true
   233  	})
   234  
   235  	// TODO pick with a good heuristic
   236  	// we use a random one for now to prevent bad addresses from making nodes unreachable
   237  	// with a random selection, multiple tries may work.
   238  	return laddrs[rand.Intn(len(laddrs))]
   239  }
   240  
   241  // MultiaddrProtocolsMatch returns whether two multiaddrs match in protocol stacks.
   242  func MultiaddrProtocolsMatch(a, b ma.Multiaddr) bool {
   243  	ap := a.Protocols()
   244  	bp := b.Protocols()
   245  
   246  	if len(ap) != len(bp) {
   247  		return false
   248  	}
   249  
   250  	for i, api := range ap {
   251  		if api.Code != bp[i].Code {
   252  			return false
   253  		}
   254  	}
   255  
   256  	return true
   257  }
   258  
   259  // MultiaddrNetMatch returns the first Multiaddr found to match  network.
   260  func MultiaddrNetMatch(tgt ma.Multiaddr, srcs []ma.Multiaddr) ma.Multiaddr {
   261  	for _, a := range srcs {
   262  		if MultiaddrProtocolsMatch(tgt, a) {
   263  			return a
   264  		}
   265  	}
   266  	return nil
   267  }