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 }