// rawConnDial dials the underlying net.Conn + manet.Conns func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (manet.Conn, error) { // before doing anything, check we're going to be able to dial. // we may not support the given address. if _, _, err := manet.DialArgs(raddr); err != nil { return nil, err } if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") { log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)) return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr) } // get local addr to use. laddr := pickLocalAddr(d.LocalAddrs, raddr) logdial := lgbl.Dial("conn", d.LocalPeer, remote, laddr, raddr) defer log.EventBegin(ctx, "connDialRawConn", logdial).Done() // make a copy of the manet.Dialer, we may need to change its timeout. madialer := d.Dialer if laddr != nil && reuseportIsAvailable() { // we're perhaps going to dial twice. half the timeout, so we can afford to. // otherwise our context would expire right after the first dial. madialer.Dialer.Timeout = (madialer.Dialer.Timeout / 2) // dial using reuseport.Dialer, because we're probably reusing addrs. // this is optimistic, as the reuseDial may fail to bind the port. rpev := log.EventBegin(ctx, "connDialReusePort", logdial) if nconn, retry, reuseErr := reuseDial(madialer.Dialer, laddr, raddr); reuseErr == nil { // if it worked, wrap the raw net.Conn with our manet.Conn logdial["reuseport"] = "success" rpev.Done() return manet.WrapNetConn(nconn) } else if !retry { // reuseDial is sure this is a legitimate dial failure, not a reuseport failure. logdial["reuseport"] = "failure" logdial["error"] = reuseErr rpev.Done() return nil, reuseErr } else { // this is a failure to reuse port. log it. logdial["reuseport"] = "retry" logdial["error"] = reuseErr rpev.Done() } } defer log.EventBegin(ctx, "connDialManet", logdial).Done() return madialer.Dial(raddr) }
// newConn constructs a new connection func newSingleConn(ctx context.Context, local, remote peer.ID, maconn manet.Conn) (Conn, error) { ml := lgbl.Dial("conn", local, remote, maconn.LocalMultiaddr(), maconn.RemoteMultiaddr()) conn := &singleConn{ local: local, remote: remote, maconn: maconn, msgrw: msgio.NewReadWriter(maconn), event: log.EventBegin(ctx, "connLifetime", ml), } log.Debugf("newSingleConn %p: %v to %v", conn, local, remote) return conn, nil }
// Dial connects to a peer over a particular address // Ensures raddr is part of peer.Addresses() // Example: d.DialAddr(ctx, peer.Addresses()[0], peer) func (d *Dialer) Dial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (Conn, error) { logdial := lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr) logdial["encrypted"] = (d.PrivateKey != nil) // log wether this will be an encrypted dial or not. defer log.EventBegin(ctx, "connDial", logdial).Done() var connOut Conn var errOut error done := make(chan struct{}) // do it async to ensure we respect don contexteone go func() { defer func() { select { case done <- struct{}{}: case <-ctx.Done(): } }() maconn, err := d.rawConnDial(ctx, raddr, remote) if err != nil { errOut = err return } if d.Wrapper != nil { maconn = d.Wrapper(maconn) } c, err := newSingleConn(ctx, d.LocalPeer, remote, maconn) if err != nil { maconn.Close() errOut = err return } if d.PrivateKey == nil || EncryptConnections == false { log.Warning("dialer %s dialing INSECURELY %s at %s!", d, remote, raddr) connOut = c return } c2, err := newSecureConn(ctx, d.PrivateKey, c) if err != nil { errOut = err c.Close() return } connOut = c2 }() select { case <-ctx.Done(): logdial["error"] = ctx.Err() logdial["dial"] = "failure" return nil, ctx.Err() case <-done: // whew, finished. } if errOut != nil { logdial["error"] = errOut logdial["dial"] = "failure" return nil, errOut } logdial["dial"] = "success" return connOut, nil }