github.com/phillinzzz/newBsc@v1.1.6/p2p/transport.go (about) 1 // Copyright 2015 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 "bytes" 21 "crypto/ecdsa" 22 "fmt" 23 "io" 24 "net" 25 "sync" 26 "time" 27 28 "github.com/phillinzzz/newBsc/common/bitutil" 29 "github.com/phillinzzz/newBsc/common/gopool" 30 "github.com/phillinzzz/newBsc/metrics" 31 "github.com/phillinzzz/newBsc/p2p/rlpx" 32 "github.com/phillinzzz/newBsc/rlp" 33 ) 34 35 const ( 36 // total timeout for encryption handshake and protocol 37 // handshake in both directions. 38 handshakeTimeout = 5 * time.Second 39 40 // This is the timeout for sending the disconnect reason. 41 // This is shorter than the usual timeout because we don't want 42 // to wait if the connection is known to be bad anyway. 43 discWriteTimeout = 1 * time.Second 44 ) 45 46 // rlpxTransport is the transport used by actual (non-test) connections. 47 // It wraps an RLPx connection with locks and read/write deadlines. 48 type rlpxTransport struct { 49 rmu, wmu sync.Mutex 50 wbuf bytes.Buffer 51 conn *rlpx.Conn 52 } 53 54 func newRLPX(conn net.Conn, dialDest *ecdsa.PublicKey) transport { 55 return &rlpxTransport{conn: rlpx.NewConn(conn, dialDest)} 56 } 57 58 func (t *rlpxTransport) ReadMsg() (Msg, error) { 59 t.rmu.Lock() 60 defer t.rmu.Unlock() 61 62 var msg Msg 63 t.conn.SetReadDeadline(time.Now().Add(frameReadTimeout)) 64 code, data, wireSize, err := t.conn.Read() 65 if err == nil { 66 msg = Msg{ 67 ReceivedAt: time.Now(), 68 Code: code, 69 Size: uint32(len(data)), 70 meterSize: uint32(wireSize), 71 Payload: bytes.NewReader(data), 72 } 73 } 74 return msg, err 75 } 76 77 func (t *rlpxTransport) WriteMsg(msg Msg) error { 78 t.wmu.Lock() 79 defer t.wmu.Unlock() 80 81 // Copy message data to write buffer. 82 t.wbuf.Reset() 83 if _, err := io.CopyN(&t.wbuf, msg.Payload, int64(msg.Size)); err != nil { 84 return err 85 } 86 87 // Write the message. 88 t.conn.SetWriteDeadline(time.Now().Add(frameWriteTimeout)) 89 size, err := t.conn.Write(msg.Code, t.wbuf.Bytes()) 90 if err != nil { 91 return err 92 } 93 94 // Set metrics. 95 msg.meterSize = size 96 if metrics.Enabled && msg.meterCap.Name != "" { // don't meter non-subprotocol messages 97 m := fmt.Sprintf("%s/%s/%d/%#02x", egressMeterName, msg.meterCap.Name, msg.meterCap.Version, msg.meterCode) 98 metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) 99 metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) 100 } 101 return nil 102 } 103 104 func (t *rlpxTransport) close(err error) { 105 t.wmu.Lock() 106 defer t.wmu.Unlock() 107 108 // Tell the remote end why we're disconnecting if possible. 109 // We only bother doing this if the underlying connection supports 110 // setting a timeout tough. 111 if t.conn != nil { 112 if r, ok := err.(DiscReason); ok && r != DiscNetworkError { 113 deadline := time.Now().Add(discWriteTimeout) 114 if err := t.conn.SetWriteDeadline(deadline); err == nil { 115 // Connection supports write deadline. 116 t.wbuf.Reset() 117 rlp.Encode(&t.wbuf, []DiscReason{r}) 118 t.conn.Write(discMsg, t.wbuf.Bytes()) 119 } 120 } 121 } 122 t.conn.Close() 123 } 124 125 func (t *rlpxTransport) doEncHandshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) { 126 t.conn.SetDeadline(time.Now().Add(handshakeTimeout)) 127 return t.conn.Handshake(prv) 128 } 129 130 func (t *rlpxTransport) doProtoHandshake(our *protoHandshake) (their *protoHandshake, err error) { 131 // Writing our handshake happens concurrently, we prefer 132 // returning the handshake read error. If the remote side 133 // disconnects us early with a valid reason, we should return it 134 // as the error so it can be tracked elsewhere. 135 werr := make(chan error, 1) 136 gopool.Submit(func() { werr <- Send(t, handshakeMsg, our) }) 137 if their, err = readProtocolHandshake(t); err != nil { 138 <-werr // make sure the write terminates too 139 return nil, err 140 } 141 if err := <-werr; err != nil { 142 return nil, fmt.Errorf("write error: %v", err) 143 } 144 // If the protocol version supports Snappy encoding, upgrade immediately 145 t.conn.SetSnappy(their.Version >= snappyProtocolVersion) 146 147 return their, nil 148 } 149 150 func readProtocolHandshake(rw MsgReader) (*protoHandshake, error) { 151 msg, err := rw.ReadMsg() 152 if err != nil { 153 return nil, err 154 } 155 if msg.Size > baseProtocolMaxMsgSize { 156 return nil, fmt.Errorf("message too big") 157 } 158 if msg.Code == discMsg { 159 // Disconnect before protocol handshake is valid according to the 160 // spec and we send it ourself if the post-handshake checks fail. 161 // We can't return the reason directly, though, because it is echoed 162 // back otherwise. Wrap it in a string instead. 163 var reason [1]DiscReason 164 rlp.Decode(msg.Payload, &reason) 165 return nil, reason[0] 166 } 167 if msg.Code != handshakeMsg { 168 return nil, fmt.Errorf("expected handshake, got %x", msg.Code) 169 } 170 var hs protoHandshake 171 if err := msg.Decode(&hs); err != nil { 172 return nil, err 173 } 174 if len(hs.ID) != 64 || !bitutil.TestBytes(hs.ID) { 175 return nil, DiscInvalidIdentity 176 } 177 return &hs, nil 178 }