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