github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/p2p/peer.go (about) 1 package p2p 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "net" 9 "sort" 10 "sync" 11 "time" 12 13 "github.com/jonasnick/go-ethereum/logger" 14 "github.com/jonasnick/go-ethereum/p2p/discover" 15 "github.com/jonasnick/go-ethereum/rlp" 16 ) 17 18 const ( 19 baseProtocolVersion = 3 20 baseProtocolLength = uint64(16) 21 baseProtocolMaxMsgSize = 10 * 1024 * 1024 22 23 disconnectGracePeriod = 2 * time.Second 24 ) 25 26 const ( 27 // devp2p message codes 28 handshakeMsg = 0x00 29 discMsg = 0x01 30 pingMsg = 0x02 31 pongMsg = 0x03 32 getPeersMsg = 0x04 33 peersMsg = 0x05 34 ) 35 36 // handshake is the RLP structure of the protocol handshake. 37 type handshake struct { 38 Version uint64 39 Name string 40 Caps []Cap 41 ListenPort uint64 42 NodeID discover.NodeID 43 } 44 45 // Peer represents a connected remote node. 46 type Peer struct { 47 // Peers have all the log methods. 48 // Use them to display messages related to the peer. 49 *logger.Logger 50 51 infoMu sync.Mutex 52 name string 53 caps []Cap 54 55 ourID, remoteID *discover.NodeID 56 ourName string 57 58 rw *frameRW 59 60 // These fields maintain the running protocols. 61 protocols []Protocol 62 runlock sync.RWMutex // protects running 63 running map[string]*proto 64 65 // disables protocol handshake, for testing 66 noHandshake bool 67 68 protoWG sync.WaitGroup 69 protoErr chan error 70 closed chan struct{} 71 disc chan DiscReason 72 } 73 74 // NewPeer returns a peer for testing purposes. 75 func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { 76 conn, _ := net.Pipe() 77 peer := newPeer(conn, nil, "", nil, &id) 78 peer.setHandshakeInfo(name, caps) 79 close(peer.closed) // ensures Disconnect doesn't block 80 return peer 81 } 82 83 // ID returns the node's public key. 84 func (p *Peer) ID() discover.NodeID { 85 return *p.remoteID 86 } 87 88 // Name returns the node name that the remote node advertised. 89 func (p *Peer) Name() string { 90 // this needs a lock because the information is part of the 91 // protocol handshake. 92 p.infoMu.Lock() 93 name := p.name 94 p.infoMu.Unlock() 95 return name 96 } 97 98 // Caps returns the capabilities (supported subprotocols) of the remote peer. 99 func (p *Peer) Caps() []Cap { 100 // this needs a lock because the information is part of the 101 // protocol handshake. 102 p.infoMu.Lock() 103 caps := p.caps 104 p.infoMu.Unlock() 105 return caps 106 } 107 108 // RemoteAddr returns the remote address of the network connection. 109 func (p *Peer) RemoteAddr() net.Addr { 110 return p.rw.RemoteAddr() 111 } 112 113 // LocalAddr returns the local address of the network connection. 114 func (p *Peer) LocalAddr() net.Addr { 115 return p.rw.LocalAddr() 116 } 117 118 // Disconnect terminates the peer connection with the given reason. 119 // It returns immediately and does not wait until the connection is closed. 120 func (p *Peer) Disconnect(reason DiscReason) { 121 select { 122 case p.disc <- reason: 123 case <-p.closed: 124 } 125 } 126 127 // String implements fmt.Stringer. 128 func (p *Peer) String() string { 129 return fmt.Sprintf("Peer %.8x %v", p.remoteID[:], p.RemoteAddr()) 130 } 131 132 func newPeer(conn net.Conn, protocols []Protocol, ourName string, ourID, remoteID *discover.NodeID) *Peer { 133 logtag := fmt.Sprintf("Peer %.8x %v", remoteID[:], conn.RemoteAddr()) 134 return &Peer{ 135 Logger: logger.NewLogger(logtag), 136 rw: newFrameRW(conn, msgWriteTimeout), 137 ourID: ourID, 138 ourName: ourName, 139 remoteID: remoteID, 140 protocols: protocols, 141 running: make(map[string]*proto), 142 disc: make(chan DiscReason), 143 protoErr: make(chan error), 144 closed: make(chan struct{}), 145 } 146 } 147 148 func (p *Peer) setHandshakeInfo(name string, caps []Cap) { 149 p.infoMu.Lock() 150 p.name = name 151 p.caps = caps 152 p.infoMu.Unlock() 153 } 154 155 func (p *Peer) run() DiscReason { 156 var readErr = make(chan error, 1) 157 defer p.closeProtocols() 158 defer close(p.closed) 159 160 go func() { readErr <- p.readLoop() }() 161 162 if !p.noHandshake { 163 if err := writeProtocolHandshake(p.rw, p.ourName, *p.ourID, p.protocols); err != nil { 164 p.DebugDetailf("Protocol handshake error: %v\n", err) 165 p.rw.Close() 166 return DiscProtocolError 167 } 168 } 169 170 // Wait for an error or disconnect. 171 var reason DiscReason 172 select { 173 case err := <-readErr: 174 // We rely on protocols to abort if there is a write error. It 175 // might be more robust to handle them here as well. 176 p.DebugDetailf("Read error: %v\n", err) 177 p.rw.Close() 178 return DiscNetworkError 179 180 case err := <-p.protoErr: 181 reason = discReasonForError(err) 182 case reason = <-p.disc: 183 } 184 p.politeDisconnect(reason) 185 186 // Wait for readLoop. It will end because conn is now closed. 187 <-readErr 188 p.Debugf("Disconnected: %v\n", reason) 189 return reason 190 } 191 192 func (p *Peer) politeDisconnect(reason DiscReason) { 193 done := make(chan struct{}) 194 go func() { 195 EncodeMsg(p.rw, discMsg, uint(reason)) 196 // Wait for the other side to close the connection. 197 // Discard any data that they send until then. 198 io.Copy(ioutil.Discard, p.rw) 199 close(done) 200 }() 201 select { 202 case <-done: 203 case <-time.After(disconnectGracePeriod): 204 } 205 p.rw.Close() 206 } 207 208 func (p *Peer) readLoop() error { 209 if !p.noHandshake { 210 if err := readProtocolHandshake(p, p.rw); err != nil { 211 return err 212 } 213 } 214 for { 215 msg, err := p.rw.ReadMsg() 216 if err != nil { 217 return err 218 } 219 if err = p.handle(msg); err != nil { 220 return err 221 } 222 } 223 return nil 224 } 225 226 func (p *Peer) handle(msg Msg) error { 227 switch { 228 case msg.Code == pingMsg: 229 msg.Discard() 230 go EncodeMsg(p.rw, pongMsg) 231 case msg.Code == discMsg: 232 var reason DiscReason 233 // no need to discard or for error checking, we'll close the 234 // connection after this. 235 rlp.Decode(msg.Payload, &reason) 236 p.Disconnect(DiscRequested) 237 return discRequestedError(reason) 238 case msg.Code < baseProtocolLength: 239 // ignore other base protocol messages 240 return msg.Discard() 241 default: 242 // it's a subprotocol message 243 proto, err := p.getProto(msg.Code) 244 if err != nil { 245 return fmt.Errorf("msg code out of range: %v", msg.Code) 246 } 247 proto.in <- msg 248 } 249 return nil 250 } 251 252 func readProtocolHandshake(p *Peer, rw MsgReadWriter) error { 253 // read and handle remote handshake 254 msg, err := rw.ReadMsg() 255 if err != nil { 256 return err 257 } 258 if msg.Code == discMsg { 259 // disconnect before protocol handshake is valid according to the 260 // spec and we send it ourself if Server.addPeer fails. 261 var reason DiscReason 262 rlp.Decode(msg.Payload, &reason) 263 return discRequestedError(reason) 264 } 265 if msg.Code != handshakeMsg { 266 return newPeerError(errProtocolBreach, "expected handshake, got %x", msg.Code) 267 } 268 if msg.Size > baseProtocolMaxMsgSize { 269 return newPeerError(errInvalidMsg, "message too big") 270 } 271 var hs handshake 272 if err := msg.Decode(&hs); err != nil { 273 return err 274 } 275 // validate handshake info 276 if hs.Version != baseProtocolVersion { 277 return newPeerError(errP2PVersionMismatch, "required version %d, received %d\n", 278 baseProtocolVersion, hs.Version) 279 } 280 if hs.NodeID == *p.remoteID { 281 return newPeerError(errPubkeyForbidden, "node ID mismatch") 282 } 283 // TODO: remove Caps with empty name 284 p.setHandshakeInfo(hs.Name, hs.Caps) 285 p.startSubprotocols(hs.Caps) 286 return nil 287 } 288 289 func writeProtocolHandshake(w MsgWriter, name string, id discover.NodeID, ps []Protocol) error { 290 var caps []interface{} 291 for _, proto := range ps { 292 caps = append(caps, proto.cap()) 293 } 294 return EncodeMsg(w, handshakeMsg, baseProtocolVersion, name, caps, 0, id) 295 } 296 297 // startProtocols starts matching named subprotocols. 298 func (p *Peer) startSubprotocols(caps []Cap) { 299 sort.Sort(capsByName(caps)) 300 p.runlock.Lock() 301 defer p.runlock.Unlock() 302 offset := baseProtocolLength 303 outer: 304 for _, cap := range caps { 305 for _, proto := range p.protocols { 306 if proto.Name == cap.Name && 307 proto.Version == cap.Version && 308 p.running[cap.Name] == nil { 309 p.running[cap.Name] = p.startProto(offset, proto) 310 offset += proto.Length 311 continue outer 312 } 313 } 314 } 315 } 316 317 func (p *Peer) startProto(offset uint64, impl Protocol) *proto { 318 p.DebugDetailf("Starting protocol %s/%d\n", impl.Name, impl.Version) 319 rw := &proto{ 320 name: impl.Name, 321 in: make(chan Msg), 322 offset: offset, 323 maxcode: impl.Length, 324 w: p.rw, 325 } 326 p.protoWG.Add(1) 327 go func() { 328 err := impl.Run(p, rw) 329 if err == nil { 330 p.DebugDetailf("Protocol %s/%d returned\n", impl.Name, impl.Version) 331 err = errors.New("protocol returned") 332 } else { 333 p.DebugDetailf("Protocol %s/%d error: %v\n", impl.Name, impl.Version, err) 334 } 335 select { 336 case p.protoErr <- err: 337 case <-p.closed: 338 } 339 p.protoWG.Done() 340 }() 341 return rw 342 } 343 344 // getProto finds the protocol responsible for handling 345 // the given message code. 346 func (p *Peer) getProto(code uint64) (*proto, error) { 347 p.runlock.RLock() 348 defer p.runlock.RUnlock() 349 for _, proto := range p.running { 350 if code >= proto.offset && code < proto.offset+proto.maxcode { 351 return proto, nil 352 } 353 } 354 return nil, newPeerError(errInvalidMsgCode, "%d", code) 355 } 356 357 func (p *Peer) closeProtocols() { 358 p.runlock.RLock() 359 for _, p := range p.running { 360 close(p.in) 361 } 362 p.runlock.RUnlock() 363 p.protoWG.Wait() 364 } 365 366 // writeProtoMsg sends the given message on behalf of the given named protocol. 367 // this exists because of Server.Broadcast. 368 func (p *Peer) writeProtoMsg(protoName string, msg Msg) error { 369 p.runlock.RLock() 370 proto, ok := p.running[protoName] 371 p.runlock.RUnlock() 372 if !ok { 373 return fmt.Errorf("protocol %s not handled by peer", protoName) 374 } 375 if msg.Code >= proto.maxcode { 376 return newPeerError(errInvalidMsgCode, "code %x is out of range for protocol %q", msg.Code, protoName) 377 } 378 msg.Code += proto.offset 379 return p.rw.WriteMsg(msg) 380 } 381 382 type proto struct { 383 name string 384 in chan Msg 385 maxcode, offset uint64 386 w MsgWriter 387 } 388 389 func (rw *proto) WriteMsg(msg Msg) error { 390 if msg.Code >= rw.maxcode { 391 return newPeerError(errInvalidMsgCode, "not handled") 392 } 393 msg.Code += rw.offset 394 return rw.w.WriteMsg(msg) 395 } 396 397 func (rw *proto) ReadMsg() (Msg, error) { 398 msg, ok := <-rw.in 399 if !ok { 400 return msg, io.EOF 401 } 402 msg.Code -= rw.offset 403 return msg, nil 404 }