2021-11-08 16:39:17 +00:00
|
|
|
package p2p
|
|
|
|
|
|
|
|
// this file implements CBOR codec to prevent from certain attacks
|
|
|
|
import "fmt"
|
|
|
|
import "io"
|
|
|
|
import "net"
|
2021-11-21 15:25:11 +00:00
|
|
|
import "sync"
|
|
|
|
import "time"
|
|
|
|
import "github.com/cenkalti/rpc2"
|
2021-11-08 16:39:17 +00:00
|
|
|
import "encoding/binary"
|
|
|
|
import "github.com/fxamacker/cbor/v2"
|
|
|
|
|
|
|
|
import "github.com/deroproject/derohe/config" // only used get constants such as max data per frame
|
|
|
|
|
2021-11-21 15:25:11 +00:00
|
|
|
// it processes both
|
|
|
|
type RequestResponse struct {
|
|
|
|
Method string `cbor:"M"` // format: "Service.Method"
|
|
|
|
Seq uint64 `cbor:"S"` // echoes that of the request
|
|
|
|
Error string `cbor:"E"` // error, if any.
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
2021-11-21 15:25:11 +00:00
|
|
|
const READ_TIMEOUT = 20 * time.Second
|
|
|
|
const WRITE_TIMEOUT = 20 * time.Second
|
2021-11-08 16:39:17 +00:00
|
|
|
|
|
|
|
// reads our data, length prefix blocks
|
2021-11-21 15:25:11 +00:00
|
|
|
func Read_Data_Frame(r net.Conn, obj interface{}) error {
|
2021-11-08 16:39:17 +00:00
|
|
|
var frame_length_buf [4]byte
|
|
|
|
|
|
|
|
//connection.set_timeout()
|
2021-11-21 15:25:11 +00:00
|
|
|
r.SetReadDeadline(time.Now().Add(READ_TIMEOUT))
|
2021-11-08 16:39:17 +00:00
|
|
|
nbyte, err := io.ReadFull(r, frame_length_buf[:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if nbyte != 4 {
|
|
|
|
return fmt.Errorf("needed 4 bytes, but got %d bytes", nbyte)
|
|
|
|
}
|
|
|
|
|
|
|
|
// time to ban
|
|
|
|
frame_length := binary.LittleEndian.Uint32(frame_length_buf[:])
|
|
|
|
if frame_length == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// most probably memory DDOS attack, kill the connection
|
|
|
|
if uint64(frame_length) > (5 * config.STARGATE_HE_MAX_BLOCK_SIZE) {
|
|
|
|
return fmt.Errorf("Frame length is too big Expected %d Actual %d", 5*config.STARGATE_HE_MAX_BLOCK_SIZE, frame_length)
|
|
|
|
}
|
|
|
|
data_buf := make([]byte, frame_length)
|
|
|
|
data_size, err := io.ReadFull(r, data_buf)
|
|
|
|
if err != nil || data_size <= 0 || uint32(data_size) != frame_length {
|
|
|
|
return fmt.Errorf("Could not read data size read %d, frame length %d err %s", data_size, frame_length, err)
|
|
|
|
}
|
|
|
|
data_buf = data_buf[:frame_length]
|
|
|
|
err = cbor.Unmarshal(data_buf, obj)
|
|
|
|
|
|
|
|
//fmt.Printf("Read object %+v raw %s\n",obj, data_buf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// reads our data, length prefix blocks
|
2021-11-21 15:25:11 +00:00
|
|
|
func Write_Data_Frame(w net.Conn, obj interface{}) error {
|
2021-11-08 16:39:17 +00:00
|
|
|
var frame_length_buf [4]byte
|
|
|
|
data_bytes, err := cbor.Marshal(obj)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
binary.LittleEndian.PutUint32(frame_length_buf[:], uint32(len(data_bytes)))
|
|
|
|
|
2021-11-21 15:25:11 +00:00
|
|
|
w.SetWriteDeadline(time.Now().Add(WRITE_TIMEOUT))
|
2021-11-08 16:39:17 +00:00
|
|
|
if _, err = w.Write(frame_length_buf[:]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
_, err = w.Write(data_bytes[:])
|
|
|
|
//fmt.Printf("Wrote object %+v raw %s\n",obj, data_bytes)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClientCodec implements the rpc.ClientCodec interface for generic golang objects.
|
|
|
|
type ClientCodec struct {
|
2021-11-21 15:25:11 +00:00
|
|
|
r net.Conn
|
|
|
|
sync.Mutex
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewClientCodec returns a ClientCodec for communicating with the ServerCodec
|
|
|
|
// on the other end of the conn.
|
2021-11-21 15:25:11 +00:00
|
|
|
// to support deadlines we use net.conn
|
|
|
|
func NewCBORCodec(conn net.Conn) *ClientCodec {
|
|
|
|
return &ClientCodec{r: conn}
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReadResponseHeader reads a 4 byte length from the connection and decodes that many
|
|
|
|
// subsequent bytes into the given object, decodes it, and stores the fields
|
|
|
|
// in the given request.
|
2021-11-21 15:25:11 +00:00
|
|
|
func (c *ClientCodec) ReadResponseHeader(resp *rpc2.Response) error {
|
|
|
|
var header RequestResponse
|
2021-11-08 16:39:17 +00:00
|
|
|
if err := Read_Data_Frame(c.r, &header); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-21 15:25:11 +00:00
|
|
|
//if header.Method == "" {
|
|
|
|
// return fmt.Errorf("header missing method: %s", "no Method")
|
|
|
|
//}
|
|
|
|
//resp.Method = header.Method
|
2021-11-08 16:39:17 +00:00
|
|
|
resp.Seq = header.Seq
|
|
|
|
resp.Error = header.Error
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close closes the underlying connection.
|
|
|
|
func (c *ClientCodec) Close() error {
|
2021-11-21 15:25:11 +00:00
|
|
|
return c.r.Close()
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReadRequestHeader reads the header (which is prefixed by a 4 byte lil endian length
|
|
|
|
// indicating its size) from the connection, decodes it, and stores the fields
|
|
|
|
// in the given request.
|
2021-11-21 15:25:11 +00:00
|
|
|
func (s *ClientCodec) ReadHeader(req *rpc2.Request, resp *rpc2.Response) error {
|
|
|
|
var header RequestResponse
|
2021-11-08 16:39:17 +00:00
|
|
|
if err := Read_Data_Frame(s.r, &header); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-11-21 15:25:11 +00:00
|
|
|
|
|
|
|
if header.Method != "" {
|
|
|
|
req.Seq = header.Seq
|
|
|
|
req.Method = header.Method
|
|
|
|
} else {
|
|
|
|
resp.Seq = header.Seq
|
|
|
|
resp.Error = header.Error
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadRequestBody reads a 4 byte length from the connection and decodes that many
|
|
|
|
// subsequent bytes into the object
|
2021-11-21 15:25:11 +00:00
|
|
|
func (s *ClientCodec) ReadRequestBody(obj interface{}) error {
|
2021-11-08 16:39:17 +00:00
|
|
|
if obj == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return Read_Data_Frame(s.r, obj)
|
|
|
|
}
|
|
|
|
|
2021-11-21 15:25:11 +00:00
|
|
|
// ReadResponseBody reads a 4 byte length from the connection and decodes that many
|
|
|
|
// subsequent bytes into the given object (which should be a pointer to a
|
|
|
|
// struct).
|
|
|
|
func (c *ClientCodec) ReadResponseBody(obj interface{}) error {
|
|
|
|
if obj == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return Read_Data_Frame(c.r, obj)
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteRequest writes the 4 byte length from the connection and encodes that many
|
|
|
|
// subsequent bytes into the given object.
|
|
|
|
func (c *ClientCodec) WriteRequest(req *rpc2.Request, obj interface{}) error {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
|
|
|
|
header := RequestResponse{Method: req.Method, Seq: req.Seq}
|
|
|
|
if err := Write_Data_Frame(c.r, header); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return Write_Data_Frame(c.r, obj)
|
|
|
|
}
|
|
|
|
|
2021-11-08 16:39:17 +00:00
|
|
|
// WriteResponse writes the appropriate header. If
|
|
|
|
// the response was invalid, the size of the body of the resp is reported as
|
|
|
|
// having size zero and is not sent.
|
2021-11-21 15:25:11 +00:00
|
|
|
func (c *ClientCodec) WriteResponse(resp *rpc2.Response, obj interface{}) error {
|
|
|
|
c.Lock()
|
|
|
|
defer c.Unlock()
|
|
|
|
header := RequestResponse{Seq: resp.Seq, Error: resp.Error}
|
|
|
|
if err := Write_Data_Frame(c.r, header); err != nil {
|
2021-11-08 16:39:17 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.Error == "" { // only write response object if error is nil
|
2021-11-21 15:25:11 +00:00
|
|
|
return Write_Data_Frame(c.r, obj)
|
2021-11-08 16:39:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|