182 lines
5.2 KiB
Go
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)
|
|
}
|
|
}
|