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  }