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