385 lines
8.7 KiB
Go
385 lines
8.7 KiB
Go
// Copyright 2015 Tony Bai.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package cmpp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type Type int8
|
|
|
|
const (
|
|
V30 Type = 0x30
|
|
V21 Type = 0x21
|
|
V20 Type = 0x20
|
|
)
|
|
|
|
func (t Type) String() string {
|
|
switch {
|
|
case t == V30:
|
|
return "cmpp30"
|
|
case t == V21:
|
|
return "cmpp21"
|
|
case t == V20:
|
|
return "cmpp20"
|
|
default:
|
|
return "unknown"
|
|
}
|
|
}
|
|
|
|
const (
|
|
CMPP_HEADER_LEN uint32 = 12
|
|
CMPP2_PACKET_MAX uint32 = 2477
|
|
CMPP2_PACKET_MIN uint32 = 12
|
|
CMPP3_PACKET_MAX uint32 = 3335
|
|
CMPP3_PACKET_MIN uint32 = 12
|
|
)
|
|
|
|
// Common errors.
|
|
var ErrMethodParamsInvalid = errors.New("params passed to method is invalid")
|
|
|
|
// Protocol errors.
|
|
var ErrTotalLengthInvalid = errors.New("total_length in Packet data is invalid")
|
|
var ErrCommandIdInvalid = errors.New("command_Id in Packet data is invalid")
|
|
var ErrCommandIdNotSupported = errors.New("command_Id in Packet data is not supported")
|
|
|
|
type CommandId uint32
|
|
|
|
const (
|
|
CMPP_REQUEST_MIN, CMPP_RESPONSE_MIN CommandId = iota, 0x80000000 + iota
|
|
CMPP_CONNECT, CMPP_CONNECT_RESP
|
|
CMPP_TERMINATE, CMPP_TERMINATE_RESP
|
|
_, _
|
|
CMPP_SUBMIT, CMPP_SUBMIT_RESP
|
|
CMPP_DELIVER, CMPP_DELIVER_RESP
|
|
CMPP_QUERY, CMPP_QUERY_RESP
|
|
CMPP_CANCEL, CMPP_CANCEL_RESP
|
|
CMPP_ACTIVE_TEST, CMPP_ACTIVE_TEST_RESP
|
|
CMPP_FWD, CMPP_FWD_RESP
|
|
CMPP_MT_ROUTE, CMPP_MT_ROUTE_RESP CommandId = 0x00000010 - 10 + iota, 0x80000010 - 10 + iota
|
|
CMPP_MO_ROUTE, CMPP_MO_ROUTE_RESP
|
|
CMPP_GET_MT_ROUTE, CMPP_GET_MT_ROUTE_RESP
|
|
CMPP_MT_ROUTE_UPDATE, CMPP_MT_ROUTE_UPDATE_RESP
|
|
CMPP_MO_ROUTE_UPDATE, CMPP_MO_ROUTE_UPDATE_RESP
|
|
CMPP_PUSH_MT_ROUTE_UPDATE, CMPP_PUSH_MT_ROUTE_UPDATE_RESP
|
|
CMPP_PUSH_MO_ROUTE_UPDATE, CMPP_PUSH_MO_ROUTE_UPDATE_RESP
|
|
CMPP_GET_MO_ROUTE, CMPP_GET_MO_ROUTE_RESP
|
|
CMPP_REQUEST_MAX, CMPP_RESPONSE_MAX
|
|
)
|
|
|
|
func (id CommandId) String() string {
|
|
if id <= CMPP_FWD && id > CMPP_REQUEST_MIN {
|
|
return []string{
|
|
"CMPP_CONNECT",
|
|
"CMPP_TERMINATE",
|
|
"CMPP_UNKNOWN",
|
|
"CMPP_SUBMIT",
|
|
"CMPP_DELIVER",
|
|
"CMPP_QUERY",
|
|
"CMPP_CANCEL",
|
|
"CMPP_ACTIVE_TEST",
|
|
"CMPP_FWD",
|
|
}[id-1]
|
|
} else if id >= CMPP_MT_ROUTE && id < CMPP_REQUEST_MAX {
|
|
return []string{
|
|
"CMPP_MT_ROUTE",
|
|
"CMPP_MO_ROUTE",
|
|
"CMPP_GET_MT_ROUTE",
|
|
"CMPP_MT_ROUTE_UPDATE",
|
|
"CMPP_MO_ROUTE_UPDATE",
|
|
"CMPP_PUSH_MT_ROUTE_UPDATE",
|
|
"CMPP_PUSH_MO_ROUTE_UPDATE",
|
|
"CMPP_GET_MO_ROUTE",
|
|
}[id-0x00000010]
|
|
}
|
|
|
|
if id <= CMPP_FWD_RESP && id > CMPP_RESPONSE_MIN {
|
|
return []string{
|
|
"CMPP_CONNECT_RESP",
|
|
"CMPP_TERMINATE_RESP",
|
|
"CMPP_UNKNOWN",
|
|
"CMPP_SUBMIT_RESP",
|
|
"CMPP_DELIVER_RESP",
|
|
"CMPP_QUERY_RESP",
|
|
"CMPP_CANCEL_RESP",
|
|
"CMPP_ACTIVE_TEST_RESP",
|
|
"CMPP_FWD_RESP",
|
|
}[id-0x80000001]
|
|
} else if id >= CMPP_MT_ROUTE_RESP && id < CMPP_RESPONSE_MAX {
|
|
return []string{
|
|
"CMPP_MT_ROUTE_RESP",
|
|
"CMPP_MO_ROUTE_RESP",
|
|
"CMPP_GET_MT_ROUTE_RESP",
|
|
"CMPP_MT_ROUTE_UPDATE_RESP",
|
|
"CMPP_MO_ROUTE_UPDATE_RESP",
|
|
"CMPP_PUSH_MT_ROUTE_UPDATE_RESP",
|
|
"CMPP_PUSH_MO_ROUTE_UPDATE_RESP",
|
|
"CMPP_GET_MO_ROUTE_RESP",
|
|
}[id-0x80000010]
|
|
}
|
|
return "unknown"
|
|
}
|
|
|
|
type Packer interface {
|
|
Pack(seqId uint32) ([]byte, error)
|
|
Unpack(data []byte) error
|
|
}
|
|
|
|
// OpError is the error type usually returned by functions in the cmpppacket
|
|
// package. It describes the operation and the error which the operation caused.
|
|
type OpError struct {
|
|
// err is the error that occurred during the operation.
|
|
// it is the origin error.
|
|
err error
|
|
|
|
// op is the operation which caused the error, such as
|
|
// some "read" or "write" in packetWriter or packetReader.
|
|
op string
|
|
}
|
|
|
|
func NewOpError(e error, op string) *OpError {
|
|
return &OpError{
|
|
err: e,
|
|
op: op,
|
|
}
|
|
}
|
|
|
|
func (e *OpError) Error() string {
|
|
if e.err == nil {
|
|
return "<nil>"
|
|
}
|
|
return e.op + " error: " + e.err.Error()
|
|
}
|
|
|
|
func (e *OpError) Cause() error {
|
|
return e.err
|
|
}
|
|
|
|
func (e *OpError) Op() string {
|
|
return e.op
|
|
}
|
|
|
|
type packetWriter struct {
|
|
wb *bytes.Buffer
|
|
err *OpError
|
|
}
|
|
|
|
func newPacketWriter(initSize uint32) *packetWriter {
|
|
buf := make([]byte, 0, initSize)
|
|
return &packetWriter{
|
|
wb: bytes.NewBuffer(buf),
|
|
}
|
|
}
|
|
|
|
// Bytes returns a slice of the contents of the inner buffer;
|
|
// If the caller changes the contents of the
|
|
// returned slice, the contents of the buffer will change provided there
|
|
// are no intervening method calls on the Buffer.
|
|
func (w *packetWriter) Bytes() ([]byte, error) {
|
|
if w.err != nil {
|
|
return nil, w.err
|
|
}
|
|
len := w.wb.Len()
|
|
return (w.wb.Bytes())[:len], nil
|
|
}
|
|
|
|
// WriteByte appends the byte of b to the inner buffer, growing the buffer as
|
|
// needed.
|
|
func (w *packetWriter) WriteByte(b byte) {
|
|
if w.err != nil {
|
|
return
|
|
}
|
|
|
|
err := w.wb.WriteByte(b)
|
|
if err != nil {
|
|
w.err = NewOpError(err,
|
|
fmt.Sprintf("packetWriter.WriteByte writes: %x", b))
|
|
return
|
|
}
|
|
}
|
|
|
|
// WriteFixedSizeString writes a string to buffer, if the length of s is less than size,
|
|
// Pad binary zero to the right.
|
|
func (w *packetWriter) WriteFixedSizeString(s string, size int) {
|
|
if w.err != nil {
|
|
return
|
|
}
|
|
|
|
l1 := len(s)
|
|
l2 := l1
|
|
if l2 > 10 {
|
|
l2 = 10
|
|
}
|
|
|
|
if l1 > size {
|
|
w.err = NewOpError(ErrMethodParamsInvalid,
|
|
fmt.Sprintf("packetWriter.WriteFixedSizeString writes: %s", s[0:l2]))
|
|
return
|
|
}
|
|
|
|
w.WriteString(strings.Join([]string{s, string(make([]byte, size-l1))}, ""))
|
|
}
|
|
|
|
// WriteString appends the contents of s to the inner buffer, growing the buffer as
|
|
// needed.
|
|
func (w *packetWriter) WriteString(s string) {
|
|
if w.err != nil {
|
|
return
|
|
}
|
|
|
|
l1 := len(s)
|
|
l2 := l1
|
|
if l2 > 10 {
|
|
l2 = 10
|
|
}
|
|
|
|
n, err := w.wb.WriteString(s)
|
|
if err != nil {
|
|
w.err = NewOpError(err,
|
|
fmt.Sprintf("packetWriter.WriteString writes: %s", s[0:l2]))
|
|
return
|
|
}
|
|
|
|
if n != l1 {
|
|
w.err = NewOpError(fmt.Errorf("WriteString writes %d bytes, not equal to %d we expected", n, l1),
|
|
fmt.Sprintf("packetWriter.WriteString writes: %s", s[0:l2]))
|
|
return
|
|
}
|
|
}
|
|
|
|
// WriteInt appends the content of data to the inner buffer in order, growing the buffer as
|
|
// needed.
|
|
func (w *packetWriter) WriteInt(order binary.ByteOrder, data interface{}) {
|
|
if w.err != nil {
|
|
return
|
|
}
|
|
|
|
err := binary.Write(w.wb, order, data)
|
|
if err != nil {
|
|
w.err = NewOpError(err,
|
|
fmt.Sprintf("packetWriter.WriteInt writes: %#v", data))
|
|
return
|
|
}
|
|
}
|
|
|
|
const maxCStringSize = 160
|
|
|
|
type packetReader struct {
|
|
rb *bytes.Buffer
|
|
err *OpError
|
|
cbuf [maxCStringSize]byte
|
|
}
|
|
|
|
func newPacketReader(data []byte) *packetReader {
|
|
return &packetReader{
|
|
rb: bytes.NewBuffer(data),
|
|
}
|
|
}
|
|
|
|
// ReadByte reads and returns the next byte from the inner buffer.
|
|
// If no byte is available, it returns an OpError.
|
|
func (r *packetReader) ReadByte() byte {
|
|
if r.err != nil {
|
|
return 0
|
|
}
|
|
|
|
b, err := r.rb.ReadByte()
|
|
if err != nil {
|
|
r.err = NewOpError(err,
|
|
"packetReader.ReadByte")
|
|
return 0
|
|
}
|
|
return b
|
|
}
|
|
|
|
// ReadInt reads reads structured binary data from r into data.
|
|
// Data must be a pointer to a fixed-size value or a slice
|
|
// of fixed-size values.
|
|
// Bytes read from r are decoded using the specified byte order
|
|
// and written to successive fields of the data.
|
|
func (r *packetReader) ReadInt(order binary.ByteOrder, data interface{}) {
|
|
if r.err != nil {
|
|
return
|
|
}
|
|
|
|
err := binary.Read(r.rb, order, data)
|
|
if err != nil {
|
|
r.err = NewOpError(err,
|
|
"packetReader.ReadInt")
|
|
return
|
|
}
|
|
}
|
|
|
|
// ReadBytes reads the next len(s) bytes from the inner buffer to s.
|
|
// If the buffer has no data to return, an OpError would be stored in r.err.
|
|
func (r *packetReader) ReadBytes(s []byte) {
|
|
if r.err != nil {
|
|
return
|
|
}
|
|
|
|
n, err := r.rb.Read(s)
|
|
if err != nil {
|
|
r.err = NewOpError(err,
|
|
"packetReader.ReadBytes")
|
|
return
|
|
}
|
|
|
|
if n != len(s) {
|
|
r.err = NewOpError(fmt.Errorf("ReadBytes reads %d bytes, not equal to %d we expected", n, len(s)),
|
|
"packetWriter.ReadBytes")
|
|
return
|
|
}
|
|
}
|
|
|
|
// ReadCString read bytes from packerReader's inner buffer,
|
|
// it would trim the tail-zero byte and the bytes after that.
|
|
func (r *packetReader) ReadCString(length int) []byte {
|
|
if r.err != nil {
|
|
return nil
|
|
}
|
|
|
|
var tmp = r.cbuf[:length]
|
|
n, err := r.rb.Read(tmp)
|
|
if err != nil {
|
|
r.err = NewOpError(err,
|
|
"packetReader.ReadCString")
|
|
return nil
|
|
}
|
|
|
|
if n != length {
|
|
r.err = NewOpError(fmt.Errorf("ReadCString reads %d bytes, not equal to %d we expected", n, length),
|
|
"packetWriter.ReadCString")
|
|
return nil
|
|
}
|
|
|
|
i := bytes.IndexByte(tmp, 0)
|
|
if i == -1 {
|
|
return tmp
|
|
} else {
|
|
return tmp[:i]
|
|
}
|
|
}
|
|
|
|
// Error return the inner err.
|
|
func (r *packetReader) Error() error {
|
|
if r.err != nil {
|
|
return r.err
|
|
}
|
|
return nil
|
|
}
|