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