github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/p2p/wireformat.go (about) 1 /* 2 * Copyright (C) 2020 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU 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 * This program 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 General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package p2p 19 20 import ( 21 "bufio" 22 "bytes" 23 "encoding/binary" 24 "errors" 25 "fmt" 26 "io" 27 "net/textproto" 28 "strconv" 29 30 "github.com/rs/zerolog/log" 31 "google.golang.org/protobuf/proto" 32 33 "github.com/mysteriumnetwork/node/p2p/compat" 34 "github.com/mysteriumnetwork/node/pb" 35 ) 36 37 const maxTransportMsgLen = 128 * 1024 38 39 func init() { 40 // This is needed to initialize global common headers map state internally 41 // before reading header values so race detector doesn't fail unit tests 42 // when multiple goroutines reads conn data headers. 43 textproto.CanonicalMIMEHeaderKey("") 44 } 45 46 type wireReader interface { 47 readMsg(*transportMsg) error 48 } 49 50 type wireWriter interface { 51 writeMsg(*transportMsg) error 52 } 53 54 const ( 55 headerFieldRequestID = "Request-ID" 56 headerFieldTopic = "Topic" 57 headerStatusCode = "Status-Code" 58 headerMsg = "Message" 59 ) 60 61 func newCompatibleWireReader(c io.Reader, peerCompatibility int) wireReader { 62 if compat.FeaturePBP2P(peerCompatibility) { 63 log.Debug().Msg("Using protobufWireReader") 64 return newProtobufWireReader(c) 65 } 66 log.Debug().Msg("Using textWireReader") 67 return newTextWireReader(c) 68 } 69 70 func newCompatibleWireWriter(c io.Writer, peerCompatibility int) wireWriter { 71 if compat.FeaturePBP2P(peerCompatibility) { 72 log.Debug().Msg("Using protobufWireWriter") 73 return newProtobufWireWriter(c) 74 } 75 log.Debug().Msg("Using textWireWriter") 76 return newTextWireWriter(c) 77 } 78 79 type textWireReader textproto.Reader 80 81 type textWireWriter textproto.Writer 82 83 func newTextWireReader(c io.Reader) *textWireReader { 84 return (*textWireReader)(textproto.NewReader(bufio.NewReader(c))) 85 } 86 87 func (r *textWireReader) readMsg(m *transportMsg) error { 88 // Read header. 89 header, err := (*textproto.Reader)(r).ReadMIMEHeader() 90 if err != nil { 91 return fmt.Errorf("could not read mime header: %w", err) 92 } 93 id, err := strconv.ParseUint(header.Get(headerFieldRequestID), 10, 64) 94 if err != nil { 95 return fmt.Errorf("could not parse request id: %w", err) 96 } 97 m.id = id 98 statusCode, err := strconv.ParseUint(header.Get(headerStatusCode), 10, 64) 99 if err != nil { 100 return fmt.Errorf("could not parse status code: %w", err) 101 } 102 m.statusCode = statusCode 103 m.topic = header.Get(headerFieldTopic) 104 m.msg = header.Get(headerMsg) 105 106 // Read data. 107 data, err := (*textproto.Reader)(r).ReadDotBytes() 108 if err != nil { 109 return fmt.Errorf("could not read dot bytes: %w", err) 110 } 111 if len(data) > 0 { 112 m.data = data[:len(data)-1] 113 } 114 return nil 115 } 116 117 func newTextWireWriter(c io.Writer) *textWireWriter { 118 return (*textWireWriter)(textproto.NewWriter(bufio.NewWriter(c))) 119 } 120 121 func (w *textWireWriter) writeMsg(m *transportMsg) error { 122 dotWriter := (*textproto.Writer)(w).DotWriter() 123 var header bytes.Buffer 124 header.WriteString(fmt.Sprintf("%s:%d\r\n", headerFieldRequestID, m.id)) 125 header.WriteString(fmt.Sprintf("%s:%s\r\n", headerFieldTopic, m.topic)) 126 header.WriteString(fmt.Sprintf("%s:%d\r\n", headerStatusCode, m.statusCode)) 127 header.WriteString(fmt.Sprintf("%s:%s\r\n", headerMsg, m.msg)) 128 header.WriteByte('\n') 129 dotWriter.Write(header.Bytes()) 130 dotWriter.Write(m.data) 131 return dotWriter.Close() 132 } 133 134 type protobufWireReader struct { 135 r *bufio.Reader 136 closed bool 137 } 138 139 func newProtobufWireReader(c io.Reader) *protobufWireReader { 140 return &protobufWireReader{ 141 r: bufio.NewReader(c), 142 } 143 } 144 145 func (r *protobufWireReader) readMsg(m *transportMsg) error { 146 if r.closed { 147 return io.EOF 148 } 149 150 msgLen, err := binary.ReadUvarint(r.r) 151 if err != nil { 152 r.closed = true 153 return err 154 } 155 156 if msgLen > maxTransportMsgLen { 157 r.closed = true 158 return io.EOF 159 } 160 161 msgBytes := make([]byte, msgLen) 162 _, err = io.ReadFull(r.r, msgBytes) 163 if err != nil { 164 r.closed = true 165 return err 166 } 167 168 var pbMsg pb.P2PChannelEnvelope 169 err = proto.Unmarshal(msgBytes, &pbMsg) 170 if err != nil { 171 return err 172 } 173 174 m.id = pbMsg.ID 175 m.statusCode = pbMsg.StatusCode 176 m.topic = pbMsg.Topic 177 m.msg = pbMsg.Msg 178 m.data = pbMsg.Data 179 180 return nil 181 } 182 183 type protobufWireWriter struct { 184 w *bufio.Writer 185 } 186 187 func newProtobufWireWriter(c io.Writer) *protobufWireWriter { 188 return &protobufWireWriter{ 189 w: bufio.NewWriter(c), 190 } 191 } 192 193 func (w *protobufWireWriter) writeMsg(m *transportMsg) error { 194 pbMsg := pb.P2PChannelEnvelope{ 195 ID: m.id, 196 StatusCode: m.statusCode, 197 Topic: m.topic, 198 Msg: m.msg, 199 Data: m.data, 200 } 201 202 msgBytes, err := proto.Marshal(&pbMsg) 203 if err != nil { 204 return err 205 } 206 207 msgLen := len(msgBytes) 208 if msgLen > maxTransportMsgLen { 209 return errors.New("can't marshal: message too long") 210 } 211 212 lenBuf := make([]byte, binary.MaxVarintLen64) 213 lenBufLen := binary.PutUvarint(lenBuf, uint64(msgLen)) 214 lenBuf = lenBuf[:lenBufLen] 215 216 _, err = w.w.Write(lenBuf) 217 if err != nil { 218 return err 219 } 220 221 _, err = w.w.Write(msgBytes) 222 if err != nil { 223 return err 224 } 225 226 err = w.w.Flush() 227 if err != nil { 228 return err 229 } 230 231 return nil 232 }