github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/p2p/peer.go (about) 1 // Copyright 2014 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package p2p 18 19 import ( 20 "errors" 21 "fmt" 22 "io" 23 "net" 24 "sort" 25 "sync" 26 "time" 27 28 "github.com/ethereum/go-ethereum/logger" 29 "github.com/ethereum/go-ethereum/logger/glog" 30 "github.com/ethereum/go-ethereum/p2p/discover" 31 "github.com/ethereum/go-ethereum/rlp" 32 ) 33 34 const ( 35 baseProtocolVersion = 4 36 baseProtocolLength = uint64(16) 37 baseProtocolMaxMsgSize = 2 * 1024 38 39 pingInterval = 15 * time.Second 40 ) 41 42 const ( 43 // devp2p message codes 44 handshakeMsg = 0x00 45 discMsg = 0x01 46 pingMsg = 0x02 47 pongMsg = 0x03 48 getPeersMsg = 0x04 49 peersMsg = 0x05 50 ) 51 52 // protoHandshake is the RLP structure of the protocol handshake. 53 type protoHandshake struct { 54 Version uint64 55 Name string 56 Caps []Cap 57 ListenPort uint64 58 ID discover.NodeID 59 60 // Ignore additional fields (for forward compatibility). 61 Rest []rlp.RawValue `rlp:"tail"` 62 } 63 64 // Peer represents a connected remote node. 65 type Peer struct { 66 rw *conn 67 running map[string]*protoRW 68 69 wg sync.WaitGroup 70 protoErr chan error 71 closed chan struct{} 72 disc chan DiscReason 73 } 74 75 // NewPeer returns a peer for testing purposes. 76 func NewPeer(id discover.NodeID, name string, caps []Cap) *Peer { 77 pipe, _ := net.Pipe() 78 conn := &conn{fd: pipe, transport: nil, id: id, caps: caps, name: name} 79 peer := newPeer(conn, nil) 80 close(peer.closed) // ensures Disconnect doesn't block 81 return peer 82 } 83 84 // ID returns the node's public key. 85 func (p *Peer) ID() discover.NodeID { 86 return p.rw.id 87 } 88 89 // Name returns the node name that the remote node advertised. 90 func (p *Peer) Name() string { 91 return p.rw.name 92 } 93 94 // Caps returns the capabilities (supported subprotocols) of the remote peer. 95 func (p *Peer) Caps() []Cap { 96 // TODO: maybe return copy 97 return p.rw.caps 98 } 99 100 // RemoteAddr returns the remote address of the network connection. 101 func (p *Peer) RemoteAddr() net.Addr { 102 return p.rw.fd.RemoteAddr() 103 } 104 105 // LocalAddr returns the local address of the network connection. 106 func (p *Peer) LocalAddr() net.Addr { 107 return p.rw.fd.LocalAddr() 108 } 109 110 // Disconnect terminates the peer connection with the given reason. 111 // It returns immediately and does not wait until the connection is closed. 112 func (p *Peer) Disconnect(reason DiscReason) { 113 select { 114 case p.disc <- reason: 115 case <-p.closed: 116 } 117 } 118 119 // String implements fmt.Stringer. 120 func (p *Peer) String() string { 121 return fmt.Sprintf("Peer %x %v", p.rw.id[:8], p.RemoteAddr()) 122 } 123 124 func newPeer(conn *conn, protocols []Protocol) *Peer { 125 protomap := matchProtocols(protocols, conn.caps, conn) 126 p := &Peer{ 127 rw: conn, 128 running: protomap, 129 disc: make(chan DiscReason), 130 protoErr: make(chan error, len(protomap)+1), // protocols + pingLoop 131 closed: make(chan struct{}), 132 } 133 return p 134 } 135 136 func (p *Peer) run() DiscReason { 137 var ( 138 writeStart = make(chan struct{}, 1) 139 writeErr = make(chan error, 1) 140 readErr = make(chan error, 1) 141 reason DiscReason 142 requested bool 143 ) 144 p.wg.Add(2) 145 go p.readLoop(readErr) 146 go p.pingLoop() 147 148 // Start all protocol handlers. 149 writeStart <- struct{}{} 150 p.startProtocols(writeStart, writeErr) 151 152 // Wait for an error or disconnect. 153 loop: 154 for { 155 select { 156 case err := <-writeErr: 157 // A write finished. Allow the next write to start if 158 // there was no error. 159 if err != nil { 160 glog.V(logger.Detail).Infof("%v: write error: %v\n", p, err) 161 reason = DiscNetworkError 162 break loop 163 } 164 writeStart <- struct{}{} 165 case err := <-readErr: 166 if r, ok := err.(DiscReason); ok { 167 glog.V(logger.Debug).Infof("%v: remote requested disconnect: %v\n", p, r) 168 requested = true 169 reason = r 170 } else { 171 glog.V(logger.Detail).Infof("%v: read error: %v\n", p, err) 172 reason = DiscNetworkError 173 } 174 break loop 175 case err := <-p.protoErr: 176 reason = discReasonForError(err) 177 glog.V(logger.Debug).Infof("%v: protocol error: %v (%v)\n", p, err, reason) 178 break loop 179 case reason = <-p.disc: 180 glog.V(logger.Debug).Infof("%v: locally requested disconnect: %v\n", p, reason) 181 break loop 182 } 183 } 184 185 close(p.closed) 186 p.rw.close(reason) 187 p.wg.Wait() 188 if requested { 189 reason = DiscRequested 190 } 191 return reason 192 } 193 194 func (p *Peer) pingLoop() { 195 ping := time.NewTicker(pingInterval) 196 defer p.wg.Done() 197 defer ping.Stop() 198 for { 199 select { 200 case <-ping.C: 201 if err := SendItems(p.rw, pingMsg); err != nil { 202 p.protoErr <- err 203 return 204 } 205 case <-p.closed: 206 return 207 } 208 } 209 } 210 211 func (p *Peer) readLoop(errc chan<- error) { 212 defer p.wg.Done() 213 for { 214 msg, err := p.rw.ReadMsg() 215 if err != nil { 216 errc <- err 217 return 218 } 219 msg.ReceivedAt = time.Now() 220 if err = p.handle(msg); err != nil { 221 errc <- err 222 return 223 } 224 } 225 } 226 227 func (p *Peer) handle(msg Msg) error { 228 switch { 229 case msg.Code == pingMsg: 230 msg.Discard() 231 go SendItems(p.rw, pongMsg) 232 case msg.Code == discMsg: 233 var reason [1]DiscReason 234 // This is the last message. We don't need to discard or 235 // check errors because, the connection will be closed after it. 236 rlp.Decode(msg.Payload, &reason) 237 return reason[0] 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 select { 248 case proto.in <- msg: 249 return nil 250 case <-p.closed: 251 return io.EOF 252 } 253 } 254 return nil 255 } 256 257 func countMatchingProtocols(protocols []Protocol, caps []Cap) int { 258 n := 0 259 for _, cap := range caps { 260 for _, proto := range protocols { 261 if proto.Name == cap.Name && proto.Version == cap.Version { 262 n++ 263 } 264 } 265 } 266 return n 267 } 268 269 // matchProtocols creates structures for matching named subprotocols. 270 func matchProtocols(protocols []Protocol, caps []Cap, rw MsgReadWriter) map[string]*protoRW { 271 sort.Sort(capsByNameAndVersion(caps)) 272 offset := baseProtocolLength 273 result := make(map[string]*protoRW) 274 275 outer: 276 for _, cap := range caps { 277 for _, proto := range protocols { 278 if proto.Name == cap.Name && proto.Version == cap.Version { 279 // If an old protocol version matched, revert it 280 if old := result[cap.Name]; old != nil { 281 offset -= old.Length 282 } 283 // Assign the new match 284 result[cap.Name] = &protoRW{Protocol: proto, offset: offset, in: make(chan Msg), w: rw} 285 offset += proto.Length 286 287 continue outer 288 } 289 } 290 } 291 return result 292 } 293 294 func (p *Peer) startProtocols(writeStart <-chan struct{}, writeErr chan<- error) { 295 p.wg.Add(len(p.running)) 296 for _, proto := range p.running { 297 proto := proto 298 proto.closed = p.closed 299 proto.wstart = writeStart 300 proto.werr = writeErr 301 glog.V(logger.Detail).Infof("%v: Starting protocol %s/%d\n", p, proto.Name, proto.Version) 302 go func() { 303 err := proto.Run(p, proto) 304 if err == nil { 305 glog.V(logger.Detail).Infof("%v: Protocol %s/%d returned\n", p, proto.Name, proto.Version) 306 err = errors.New("protocol returned") 307 } else if err != io.EOF { 308 glog.V(logger.Detail).Infof("%v: Protocol %s/%d error: %v\n", p, proto.Name, proto.Version, err) 309 } 310 p.protoErr <- err 311 p.wg.Done() 312 }() 313 } 314 } 315 316 // getProto finds the protocol responsible for handling 317 // the given message code. 318 func (p *Peer) getProto(code uint64) (*protoRW, error) { 319 for _, proto := range p.running { 320 if code >= proto.offset && code < proto.offset+proto.Length { 321 return proto, nil 322 } 323 } 324 return nil, newPeerError(errInvalidMsgCode, "%d", code) 325 } 326 327 type protoRW struct { 328 Protocol 329 in chan Msg // receices read messages 330 closed <-chan struct{} // receives when peer is shutting down 331 wstart <-chan struct{} // receives when write may start 332 werr chan<- error // for write results 333 offset uint64 334 w MsgWriter 335 } 336 337 func (rw *protoRW) WriteMsg(msg Msg) (err error) { 338 if msg.Code >= rw.Length { 339 return newPeerError(errInvalidMsgCode, "not handled") 340 } 341 msg.Code += rw.offset 342 select { 343 case <-rw.wstart: 344 err = rw.w.WriteMsg(msg) 345 // Report write status back to Peer.run. It will initiate 346 // shutdown if the error is non-nil and unblock the next write 347 // otherwise. The calling protocol code should exit for errors 348 // as well but we don't want to rely on that. 349 rw.werr <- err 350 case <-rw.closed: 351 err = fmt.Errorf("shutting down") 352 } 353 return err 354 } 355 356 func (rw *protoRW) ReadMsg() (Msg, error) { 357 select { 358 case msg := <-rw.in: 359 msg.Code -= rw.offset 360 return msg, nil 361 case <-rw.closed: 362 return Msg{}, io.EOF 363 } 364 } 365 366 // PeerInfo represents a short summary of the information known about a connected 367 // peer. Sub-protocol independent fields are contained and initialized here, with 368 // protocol specifics delegated to all connected sub-protocols. 369 type PeerInfo struct { 370 ID string `json:"id"` // Unique node identifier (also the encryption key) 371 Name string `json:"name"` // Name of the node, including client type, version, OS, custom data 372 Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer 373 Network struct { 374 LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection 375 RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection 376 } `json:"network"` 377 Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields 378 } 379 380 // Info gathers and returns a collection of metadata known about a peer. 381 func (p *Peer) Info() *PeerInfo { 382 // Gather the protocol capabilities 383 var caps []string 384 for _, cap := range p.Caps() { 385 caps = append(caps, cap.String()) 386 } 387 // Assemble the generic peer metadata 388 info := &PeerInfo{ 389 ID: p.ID().String(), 390 Name: p.Name(), 391 Caps: caps, 392 Protocols: make(map[string]interface{}), 393 } 394 info.Network.LocalAddress = p.LocalAddr().String() 395 info.Network.RemoteAddress = p.RemoteAddr().String() 396 397 // Gather all the running protocol infos 398 for _, proto := range p.running { 399 protoInfo := interface{}("unknown") 400 if query := proto.Protocol.PeerInfo; query != nil { 401 if metadata := query(p.ID()); metadata != nil { 402 protoInfo = metadata 403 } else { 404 protoInfo = "handshake" 405 } 406 } 407 info.Protocols[proto.Name] = protoInfo 408 } 409 return info 410 }