You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
2.9 KiB
124 lines
2.9 KiB
package codec |
|
|
|
import ( |
|
"hash/crc32" |
|
"io" |
|
"sync/atomic" |
|
|
|
"github.com/go-faster/errors" |
|
|
|
"github.com/gotd/td/bin" |
|
) |
|
|
|
// Full is full MTProto transport. |
|
// |
|
// See https://core.telegram.org/mtproto/mtproto-transports#full |
|
type Full struct { |
|
wSeqNo int64 |
|
rSeqNo int64 |
|
} |
|
|
|
// WriteHeader sends protocol tag. |
|
func (i *Full) WriteHeader(w io.Writer) (err error) { |
|
return nil |
|
} |
|
|
|
// ReadHeader reads protocol tag. |
|
func (i *Full) ReadHeader(r io.Reader) (err error) { |
|
return nil |
|
} |
|
|
|
// Write encode to writer message from given buffer. |
|
func (i *Full) Write(w io.Writer, b *bin.Buffer) error { |
|
if err := checkOutgoingMessage(b); err != nil { |
|
return err |
|
} |
|
|
|
if err := writeFull(w, int(atomic.AddInt64(&i.wSeqNo, 1)-1), b); err != nil { |
|
return errors.Wrap(err, "write full") |
|
} |
|
|
|
return nil |
|
} |
|
|
|
// Read fills buffer with received message. |
|
func (i *Full) Read(r io.Reader, b *bin.Buffer) error { |
|
if err := readFull(r, int(atomic.AddInt64(&i.rSeqNo, 1)-1), b); err != nil { |
|
return errors.Wrap(err, "read full") |
|
} |
|
|
|
return checkProtocolError(b) |
|
} |
|
|
|
func writeFull(w io.Writer, seqNo int, b *bin.Buffer) error { |
|
write := bin.Buffer{Buf: make([]byte, 0, 4+4+b.Len()+4)} |
|
// Length: length+seqno+payload+crc length encoded as 4 length bytes |
|
// (little endian, the length of the length field must be included, too) |
|
write.PutInt(4 + 4 + b.Len() + 4) |
|
// Seqno: the TCP sequence number for this TCP connection (different from the MTProto sequence number): |
|
// the first packet sent is numbered 0, the next one 1, etc. |
|
write.PutInt(seqNo) |
|
// payload: MTProto payload |
|
write.Put(b.Raw()) |
|
// crc: 4 CRC32 bytes computed using length, sequence number, and payload together. |
|
crc := crc32.ChecksumIEEE(write.Raw()) |
|
write.PutUint32(crc) |
|
|
|
if _, err := w.Write(write.Raw()); err != nil { |
|
return err |
|
} |
|
|
|
return nil |
|
} |
|
|
|
var errSeqNoMismatch = errors.New("seq_no mismatch") |
|
var errCRCMismatch = errors.New("crc mismatch") |
|
|
|
func readFull(r io.Reader, seqNo int, b *bin.Buffer) error { |
|
n, err := readLen(r, b) |
|
if err != nil { |
|
return errors.Wrap(err, "len") |
|
} |
|
|
|
// Put length, because it need to count CRC. |
|
b.PutInt(n) |
|
b.Expand(n - bin.Word) |
|
inner := &bin.Buffer{Buf: b.Buf[bin.Word:n]} |
|
|
|
// Reads tail of packet to the buffer. |
|
// Length already read. |
|
if _, err := io.ReadFull(r, inner.Buf); err != nil { |
|
return errors.Wrap(err, "read seqno, buffer and crc") |
|
} |
|
|
|
serverSeqNo, err := inner.Int() |
|
if err != nil { |
|
return err |
|
} |
|
if serverSeqNo != seqNo { |
|
return errSeqNoMismatch |
|
} |
|
|
|
payloadLength := n - 3*bin.Word |
|
inner.Skip(payloadLength) |
|
|
|
// Cut only crc part. |
|
crc, err := inner.Uint32() |
|
if err != nil { |
|
return err |
|
} |
|
|
|
// Compute crc using all buffer without last 4 bytes from server. |
|
clientCRC := crc32.ChecksumIEEE(b.Buf[0 : n-bin.Word]) |
|
// Compare computed and read CRCs. |
|
if crc != clientCRC { |
|
return errCRCMismatch |
|
} |
|
|
|
// n |
|
// Length | SeqNo | payload | CRC | |
|
// Word | Word | ....... | Word | |
|
copy(b.Buf, b.Buf[2*bin.Word:n-bin.Word]) |
|
b.Buf = b.Buf[:payloadLength] |
|
return nil |
|
}
|
|
|