github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/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/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/common/bitutil" 30 "github.com/ethereum/go-ethereum/metrics" 31 "github.com/ethereum/go-ethereum/p2p/rlpx" 32 "github.com/ethereum/go-ethereum/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 // Protocol messages are dispatched to subprotocol handlers asynchronously, 67 // but package rlpx may reuse the returned 'data' buffer on the next call 68 // to Read. Copy the message data to avoid this being an issue. 69 data = common.CopyBytes(data) 70 msg = Msg{ 71 ReceivedAt: time.Now(), 72 Code: code, 73 Size: uint32(len(data)), 74 meterSize: uint32(wireSize), 75 Payload: bytes.NewReader(data), 76 } 77 } 78 return msg, err 79 } 80 81 func (t *rlpxTransport) WriteMsg(msg Msg) error { 82 t.wmu.Lock() 83 defer t.wmu.Unlock() 84 85 // Copy message data to write buffer. 86 t.wbuf.Reset() 87 if _, err := io.CopyN(&t.wbuf, msg.Payload, int64(msg.Size)); err != nil { 88 return err 89 } 90 91 // Write the message. 92 t.conn.SetWriteDeadline(time.Now().Add(frameWriteTimeout)) 93 size, err := t.conn.Write(msg.Code, t.wbuf.Bytes()) 94 if err != nil { 95 return err 96 } 97 98 // Set metrics. 99 msg.meterSize = size 100 if metrics.Enabled && msg.meterCap.Name != "" { // don't meter non-subprotocol messages 101 m := fmt.Sprintf("%s/%s/%d/%#02x", egressMeterName, msg.meterCap.Name, msg.meterCap.Version, msg.meterCode) 102 metrics.GetOrRegisterMeter(m, nil).Mark(int64(msg.meterSize)) 103 metrics.GetOrRegisterMeter(m+"/packets", nil).Mark(1) 104 } 105 return nil 106 } 107 108 func (t *rlpxTransport) close(err error) { 109 t.wmu.Lock() 110 defer t.wmu.Unlock() 111 112 // Tell the remote end why we're disconnecting if possible. 113 // We only bother doing this if the underlying connection supports 114 // setting a timeout tough. 115 if t.conn != nil { 116 if r, ok := err.(DiscReason); ok && r != DiscNetworkError { 117 deadline := time.Now().Add(discWriteTimeout) 118 if err := t.conn.SetWriteDeadline(deadline); err == nil { 119 // Connection supports write deadline. 120 t.wbuf.Reset() 121 rlp.Encode(&t.wbuf, []DiscReason{r}) 122 t.conn.Write(discMsg, t.wbuf.Bytes()) 123 } 124 } 125 } 126 t.conn.Close() 127 } 128 129 func (t *rlpxTransport) doEncHandshake(prv *ecdsa.PrivateKey) (*ecdsa.PublicKey, error) { 130 t.conn.SetDeadline(time.Now().Add(handshakeTimeout)) 131 return t.conn.Handshake(prv) 132 } 133 134 func (t *rlpxTransport) doProtoHandshake(our *protoHandshake) (their *protoHandshake, err error) { 135 // Writing our handshake happens concurrently, we prefer 136 // returning the handshake read error. If the remote side 137 // disconnects us early with a valid reason, we should return it 138 // as the error so it can be tracked elsewhere. 139 werr := make(chan error, 1) 140 go func() { werr <- Send(t, handshakeMsg, our) }() 141 if their, err = readProtocolHandshake(t); err != nil { 142 <-werr // make sure the write terminates too 143 return nil, err 144 } 145 if err := <-werr; err != nil { 146 return nil, fmt.Errorf("write error: %v", err) 147 } 148 // If the protocol version supports Snappy encoding, upgrade immediately 149 t.conn.SetSnappy(their.Version >= snappyProtocolVersion) 150 151 return their, nil 152 } 153 154 func readProtocolHandshake(rw MsgReader) (*protoHandshake, error) { 155 msg, err := rw.ReadMsg() 156 if err != nil { 157 return nil, err 158 } 159 if msg.Size > baseProtocolMaxMsgSize { 160 return nil, fmt.Errorf("message too big") 161 } 162 if msg.Code == discMsg { 163 // Disconnect before protocol handshake is valid according to the 164 // spec and we send it ourself if the post-handshake checks fail. 165 // We can't return the reason directly, though, because it is echoed 166 // back otherwise. Wrap it in a string instead. 167 var reason [1]DiscReason 168 rlp.Decode(msg.Payload, &reason) 169 return nil, reason[0] 170 } 171 if msg.Code != handshakeMsg { 172 return nil, fmt.Errorf("expected handshake, got %x", msg.Code) 173 } 174 var hs protoHandshake 175 if err := msg.Decode(&hs); err != nil { 176 return nil, err 177 } 178 if len(hs.ID) != 64 || !bitutil.TestBytes(hs.ID) { 179 return nil, DiscInvalidIdentity 180 } 181 return &hs, nil 182 }