Files
lislgosms/gateway/third_party/gocmpp/protocol_compatibility_test.go
T
hectorzhao 001d5f2cbd
CSS quality / css-quality (push) Has been cancelled
fix: 修复 CMPP 协议字段容量与版本兼容性
2026-09-20 15:49:37 +08:00

182 lines
5.2 KiB
Go

package cmpp
import (
"encoding/binary"
"fmt"
"net"
"reflect"
"strings"
"testing"
"time"
)
func TestReceiptVersionLayout(t *testing.T) {
for _, version := range []Type{V20, V21, V30} {
width, size := 21, 60
if version == V30 {
width, size = 32, 71
}
original := CmppReceiptPkt{MsgId: ^uint64(0), Stat: "DELIVRD", SubmitTime: "2609201200", DoneTime: "2609201201", DestTerminalId: strings.Repeat("9", width), SmscSequence: ^uint32(0)}
raw, err := original.PackVersion(version)
if err != nil {
t.Fatal(err)
}
if len(raw) != size || binary.BigEndian.Uint32(raw[size-4:]) != ^uint32(0) {
t.Fatalf("wrong layout: %x", raw)
}
var decoded CmppReceiptPkt
if err = decoded.UnpackVersion(raw, version); err != nil || !reflect.DeepEqual(decoded, original) {
t.Fatalf("roundtrip: %+v %v", decoded, err)
}
for _, bad := range [][]byte{raw[:len(raw)-1], append(append([]byte{}, raw...), 0)} {
if decoded.UnpackVersion(bad, version) == nil {
t.Fatal("invalid length accepted")
}
}
other := V30
if version == V30 {
other = V20
}
if decoded.UnpackVersion(raw, other) == nil {
t.Fatal("wrong version accepted")
}
original.DestTerminalId += "1"
if _, err = original.PackVersion(version); err == nil {
t.Fatal("truncated destination")
}
}
}
// Real TCP peers exercise the bounded reader, not only Pack/Unpack in memory.
func tcpPair(t *testing.T, version Type) (*Conn, net.Conn) {
t.Helper()
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
client, err := net.Dial("tcp", listener.Addr().String())
if err != nil {
t.Fatal(err)
}
server, err := listener.Accept()
if err != nil {
t.Fatal(err)
}
conn := NewConn(server, version)
conn.SetState(CONN_AUTHOK)
t.Cleanup(func() { conn.Close(); client.Close() })
return conn, client
}
func TestCMPP3LargeSubmitAndMalformedPackets(t *testing.T) {
for _, length := range []int{140, 159} {
p := Cmpp3SubmitReqPkt{DestUsrTl: 99, DestTerminalId: make([]string, 99), MsgLength: uint8(length), MsgContent: strings.Repeat("x", length)}
if length == 140 {
p.MsgFmt = 8
}
raw, err := p.Pack(^uint32(0))
if err != nil {
t.Fatal(err)
}
if len(raw) != 3331+length {
t.Fatalf("size %d", len(raw))
}
conn, peer := tcpPair(t, V30)
go peer.Write(raw)
pkt, err := conn.RecvAndUnpackPkt(time.Second)
if err != nil {
t.Fatal(err)
}
decoded := pkt.(*Cmpp3SubmitReqPkt)
if decoded.SeqId != ^uint32(0) || len(decoded.DestTerminalId) != 99 || decoded.MsgContent != p.MsgContent {
t.Fatal("wire mismatch")
}
var d Cmpp3SubmitReqPkt
if d.Unpack(raw[8:len(raw)-1]) == nil || d.Unpack(append(raw[8:], 0)) == nil {
t.Fatal("malformed body accepted")
}
p.DestUsrTl = 100
p.DestTerminalId = append(p.DestTerminalId, "")
if _, err = p.Pack(0); err == nil {
t.Fatal("100 destinations accepted")
}
}
for _, size := range []uint32{0, 11, CMPP3_PACKET_MAX + 1, ^uint32(0)} {
conn, peer := tcpPair(t, V30)
raw := make([]byte, 4)
binary.BigEndian.PutUint32(raw, size)
go peer.Write(raw)
if _, err := conn.RecvAndUnpackPkt(time.Second); err == nil {
t.Fatalf("accepted length %d", size)
}
}
p := Cmpp3SubmitReqPkt{DestUsrTl: 1, DestTerminalId: []string{"1"}, MsgFmt: 8, MsgLength: 141, MsgContent: strings.Repeat("a", 141)}
if _, err := p.Pack(0); err == nil {
t.Fatal("oversized non-ASCII accepted")
}
p.MsgFmt, p.MsgLength, p.MsgContent = 0, 160, strings.Repeat("a", 160)
if _, err := p.Pack(0); err == nil {
t.Fatal("ASCII must be strictly shorter than 160 bytes")
}
}
func TestConnectStatusKeepsAll32Bits(t *testing.T) {
for _, status := range []uint32{0, 5, 255, 256, ^uint32(0)} {
t.Run(fmt.Sprint(status), func(t *testing.T) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
defer listener.Close()
done := make(chan error, 1)
go func() {
peer, e := listener.Accept()
if e != nil {
done <- e
return
}
conn := NewConn(peer, V30)
defer conn.Close()
conn.SetState(CONN_CONNECTED)
req, e := conn.RecvAndUnpackPkt(time.Second)
if e == nil {
e = conn.SendPkt(&Cmpp3ConnRspPkt{Status: status, Version: V30}, req.(*CmppConnReqPkt).SeqId)
}
done <- e
}()
client := NewClient(V30)
defer client.Disconnect()
err = client.Connect(listener.Addr().String(), "123456", "secret", time.Second)
if status == 0 && err != nil {
t.Fatal(err)
}
if status != 0 && (err == nil || !strings.Contains(err.Error(), fmt.Sprintf("status=%d", status))) {
t.Fatalf("status truncated: %v", err)
}
if e := <-done; e != nil {
t.Fatal(e)
}
})
}
}
func TestRequestSequenceSkipsInFlightAcrossWrap(t *testing.T) {
conn, peer := tcpPair(t, V30)
sequences := make(chan uint32, 3)
sequences <- ^uint32(0)
sequences <- 0
sequences <- 1
conn.SeqId = sequences
client := &Client{conn: conn, typ: V30}
read := make(chan error, 1)
go func() { raw := make([]byte, 12); _, err := peer.Read(raw); read <- err }()
seq, err := client.SendReqPktAvailable(&CmppActiveTestReqPkt{}, func(n uint32) bool { return n != ^uint32(0) })
if err != nil || seq != 0 {
t.Fatalf("zero lost: %d %v", seq, err)
}
if err = <-read; err != nil {
t.Fatal(err)
}
}