fix: log cmpp packet decode failures
This commit is contained in:
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// 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 "encoding/binary"
|
||||
|
||||
// Packet length const for cmpp active test request and response packets.
|
||||
const (
|
||||
CmppActiveTestReqPktLen uint32 = 12 //12d, 0xc
|
||||
CmppActiveTestRspPktLen uint32 = 12 + 1 //13d, 0xd
|
||||
)
|
||||
|
||||
type CmppActiveTestReqPkt struct {
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
type CmppActiveTestRspPkt struct {
|
||||
Reserved uint8
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the CmppActiveTestReqPkt to bytes stream for client side.
|
||||
func (p *CmppActiveTestReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = CmppActiveTestReqPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_ACTIVE_TEST)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppActiveTestReqPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// CmppActiveTestReqPkt struct.
|
||||
func (p *CmppActiveTestReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the CmppActiveTestRspPkt to bytes stream for client side.
|
||||
func (p *CmppActiveTestRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = CmppActiveTestRspPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_ACTIVE_TEST_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
w.WriteByte(p.Reserved)
|
||||
p.SeqId = seqId
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppActiveTestRspPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// CmppActiveTestRspPkt struct.
|
||||
func (p *CmppActiveTestRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
p.Reserved = r.ReadByte()
|
||||
return r.Error()
|
||||
}
|
||||
Vendored
+127
@@ -0,0 +1,127 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
var ErrNotCompleted = errors.New("data not being handled completed")
|
||||
var ErrRespNotMatch = errors.New("the response is not matched with the request")
|
||||
|
||||
// Client stands for one client-side instance, just like a session.
|
||||
// It may connect to the server, send & recv cmpp packets and terminate the connection.
|
||||
type Client struct {
|
||||
conn *Conn
|
||||
typ Type
|
||||
}
|
||||
|
||||
// New establishes a new cmpp client.
|
||||
func NewClient(typ Type) *Client {
|
||||
return &Client{
|
||||
typ: typ,
|
||||
}
|
||||
}
|
||||
|
||||
// Connect connect to the cmpp server in block mode.
|
||||
// It sends login packet, receive and parse connect response packet.
|
||||
func (cli *Client) Connect(servAddr, user, password string, timeout time.Duration) error {
|
||||
var err error
|
||||
conn, err := net.DialTimeout("tcp", servAddr, timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cli.conn = NewConn(conn, cli.typ)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if cli.conn != nil {
|
||||
cli.conn.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
cli.conn.SetState(CONN_CONNECTED)
|
||||
|
||||
// Login to the server.
|
||||
req := &CmppConnReqPkt{
|
||||
SrcAddr: user,
|
||||
Secret: password,
|
||||
Version: cli.typ,
|
||||
}
|
||||
|
||||
_, err = cli.SendReqPkt(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := cli.conn.RecvAndUnpackPkt(timeout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
var status uint8
|
||||
if cli.typ == V20 || cli.typ == V21 {
|
||||
var rsp *Cmpp2ConnRspPkt
|
||||
rsp, ok = p.(*Cmpp2ConnRspPkt)
|
||||
if !ok {
|
||||
err = ErrRespNotMatch
|
||||
return err
|
||||
}
|
||||
status = rsp.Status
|
||||
} else {
|
||||
var rsp *Cmpp3ConnRspPkt
|
||||
rsp, ok = p.(*Cmpp3ConnRspPkt)
|
||||
if !ok {
|
||||
err = ErrRespNotMatch
|
||||
return err
|
||||
}
|
||||
status = uint8(rsp.Status)
|
||||
}
|
||||
|
||||
if status != 0 {
|
||||
if status <= ErrnoConnOthers { //ErrnoConnOthers = 5
|
||||
err = ConnRspStatusErrMap[status]
|
||||
} else {
|
||||
err = ConnRspStatusErrMap[ErrnoConnOthers]
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
cli.conn.SetState(CONN_AUTHOK)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *Client) Disconnect() {
|
||||
if cli.conn != nil {
|
||||
cli.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// SendReqPkt pack the cmpp request packet structure and send it to the other peer.
|
||||
func (cli *Client) SendReqPkt(packet Packer) (uint32, error) {
|
||||
seq := <-cli.conn.SeqId
|
||||
return seq, cli.conn.SendPkt(packet, seq)
|
||||
}
|
||||
|
||||
// SendRspPkt pack the cmpp response packet structure and send it to the other peer.
|
||||
func (cli *Client) SendRspPkt(packet Packer, seqId uint32) error {
|
||||
return cli.conn.SendPkt(packet, seqId)
|
||||
}
|
||||
|
||||
// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
|
||||
func (cli *Client) RecvAndUnpackPkt(timeout time.Duration) (interface{}, error) {
|
||||
return cli.conn.RecvAndUnpackPkt(timeout)
|
||||
}
|
||||
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type State uint8
|
||||
|
||||
// Errors for conn operations
|
||||
var (
|
||||
ErrConnIsClosed = errors.New("connection is closed")
|
||||
ErrReadCmdIDTimeout = errors.New("read commandId timeout")
|
||||
ErrReadPktBodyTimeout = errors.New("read packet body timeout")
|
||||
)
|
||||
|
||||
var noDeadline = time.Time{}
|
||||
|
||||
// Conn States
|
||||
const (
|
||||
CONN_CLOSED State = iota
|
||||
CONN_CONNECTED
|
||||
CONN_AUTHOK
|
||||
)
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
State State
|
||||
Typ Type
|
||||
|
||||
// for SeqId generator goroutine
|
||||
SeqId <-chan uint32
|
||||
done chan<- struct{}
|
||||
}
|
||||
|
||||
func newSeqIdGenerator() (<-chan uint32, chan<- struct{}) {
|
||||
out := make(chan uint32)
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
var i uint32
|
||||
for {
|
||||
select {
|
||||
case out <- i:
|
||||
i++
|
||||
case <-done:
|
||||
close(out)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return out, done
|
||||
}
|
||||
|
||||
// New returns an abstract structure for successfully
|
||||
// established underlying net.Conn.
|
||||
func NewConn(conn net.Conn, typ Type) *Conn {
|
||||
seqId, done := newSeqIdGenerator()
|
||||
c := &Conn{
|
||||
Conn: conn,
|
||||
Typ: typ,
|
||||
SeqId: seqId,
|
||||
done: done,
|
||||
}
|
||||
tc := c.Conn.(*net.TCPConn) // Always tcpconn
|
||||
tc.SetKeepAlive(true) //Keepalive as default
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Conn) Close() {
|
||||
if c != nil {
|
||||
if c.State == CONN_CLOSED {
|
||||
return
|
||||
}
|
||||
close(c.done) // let the SeqId goroutine exit.
|
||||
c.Conn.Close() // close the underlying net.Conn
|
||||
c.State = CONN_CLOSED
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Conn) SetState(state State) {
|
||||
c.State = state
|
||||
}
|
||||
|
||||
// SendPkt pack the cmpp packet structure and send it to the other peer.
|
||||
func (c *Conn) SendPkt(packet Packer, seqId uint32) error {
|
||||
if c.State == CONN_CLOSED {
|
||||
return ErrConnIsClosed
|
||||
}
|
||||
|
||||
data, err := packet.Pack(seqId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = c.Conn.Write(data) //block write
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
defaultReadBufferSize = 4096
|
||||
)
|
||||
|
||||
// readBuffer is used to optimize the performance of
|
||||
// RecvAndUnpackPkt.
|
||||
type readBuffer struct {
|
||||
totalLen uint32
|
||||
commandId CommandId
|
||||
leftData [defaultReadBufferSize]byte
|
||||
}
|
||||
|
||||
var readBufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &readBuffer{}
|
||||
},
|
||||
}
|
||||
|
||||
// RecvAndUnpackPkt receives cmpp byte stream, and unpack it to some cmpp packet structure.
|
||||
func (c *Conn) RecvAndUnpackPkt(timeout time.Duration) (interface{}, error) {
|
||||
if c.State == CONN_CLOSED {
|
||||
return nil, ErrConnIsClosed
|
||||
}
|
||||
defer c.SetReadDeadline(noDeadline)
|
||||
|
||||
rb := readBufferPool.Get().(*readBuffer)
|
||||
defer readBufferPool.Put(rb)
|
||||
|
||||
// Total_Length in packet
|
||||
if timeout != 0 {
|
||||
c.SetReadDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
err := binary.Read(c.Conn, binary.BigEndian, &rb.totalLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.Typ == V30 {
|
||||
if rb.totalLen < CMPP3_PACKET_MIN || rb.totalLen > CMPP3_PACKET_MAX {
|
||||
return nil, ErrTotalLengthInvalid
|
||||
}
|
||||
}
|
||||
|
||||
if c.Typ == V21 || c.Typ == V20 {
|
||||
if rb.totalLen < CMPP2_PACKET_MIN || rb.totalLen > CMPP2_PACKET_MAX {
|
||||
return nil, ErrTotalLengthInvalid
|
||||
}
|
||||
}
|
||||
|
||||
// Command_Id
|
||||
if timeout != 0 {
|
||||
c.SetReadDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
err = binary.Read(c.Conn, binary.BigEndian, &rb.commandId)
|
||||
if err != nil {
|
||||
netErr, ok := err.(net.Error)
|
||||
if ok {
|
||||
if netErr.Timeout() {
|
||||
return nil, ErrReadCmdIDTimeout
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !((rb.commandId > CMPP_REQUEST_MIN && rb.commandId < CMPP_REQUEST_MAX) ||
|
||||
(rb.commandId > CMPP_RESPONSE_MIN && rb.commandId < CMPP_RESPONSE_MAX)) {
|
||||
return nil, ErrCommandIdInvalid
|
||||
}
|
||||
|
||||
// The left packet data (start from seqId in header).
|
||||
if timeout != 0 {
|
||||
c.SetReadDeadline(time.Now().Add(timeout))
|
||||
}
|
||||
var leftData = rb.leftData[0:(rb.totalLen - 8)]
|
||||
_, err = io.ReadFull(c.Conn, leftData)
|
||||
if err != nil {
|
||||
netErr, ok := err.(net.Error)
|
||||
if ok {
|
||||
if netErr.Timeout() {
|
||||
return nil, ErrReadPktBodyTimeout
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p Packer
|
||||
switch rb.commandId {
|
||||
case CMPP_CONNECT:
|
||||
p = &CmppConnReqPkt{}
|
||||
case CMPP_CONNECT_RESP:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3ConnRspPkt{}
|
||||
} else {
|
||||
p = &Cmpp2ConnRspPkt{}
|
||||
}
|
||||
case CMPP_TERMINATE:
|
||||
p = &CmppTerminateReqPkt{}
|
||||
case CMPP_TERMINATE_RESP:
|
||||
p = &CmppTerminateRspPkt{}
|
||||
case CMPP_SUBMIT:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3SubmitReqPkt{}
|
||||
} else {
|
||||
p = &Cmpp2SubmitReqPkt{}
|
||||
}
|
||||
case CMPP_SUBMIT_RESP:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3SubmitRspPkt{}
|
||||
} else {
|
||||
p = &Cmpp2SubmitRspPkt{}
|
||||
}
|
||||
case CMPP_DELIVER:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3DeliverReqPkt{}
|
||||
} else {
|
||||
p = &Cmpp2DeliverReqPkt{}
|
||||
}
|
||||
case CMPP_DELIVER_RESP:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3DeliverRspPkt{}
|
||||
} else {
|
||||
p = &Cmpp2DeliverRspPkt{}
|
||||
}
|
||||
case CMPP_FWD:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3FwdReqPkt{}
|
||||
} else {
|
||||
p = &Cmpp2FwdReqPkt{}
|
||||
}
|
||||
case CMPP_FWD_RESP:
|
||||
if c.Typ == V30 {
|
||||
p = &Cmpp3FwdRspPkt{}
|
||||
} else {
|
||||
p = &Cmpp2FwdRspPkt{}
|
||||
}
|
||||
case CMPP_ACTIVE_TEST:
|
||||
p = &CmppActiveTestReqPkt{}
|
||||
case CMPP_ACTIVE_TEST_RESP:
|
||||
p = &CmppActiveTestRspPkt{}
|
||||
|
||||
default:
|
||||
p = nil
|
||||
return nil, ErrCommandIdNotSupported
|
||||
}
|
||||
|
||||
err = p.Unpack(leftData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
// 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"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
cmpputils "github.com/bigwhite/gocmpp/utils"
|
||||
)
|
||||
|
||||
// Packet length const for cmpp connect request and response packets.
|
||||
const (
|
||||
CmppConnReqPktLen uint32 = 4 + 4 + 4 + 6 + 16 + 1 + 4 //39d, 0x27
|
||||
Cmpp2ConnRspPktLen uint32 = 4 + 4 + 4 + 1 + 16 + 1 //30d, 0x1e
|
||||
Cmpp3ConnRspPktLen uint32 = 4 + 4 + 4 + 4 + 16 + 1 //33d, 0x21
|
||||
)
|
||||
|
||||
// Errors for connect resp status.
|
||||
var (
|
||||
ErrnoConnInvalidStruct uint8 = 1
|
||||
ErrnoConnInvalidSrcAddr uint8 = 2
|
||||
ErrnoConnAuthFailed uint8 = 3
|
||||
ErrnoConnVerTooHigh uint8 = 4
|
||||
ErrnoConnOthers uint8 = 5
|
||||
|
||||
ConnRspStatusErrMap = map[uint8]error{
|
||||
ErrnoConnInvalidStruct: errConnInvalidStruct,
|
||||
ErrnoConnInvalidSrcAddr: errConnInvalidSrcAddr,
|
||||
ErrnoConnAuthFailed: errConnAuthFailed,
|
||||
ErrnoConnVerTooHigh: errConnVerTooHigh,
|
||||
ErrnoConnOthers: errConnOthers,
|
||||
}
|
||||
|
||||
errConnInvalidStruct = errors.New("connect response status: invalid protocol structure")
|
||||
errConnInvalidSrcAddr = errors.New("connect response status: invalid source address")
|
||||
errConnAuthFailed = errors.New("connect response status: auth failed")
|
||||
errConnVerTooHigh = errors.New("connect response status: protocol version is too high")
|
||||
errConnOthers = errors.New("connect response status: other errors")
|
||||
)
|
||||
|
||||
func now() (string, uint32) {
|
||||
s := time.Now().Format("0102150405")
|
||||
i, _ := strconv.Atoi(s)
|
||||
return s, uint32(i)
|
||||
}
|
||||
|
||||
// CmppConnReqPkt represents a Cmpp2 or Cmpp3 connect request packet.
|
||||
//
|
||||
// when used in client side(pack), you should initialize it with
|
||||
// correct SourceAddr(SrcAddr), Secret and Version.
|
||||
//
|
||||
// when used in server side(unpack), nothing needed to be initialized.
|
||||
// unpack will fill the SourceAddr(SrcAddr), AuthSrc, Version, Timestamp
|
||||
// and SeqId
|
||||
//
|
||||
type CmppConnReqPkt struct {
|
||||
SrcAddr string
|
||||
AuthSrc string
|
||||
Version Type
|
||||
Timestamp uint32
|
||||
Secret string
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Cmpp2ConnRspPkt represents a Cmpp2 connect response packet.
|
||||
//
|
||||
// when used in server side(pack), you should initialize it with
|
||||
// correct Status, AuthSrc, Secret and Version.
|
||||
//
|
||||
// when used in client side(unpack), nothing needed to be initialized.
|
||||
// unpack will fill the Status, AuthImsg, Version and SeqId
|
||||
//
|
||||
type Cmpp2ConnRspPkt struct {
|
||||
Status uint8
|
||||
AuthIsmg string
|
||||
Version Type
|
||||
Secret string
|
||||
AuthSrc string
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Cmpp3ConnRspPkt represents a Cmpp3 connect response packet.
|
||||
//
|
||||
// when used in server side(pack), you should initialize it with
|
||||
// correct Status, AuthSrc, Secret and Version.
|
||||
//
|
||||
// when used in client side(unpack), nothing needed to be initialized.
|
||||
// unpack will fill the Status, AuthImsg, Version and SeqId
|
||||
//
|
||||
type Cmpp3ConnRspPkt struct {
|
||||
Status uint32
|
||||
AuthIsmg string
|
||||
Version Type
|
||||
Secret string
|
||||
AuthSrc string
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the CmppConnReqPkt to bytes stream for client side.
|
||||
// Before calling Pack, you should initialize a CmppConnReqPkt variable
|
||||
// with correct SourceAddr(SrcAddr), Secret and Version.
|
||||
func (p *CmppConnReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var w = newPacketWriter(CmppConnReqPktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, CmppConnReqPktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_CONNECT)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
var ts string
|
||||
if p.Timestamp == 0 {
|
||||
ts, p.Timestamp = now() //default: current time.
|
||||
} else {
|
||||
ts = cmpputils.TimeStamp2Str(p.Timestamp)
|
||||
}
|
||||
|
||||
// Pack body
|
||||
srcAddr := cmpputils.OctetString(p.SrcAddr, 6)
|
||||
w.WriteString(srcAddr)
|
||||
|
||||
md5 := md5.Sum(bytes.Join([][]byte{[]byte(srcAddr),
|
||||
make([]byte, 9),
|
||||
[]byte(p.Secret),
|
||||
[]byte(ts)},
|
||||
nil))
|
||||
p.AuthSrc = string(md5[:])
|
||||
|
||||
w.WriteString(p.AuthSrc)
|
||||
w.WriteInt(binary.BigEndian, p.Version)
|
||||
w.WriteInt(binary.BigEndian, p.Timestamp)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppConnReqPkt variable.
|
||||
// Usually it is used in server side. After unpack, you will get SeqId, SourceAddr,
|
||||
// AuthenticatorSource, Version and Timestamp.
|
||||
func (p *CmppConnReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body: Source_Addr
|
||||
var sa = make([]byte, 6)
|
||||
r.ReadBytes(sa)
|
||||
p.SrcAddr = string(sa)
|
||||
|
||||
// Body: AuthSrc
|
||||
var as = make([]byte, 16)
|
||||
r.ReadBytes(as)
|
||||
p.AuthSrc = string(as)
|
||||
|
||||
// Body: Version
|
||||
r.ReadInt(binary.BigEndian, &p.Version)
|
||||
// Body: timestamp
|
||||
r.ReadInt(binary.BigEndian, &p.Timestamp)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2ConnRspPkt to bytes stream for server side.
|
||||
// Before calling Pack, you should initialize a Cmpp2ConnRspPkt variable
|
||||
// with correct Status,AuthenticatorSource, Secret and Version.
|
||||
func (p *Cmpp2ConnRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var w = newPacketWriter(Cmpp2ConnRspPktLen)
|
||||
|
||||
// pack header
|
||||
w.WriteInt(binary.BigEndian, Cmpp2ConnRspPktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_CONNECT_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// pack body
|
||||
w.WriteInt(binary.BigEndian, p.Status)
|
||||
|
||||
md5 := md5.Sum(bytes.Join([][]byte{[]byte{p.Status},
|
||||
[]byte(p.AuthSrc),
|
||||
[]byte(p.Secret)},
|
||||
nil))
|
||||
p.AuthIsmg = string(md5[:])
|
||||
w.WriteString(p.AuthIsmg)
|
||||
|
||||
w.WriteInt(binary.BigEndian, p.Version)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2ConnRspPkt variable.
|
||||
// Usually it is used in client side. After unpack, you will get SeqId, Status,
|
||||
// AuthenticatorIsmg, and Version.
|
||||
// Parameter data contains seqId in header and the whole packet body.
|
||||
func (p *Cmpp2ConnRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body: Status
|
||||
r.ReadInt(binary.BigEndian, &p.Status)
|
||||
|
||||
// Body: AuthenticatorISMG
|
||||
var s = make([]byte, 16)
|
||||
r.ReadBytes(s)
|
||||
p.AuthIsmg = string(s)
|
||||
|
||||
// Body: Version
|
||||
r.ReadInt(binary.BigEndian, &p.Version)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3ConnRspPkt to bytes stream for server side.
|
||||
// Before calling Pack, you should initialize a Cmpp3ConnRspPkt variable
|
||||
// with correct Status,AuthenticatorSource, Secret and Version.
|
||||
func (p *Cmpp3ConnRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var w = newPacketWriter(Cmpp3ConnRspPktLen)
|
||||
|
||||
// pack header
|
||||
w.WriteInt(binary.BigEndian, Cmpp3ConnRspPktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_CONNECT_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// pack body
|
||||
w.WriteInt(binary.BigEndian, p.Status)
|
||||
|
||||
var statusBuf = new(bytes.Buffer)
|
||||
err := binary.Write(statusBuf, binary.BigEndian, p.Status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
md5 := md5.Sum(bytes.Join([][]byte{statusBuf.Bytes(),
|
||||
[]byte(p.AuthSrc),
|
||||
[]byte(p.Secret)},
|
||||
nil))
|
||||
p.AuthIsmg = string(md5[:])
|
||||
w.WriteString(p.AuthIsmg)
|
||||
|
||||
w.WriteInt(binary.BigEndian, p.Version)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3ConnRspPkt variable.
|
||||
// Usually it is used in client side. After unpack, you will get SeqId, Status,
|
||||
// AuthenticatorIsmg, and Version.
|
||||
// Parameter data contains seqId in header and the whole packet body.
|
||||
func (p *Cmpp3ConnRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body: Status
|
||||
r.ReadInt(binary.BigEndian, &p.Status)
|
||||
|
||||
// Body: AuthenticatorISMG
|
||||
var s = make([]byte, 16)
|
||||
r.ReadBytes(s)
|
||||
p.AuthIsmg = string(s)
|
||||
|
||||
// Body: Version
|
||||
r.ReadInt(binary.BigEndian, &p.Version)
|
||||
return r.Error()
|
||||
}
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Packet length const for cmpp deliver request and response packets.
|
||||
const (
|
||||
Cmpp2DeliverReqPktMaxLen uint32 = 12 + 233 //245d, 0xf5
|
||||
Cmpp2DeliverRspPktLen uint32 = 12 + 8 + 1 //21d, 0x15
|
||||
|
||||
Cmpp3DeliverReqPktMaxLen uint32 = 12 + 257 //269d, 0x10d
|
||||
Cmpp3DeliverRspPktLen uint32 = 12 + 8 + 4 //24d, 0x18
|
||||
)
|
||||
|
||||
// Errors for result in deliver resp.
|
||||
|
||||
var (
|
||||
ErrnoDeliverInvalidStruct uint8 = 1
|
||||
ErrnoDeliverInvalidCommandId uint8 = 2
|
||||
ErrnoDeliverInvalidSequence uint8 = 3
|
||||
ErrnoDeliverInvalidMsgLength uint8 = 4
|
||||
ErrnoDeliverInvalidFeeCode uint8 = 5
|
||||
ErrnoDeliverExceedMaxMsgLength uint8 = 6
|
||||
ErrnoDeliverInvalidServiceId uint8 = 7
|
||||
ErrnoDeliverNotPassFlowControl uint8 = 8
|
||||
ErrnoDeliverOtherError uint8 = 9
|
||||
|
||||
DeliverRspResultErrMap = map[uint8]error{
|
||||
ErrnoDeliverInvalidStruct: errDeliverInvalidStruct,
|
||||
ErrnoDeliverInvalidCommandId: errDeliverInvalidCommandId,
|
||||
ErrnoDeliverInvalidSequence: errDeliverInvalidSequence,
|
||||
ErrnoDeliverInvalidMsgLength: errDeliverInvalidMsgLength,
|
||||
ErrnoDeliverInvalidFeeCode: errDeliverInvalidFeeCode,
|
||||
ErrnoDeliverExceedMaxMsgLength: errDeliverExceedMaxMsgLength,
|
||||
ErrnoDeliverInvalidServiceId: errDeliverInvalidServiceId,
|
||||
ErrnoDeliverNotPassFlowControl: errDeliverNotPassFlowControl,
|
||||
ErrnoDeliverOtherError: errDeliverOtherError,
|
||||
}
|
||||
|
||||
errDeliverInvalidStruct = errors.New("deliver response status: invalid protocol structure")
|
||||
errDeliverInvalidCommandId = errors.New("deliver response status: invalid command id")
|
||||
errDeliverInvalidSequence = errors.New("deliver response status: invalid message sequence")
|
||||
errDeliverInvalidMsgLength = errors.New("deliver response status: invalid message length")
|
||||
errDeliverInvalidFeeCode = errors.New("deliver response status: invalid fee code")
|
||||
errDeliverExceedMaxMsgLength = errors.New("deliver response status: exceed max message length")
|
||||
errDeliverInvalidServiceId = errors.New("deliver response status: invalid service id")
|
||||
errDeliverNotPassFlowControl = errors.New("deliver response status: not pass the flow control")
|
||||
errDeliverOtherError = errors.New("deliver response status: other error")
|
||||
)
|
||||
|
||||
type Cmpp2DeliverReqPkt struct {
|
||||
MsgId uint64
|
||||
DestId string
|
||||
ServiceId string
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
SrcTerminalId string
|
||||
RegisterDelivery uint8
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
Reserve string
|
||||
|
||||
//session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp2DeliverRspPkt struct {
|
||||
MsgId uint64
|
||||
Result uint8
|
||||
|
||||
//session info
|
||||
SeqId uint32
|
||||
}
|
||||
type Cmpp3DeliverReqPkt struct {
|
||||
MsgId uint64
|
||||
DestId string
|
||||
ServiceId string
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
SrcTerminalId string
|
||||
SrcTerminalType uint8
|
||||
RegisterDelivery uint8
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
LinkId string
|
||||
|
||||
//session info
|
||||
SeqId uint32
|
||||
}
|
||||
type Cmpp3DeliverRspPkt struct {
|
||||
MsgId uint64
|
||||
Result uint32
|
||||
|
||||
//session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2DeliverReqPkt to bytes stream for client side.
|
||||
func (p *Cmpp2DeliverReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 65 + uint32(p.MsgLength) + 8
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_DELIVER)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteFixedSizeString(p.DestId, 21)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.SrcTerminalId, 21)
|
||||
w.WriteByte(p.RegisterDelivery)
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.Reserve, 8)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2DeliverReqPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// Cmpp2DeliverReqPkt struct.
|
||||
func (p *Cmpp2DeliverReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
destId := r.ReadCString(21)
|
||||
p.DestId = string(destId)
|
||||
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
srcTerminalId := r.ReadCString(21)
|
||||
p.SrcTerminalId = string(srcTerminalId)
|
||||
|
||||
p.RegisterDelivery = r.ReadByte()
|
||||
p.MsgLength = r.ReadByte()
|
||||
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
reserve := r.ReadCString(8)
|
||||
p.Reserve = string(reserve)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2DeliverRspPkt to bytes stream for client side.
|
||||
func (p *Cmpp2DeliverRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = Cmpp2DeliverRspPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_DELIVER_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteByte(p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2DeliverRspPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// Cmpp2DeliverRspPkt struct.
|
||||
func (p *Cmpp2DeliverRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
p.Result = r.ReadByte()
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3DeliverReqPkt to bytes stream for client side.
|
||||
func (p *Cmpp3DeliverReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 77 + uint32(p.MsgLength) + 20
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_DELIVER)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteFixedSizeString(p.DestId, 21)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.SrcTerminalId, 32)
|
||||
w.WriteByte(p.SrcTerminalType)
|
||||
w.WriteByte(p.RegisterDelivery)
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.LinkId, 20)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3DeliverReqPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// Cmpp3DeliverReqPkt struct.
|
||||
func (p *Cmpp3DeliverReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
destId := r.ReadCString(21)
|
||||
p.DestId = string(destId)
|
||||
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
srcTerminalId := r.ReadCString(32)
|
||||
p.SrcTerminalId = string(srcTerminalId)
|
||||
p.SrcTerminalType = r.ReadByte()
|
||||
|
||||
p.RegisterDelivery = r.ReadByte()
|
||||
p.MsgLength = r.ReadByte()
|
||||
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
linkId := r.ReadCString(20)
|
||||
p.LinkId = string(linkId)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3DeliverRspPkt to bytes stream for client side.
|
||||
func (p *Cmpp3DeliverRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = Cmpp3DeliverRspPktLen
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_DELIVER_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteInt(binary.BigEndian, p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3DeliverRspPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// Cmpp3DeliverRspPkt struct.
|
||||
func (p *Cmpp3DeliverRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
r.ReadInt(binary.BigEndian, &p.Result)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
Vendored
+489
@@ -0,0 +1,489 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Packet length const for cmpp fwd request and response packets.
|
||||
const (
|
||||
Cmpp2FwdReqPktMaxLen uint32 = 12 + 2379 //2277d, 0x957
|
||||
Cmpp2FwdRspPktLen uint32 = 12 + 8 + 1 + 1 + 1 //23d, 0x17
|
||||
|
||||
Cmpp3FwdReqPktMaxLen uint32 = 12 + 2491 //2503d, 0x9c7
|
||||
Cmpp3FwdRspPktLen uint32 = 12 + 8 + 1 + 1 + 4 //26d, 0x1a
|
||||
)
|
||||
|
||||
// Errors for result in fwd resp.
|
||||
var (
|
||||
ErrnoFwdInvalidStruct uint8 = 1
|
||||
ErrnoFwdInvalidCommandId uint8 = 2
|
||||
ErrnoFwdInvalidSequence uint8 = 3
|
||||
ErrnoFwdInvalidMsgLength uint8 = 4
|
||||
ErrnoFwdInvalidFeeCode uint8 = 5
|
||||
ErrnoFwdExceedMaxMsgLength uint8 = 6
|
||||
ErrnoFwdInvalidServiceId uint8 = 7
|
||||
ErrnoFwdNotPassFlowControl uint8 = 8
|
||||
ErrnoFwdNoPrivilege uint8 = 9
|
||||
|
||||
FwdRspResultErrMap = map[uint8]error{
|
||||
ErrnoFwdInvalidStruct: errFwdInvalidStruct,
|
||||
ErrnoFwdInvalidCommandId: errFwdInvalidCommandId,
|
||||
ErrnoFwdInvalidSequence: errFwdInvalidSequence,
|
||||
ErrnoFwdInvalidMsgLength: errFwdInvalidMsgLength,
|
||||
ErrnoFwdInvalidFeeCode: errFwdInvalidFeeCode,
|
||||
ErrnoFwdExceedMaxMsgLength: errFwdExceedMaxMsgLength,
|
||||
ErrnoFwdInvalidServiceId: errFwdInvalidServiceId,
|
||||
ErrnoFwdNotPassFlowControl: errFwdNotPassFlowControl,
|
||||
ErrnoFwdNoPrivilege: errFwdNoPrivilege,
|
||||
}
|
||||
|
||||
errFwdInvalidStruct = errors.New("fwd response status: invalid protocol structure")
|
||||
errFwdInvalidCommandId = errors.New("fwd response status: invalid command id")
|
||||
errFwdInvalidSequence = errors.New("fwd response status: invalid message sequence")
|
||||
errFwdInvalidMsgLength = errors.New("fwd response status: invalid message length")
|
||||
errFwdInvalidFeeCode = errors.New("fwd response status: invalid fee code")
|
||||
errFwdExceedMaxMsgLength = errors.New("fwd response status: exceed max message length")
|
||||
errFwdInvalidServiceId = errors.New("fwd response status: invalid service id")
|
||||
errFwdNotPassFlowControl = errors.New("fwd response status: not pass the flow control")
|
||||
errFwdNoPrivilege = errors.New("fwd response status: msg has no fwd privilege")
|
||||
)
|
||||
|
||||
type Cmpp2FwdReqPkt struct {
|
||||
SourceId string
|
||||
DestinationId string
|
||||
NodesCount uint8
|
||||
MsgFwdType uint8
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
RegisteredDelivery uint8
|
||||
MsgLevel uint8
|
||||
ServiceId string
|
||||
FeeUserType uint8
|
||||
FeeTerminalId string
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
MsgSrc string
|
||||
FeeType string
|
||||
FeeCode string
|
||||
ValidTime string
|
||||
AtTime string
|
||||
SrcId string
|
||||
DestUsrTl uint8
|
||||
DestId []string
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
Reserve string
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp2FwdRspPkt struct {
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
Result uint8
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
type Cmpp3FwdReqPkt struct {
|
||||
SourceId string
|
||||
DestinationId string
|
||||
NodesCount uint8
|
||||
MsgFwdType uint8
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
RegisteredDelivery uint8
|
||||
MsgLevel uint8
|
||||
ServiceId string
|
||||
FeeUserType uint8
|
||||
FeeTerminalId string
|
||||
FeeTerminalPseudo string
|
||||
FeeTerminalUserType uint8
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
MsgSrc string
|
||||
FeeType string
|
||||
FeeCode string
|
||||
ValidTime string
|
||||
AtTime string
|
||||
SrcId string
|
||||
SrcPseudo string
|
||||
SrcUserType uint8
|
||||
SrcType uint8
|
||||
DestUsrTl uint8
|
||||
DestId []string
|
||||
DestPseudo string
|
||||
DestUserType uint8
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
LinkId string
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp3FwdRspPkt struct {
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
Result uint32
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2FwdReqPkt to bytes stream for client side.
|
||||
// Before calling Pack, you should initialize a Cmpp2FwdReqPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp2FwdReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 131 + uint32(p.DestUsrTl)*21 + 1 + uint32(p.MsgLength) + 8
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_FWD)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteFixedSizeString(p.SourceId, 6)
|
||||
w.WriteFixedSizeString(p.DestinationId, 6)
|
||||
w.WriteByte(p.NodesCount)
|
||||
w.WriteByte(p.MsgFwdType)
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
|
||||
if p.PkTotal == 0 && p.PkNumber == 0 {
|
||||
p.PkTotal, p.PkNumber = 1, 1
|
||||
}
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
|
||||
w.WriteByte(p.RegisteredDelivery)
|
||||
w.WriteByte(p.MsgLevel)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.FeeUserType)
|
||||
w.WriteFixedSizeString(p.FeeTerminalId, 21)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.MsgSrc, 6)
|
||||
w.WriteFixedSizeString(p.FeeType, 2)
|
||||
w.WriteFixedSizeString(p.FeeCode, 6)
|
||||
w.WriteFixedSizeString(p.ValidTime, 17)
|
||||
w.WriteFixedSizeString(p.AtTime, 17)
|
||||
w.WriteFixedSizeString(p.SrcId, 21)
|
||||
w.WriteByte(p.DestUsrTl)
|
||||
for _, d := range p.DestId {
|
||||
w.WriteFixedSizeString(d, 21)
|
||||
}
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.Reserve, 8)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2FwdReqPkt variable.
|
||||
// After unpack, you will get all value of fields in Cmpp2FwdReqPkt struct.
|
||||
func (p *Cmpp2FwdReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
sourceId := r.ReadCString(6)
|
||||
p.SourceId = string(sourceId)
|
||||
destinationId := r.ReadCString(6)
|
||||
p.DestinationId = string(destinationId)
|
||||
p.NodesCount = r.ReadByte()
|
||||
p.MsgFwdType = r.ReadByte()
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
p.RegisteredDelivery = r.ReadByte()
|
||||
p.MsgLevel = r.ReadByte()
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
p.FeeUserType = r.ReadByte()
|
||||
feeTerminalId := r.ReadCString(21)
|
||||
p.FeeTerminalId = string(feeTerminalId)
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
msgSrc := r.ReadCString(6)
|
||||
p.MsgSrc = string(msgSrc)
|
||||
|
||||
feeType := make([]byte, 2)
|
||||
r.ReadBytes(feeType)
|
||||
p.FeeType = string(feeType)
|
||||
|
||||
feeCode := r.ReadCString(6)
|
||||
p.FeeCode = string(feeCode)
|
||||
|
||||
validTime := r.ReadCString(17)
|
||||
p.ValidTime = string(validTime)
|
||||
|
||||
atTime := r.ReadCString(17)
|
||||
p.AtTime = string(atTime)
|
||||
|
||||
srcId := r.ReadCString(21)
|
||||
p.SrcId = string(srcId)
|
||||
|
||||
p.DestUsrTl = r.ReadByte()
|
||||
for i := 0; i < int(p.DestUsrTl); i++ {
|
||||
destId := r.ReadCString(21)
|
||||
p.DestId = append(p.DestId, string(destId))
|
||||
}
|
||||
|
||||
p.MsgLength = r.ReadByte()
|
||||
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
reserve := r.ReadCString(8)
|
||||
p.Reserve = string(reserve)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2FwdRspPkt to bytes stream for server side.
|
||||
// Before calling Pack, you should initialize a Cmpp2FwdRspPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp2FwdRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = Cmpp2FwdRspPktLen
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_FWD_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
w.WriteByte(p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2FwdRspPkt variable.
|
||||
// After unpack, you will get all value of fields in Cmpp2FwdRspPkt struct.
|
||||
func (p *Cmpp2FwdRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
p.Result = r.ReadByte()
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3FwdReqPkt to bytes stream for client side.
|
||||
// Before calling Pack, you should initialize a Cmpp3FwdReqPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp3FwdReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 198 + uint32(p.DestUsrTl)*21 + 32 + 1 + 1 + uint32(p.MsgLength) + 20
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_FWD)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteFixedSizeString(p.SourceId, 6)
|
||||
w.WriteFixedSizeString(p.DestinationId, 6)
|
||||
w.WriteByte(p.NodesCount)
|
||||
w.WriteByte(p.MsgFwdType)
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
|
||||
if p.PkTotal == 0 && p.PkNumber == 0 {
|
||||
p.PkTotal, p.PkNumber = 1, 1
|
||||
}
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
w.WriteByte(p.RegisteredDelivery)
|
||||
w.WriteByte(p.MsgLevel)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.FeeUserType)
|
||||
w.WriteFixedSizeString(p.FeeTerminalId, 21)
|
||||
w.WriteFixedSizeString(p.FeeTerminalPseudo, 32)
|
||||
w.WriteByte(p.FeeTerminalUserType)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.MsgSrc, 6)
|
||||
w.WriteFixedSizeString(p.FeeType, 2)
|
||||
w.WriteFixedSizeString(p.FeeCode, 6)
|
||||
w.WriteFixedSizeString(p.ValidTime, 17)
|
||||
w.WriteFixedSizeString(p.AtTime, 17)
|
||||
w.WriteFixedSizeString(p.SrcId, 21)
|
||||
w.WriteFixedSizeString(p.SrcPseudo, 32)
|
||||
w.WriteByte(p.SrcUserType)
|
||||
w.WriteByte(p.SrcType)
|
||||
w.WriteByte(p.DestUsrTl)
|
||||
|
||||
for _, d := range p.DestId {
|
||||
w.WriteFixedSizeString(d, 21)
|
||||
}
|
||||
w.WriteFixedSizeString(p.DestPseudo, 32)
|
||||
w.WriteByte(p.DestUserType)
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.LinkId, 20)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3FwdReqPkt variable.
|
||||
// After unpack, you will get all value of fields in Cmpp3FwdReqPkt struct.
|
||||
func (p *Cmpp3FwdReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
// Body
|
||||
sourceId := r.ReadCString(6)
|
||||
p.SourceId = string(sourceId)
|
||||
destinationId := r.ReadCString(6)
|
||||
p.DestinationId = string(destinationId)
|
||||
p.NodesCount = r.ReadByte()
|
||||
p.MsgFwdType = r.ReadByte()
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
p.RegisteredDelivery = r.ReadByte()
|
||||
p.MsgLevel = r.ReadByte()
|
||||
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
|
||||
p.FeeUserType = r.ReadByte()
|
||||
|
||||
feeTerminalId := r.ReadCString(21)
|
||||
p.FeeTerminalId = string(feeTerminalId)
|
||||
feeTerminalPseudo := r.ReadCString(32)
|
||||
p.FeeTerminalPseudo = string(feeTerminalPseudo)
|
||||
p.FeeTerminalUserType = r.ReadByte()
|
||||
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
msgSrc := r.ReadCString(6)
|
||||
p.MsgSrc = string(msgSrc)
|
||||
|
||||
feeType := make([]byte, 2)
|
||||
r.ReadBytes(feeType)
|
||||
p.FeeType = string(feeType)
|
||||
|
||||
feeCode := r.ReadCString(6)
|
||||
p.FeeCode = string(feeCode)
|
||||
|
||||
validTime := r.ReadCString(17)
|
||||
p.ValidTime = string(validTime)
|
||||
|
||||
atTime := r.ReadCString(17)
|
||||
p.AtTime = string(atTime)
|
||||
|
||||
srcId := r.ReadCString(21)
|
||||
p.SrcId = string(srcId)
|
||||
|
||||
srcPseudo := r.ReadCString(32)
|
||||
p.SrcPseudo = string(srcPseudo)
|
||||
p.SrcUserType = r.ReadByte()
|
||||
p.SrcType = r.ReadByte()
|
||||
|
||||
p.DestUsrTl = r.ReadByte()
|
||||
for i := 0; i < int(p.DestUsrTl); i++ {
|
||||
destId := r.ReadCString(21)
|
||||
p.DestId = append(p.DestId, string(destId))
|
||||
}
|
||||
destPseudo := r.ReadCString(32)
|
||||
p.DestPseudo = string(destPseudo)
|
||||
p.DestUserType = r.ReadByte()
|
||||
|
||||
p.MsgLength = r.ReadByte()
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
linkId := r.ReadCString(20)
|
||||
p.LinkId = string(linkId)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3FwdRspPkt to bytes stream for server side.
|
||||
// Before calling Pack, you should initialize a Cmpp3FwdRspPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp3FwdRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = Cmpp3FwdRspPktLen
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_FWD_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
w.WriteInt(binary.BigEndian, p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3FwdRspPkt variable.
|
||||
// After unpack, you will get all value of fields in Cmpp3FwdRspPkt struct.
|
||||
func (p *Cmpp3FwdRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
r.ReadInt(binary.BigEndian, &p.Result)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
module github.com/bigwhite/gocmpp
|
||||
|
||||
require (
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20190516070045-5cc3605ccbb6
|
||||
golang.org/x/text v0.3.8
|
||||
)
|
||||
|
||||
go 1.13
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20190516070045-5cc3605ccbb6 h1:JQBgIQumUT/1PnZs9cWQzx/xH+djYh+oGDLO8H68SWE=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20190516070045-5cc3605ccbb6/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
Vendored
+384
@@ -0,0 +1,384 @@
|
||||
// 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
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// 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 "encoding/binary"
|
||||
|
||||
// Packet length const for cmpp receipt packet.
|
||||
const (
|
||||
CmppReceiptPktLen uint32 = 60 //60d, 0x3c
|
||||
)
|
||||
|
||||
type CmppReceiptPkt struct {
|
||||
MsgId uint64
|
||||
Stat string
|
||||
SubmitTime string // YYMMDDHHMM
|
||||
DoneTime string // YYMMDDHHMM
|
||||
DestTerminalId string
|
||||
SmscSequence uint32
|
||||
}
|
||||
|
||||
// Pack packs the CmppReceiptPkt to bytes stream for client side.
|
||||
func (p *CmppReceiptPkt) Pack() ([]byte, error) {
|
||||
var pktLen uint32 = CmppReceiptPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteFixedSizeString(p.Stat, 7)
|
||||
w.WriteFixedSizeString(p.SubmitTime, 10)
|
||||
w.WriteFixedSizeString(p.DoneTime, 10)
|
||||
w.WriteFixedSizeString(p.DestTerminalId, 21)
|
||||
w.WriteInt(binary.BigEndian, p.SmscSequence)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppReceiptPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// CmppReceiptPkt struct.
|
||||
func (p *CmppReceiptPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
stat := r.ReadCString(7)
|
||||
p.Stat = string(stat)
|
||||
|
||||
submitTime := r.ReadCString(10)
|
||||
p.SubmitTime = string(submitTime)
|
||||
|
||||
doneTime := r.ReadCString(10)
|
||||
p.DoneTime = string(doneTime)
|
||||
|
||||
destTerminalId := r.ReadCString(21)
|
||||
p.DestTerminalId = string(destTerminalId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.SmscSequence)
|
||||
return r.Error()
|
||||
}
|
||||
Vendored
+507
@@ -0,0 +1,507 @@
|
||||
// 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 (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// errors for cmpp server
|
||||
var (
|
||||
ErrEmptyServerAddr = errors.New("cmpp server listen: empty server addr")
|
||||
ErrNoHandlers = errors.New("cmpp server: no connection handler")
|
||||
ErrUnsupportedPkt = errors.New("cmpp server read packet: receive a unsupported pkt")
|
||||
)
|
||||
|
||||
type Packet struct {
|
||||
Packer
|
||||
*Conn
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
*Packet
|
||||
Packer
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
ServeCmpp(*Response, *Packet, *log.Logger) (bool, error)
|
||||
}
|
||||
|
||||
// The HandlerFunc type is an adapter to allow the use of
|
||||
// ordinary functions as Cmpp handlers. If f is a function
|
||||
// with the appropriate signature, HandlerFunc(f) is a
|
||||
// Handler object that calls f.
|
||||
//
|
||||
// The first return value indicates whether to invoke next handler in
|
||||
// the chain of handlers.
|
||||
//
|
||||
// The second return value shows the error returned from the handler. And
|
||||
// if it is non-nil, server will close the client connection
|
||||
// after sending back the corresponding response.
|
||||
type HandlerFunc func(*Response, *Packet, *log.Logger) (bool, error)
|
||||
|
||||
// ServeCmpp calls f(r, p).
|
||||
func (f HandlerFunc) ServeCmpp(r *Response, p *Packet, l *log.Logger) (bool, error) {
|
||||
return f(r, p, l)
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Addr string
|
||||
Handler Handler
|
||||
|
||||
// protocol info
|
||||
Typ Type
|
||||
T time.Duration // interval betwwen two active tests
|
||||
N int32 // continuous send times when no response back
|
||||
|
||||
// ErrorLog specifies an optional logger for errors accepting
|
||||
// connections and unexpected behavior from handlers.
|
||||
// If nil, logging goes to os.Stderr via the log package's
|
||||
// standard logger.
|
||||
ErrorLog *log.Logger
|
||||
}
|
||||
|
||||
// A conn represents the server side of a Cmpp connection.
|
||||
type conn struct {
|
||||
*Conn
|
||||
server *Server // the Server on which the connection arrived
|
||||
|
||||
// for active test
|
||||
t time.Duration // interval betwwen two active tests
|
||||
n int32 // continuous send times when no response back
|
||||
done chan struct{}
|
||||
exceed chan struct{}
|
||||
counter int32
|
||||
}
|
||||
|
||||
// Serve accepts incoming connections on the Listener l, creating a
|
||||
// new service goroutine for each. The service goroutines read requests and
|
||||
// then call srv.Handler to reply to them.
|
||||
func (srv *Server) Serve(l net.Listener) error {
|
||||
defer l.Close()
|
||||
var tempDelay time.Duration // how long to sleep on accept failure
|
||||
for {
|
||||
rw, e := l.Accept()
|
||||
if e != nil {
|
||||
if ne, ok := e.(net.Error); ok && ne.Temporary() {
|
||||
if tempDelay == 0 {
|
||||
tempDelay = 5 * time.Millisecond
|
||||
} else {
|
||||
tempDelay *= 2
|
||||
}
|
||||
if max := 1 * time.Second; tempDelay > max {
|
||||
tempDelay = max
|
||||
}
|
||||
srv.ErrorLog.Printf("accept error: %v; retrying in %v", e, tempDelay)
|
||||
time.Sleep(tempDelay)
|
||||
continue
|
||||
}
|
||||
return e
|
||||
}
|
||||
tempDelay = 0
|
||||
c, err := srv.newConn(rw)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
srv.ErrorLog.Printf("accept a connection from %v\n", c.Conn.RemoteAddr())
|
||||
go c.serve()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *conn) readPacket() (*Response, error) {
|
||||
readTimeout := time.Second * 2
|
||||
i, err := c.Conn.RecvAndUnpackPkt(readTimeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
typ := c.server.Typ
|
||||
|
||||
var pkt *Packet
|
||||
var rsp *Response
|
||||
switch p := i.(type) {
|
||||
case *CmppConnReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
if typ == V30 {
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp3ConnRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp30 connect request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
} else {
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp2ConnRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp20 connect request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
}
|
||||
|
||||
case *Cmpp2SubmitReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp2SubmitRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp20 submit request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *Cmpp3SubmitReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp3SubmitRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp30 submit request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *Cmpp2FwdReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp2FwdRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp20 forward request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *Cmpp3FwdReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &Cmpp3FwdRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp30 forward request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *Cmpp2DeliverRspPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp20 deliver response from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *Cmpp3DeliverRspPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp30 deliver response from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *CmppActiveTestReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &CmppActiveTestRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp active request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *CmppActiveTestRspPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp active response from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *CmppTerminateReqPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
Packer: &CmppTerminateRspPkt{
|
||||
SeqId: p.SeqId,
|
||||
},
|
||||
SeqId: p.SeqId,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp terminate request from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
|
||||
case *CmppTerminateRspPkt:
|
||||
pkt = &Packet{
|
||||
Packer: p,
|
||||
Conn: c.Conn,
|
||||
}
|
||||
|
||||
rsp = &Response{
|
||||
Packet: pkt,
|
||||
}
|
||||
c.server.ErrorLog.Printf("receive a cmpp terminate response from %v[%d]\n",
|
||||
c.Conn.RemoteAddr(), p.SeqId)
|
||||
default:
|
||||
return nil, NewOpError(ErrUnsupportedPkt,
|
||||
fmt.Sprintf("readPacket: receive unsupported packet type: %#v", p))
|
||||
}
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
// Close the connection.
|
||||
func (c *conn) close() {
|
||||
p := &CmppTerminateReqPkt{}
|
||||
|
||||
err := c.Conn.SendPkt(p, <-c.Conn.SeqId)
|
||||
if err != nil {
|
||||
c.server.ErrorLog.Printf("send cmpp terminate request packet to %v error: %v\n", c.Conn.RemoteAddr(), err)
|
||||
}
|
||||
|
||||
close(c.done)
|
||||
c.server.ErrorLog.Printf("close connection with %v!\n", c.Conn.RemoteAddr())
|
||||
c.Conn.Close()
|
||||
}
|
||||
|
||||
func (c *conn) finishPacket(r *Response) error {
|
||||
if _, ok := r.Packet.Packer.(*CmppActiveTestRspPkt); ok {
|
||||
atomic.AddInt32(&c.counter, -1)
|
||||
return nil
|
||||
}
|
||||
|
||||
if r.Packer == nil {
|
||||
// For response packet received, it need not
|
||||
// to send anything back.
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.Conn.SendPkt(r.Packer, r.SeqId)
|
||||
}
|
||||
|
||||
func startActiveTest(c *conn) {
|
||||
exceed, done := make(chan struct{}), make(chan struct{})
|
||||
c.done = done
|
||||
c.exceed = exceed
|
||||
|
||||
go func() {
|
||||
t := time.NewTicker(c.t)
|
||||
defer t.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
// once conn close, the goroutine should exit
|
||||
return
|
||||
case <-t.C:
|
||||
// check whether c.counter exceeds
|
||||
if atomic.LoadInt32(&c.counter) >= c.n {
|
||||
c.server.ErrorLog.Printf("no cmpp active test response returned from %v for %d times!",
|
||||
c.Conn.RemoteAddr(), c.n)
|
||||
exceed <- struct{}{}
|
||||
break
|
||||
}
|
||||
// send a active test packet to peer, increase the active test counter
|
||||
p := &CmppActiveTestReqPkt{}
|
||||
err := c.Conn.SendPkt(p, <-c.Conn.SeqId)
|
||||
if err != nil {
|
||||
c.server.ErrorLog.Printf("send cmpp active test request to %v error: %v", c.Conn.RemoteAddr(), err)
|
||||
} else {
|
||||
atomic.AddInt32(&c.counter, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Serve a new connection.
|
||||
func (c *conn) serve() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
c.server.ErrorLog.Printf("panic serving %v: %v\n", c.Conn.RemoteAddr(), err)
|
||||
}
|
||||
}()
|
||||
|
||||
defer c.close()
|
||||
|
||||
// start a goroutine for sending active test.
|
||||
startActiveTest(c)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.exceed:
|
||||
return // close the connection.
|
||||
default:
|
||||
}
|
||||
|
||||
r, err := c.readPacket()
|
||||
if err != nil {
|
||||
if e, ok := err.(net.Error); ok && e.Timeout() {
|
||||
continue
|
||||
}
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
c.server.ErrorLog.Printf(
|
||||
"read/unpack packet failed remote=%v protocol=%s err_type=%T err=%v",
|
||||
c.Conn.RemoteAddr(), c.Conn.Typ, err, err,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
_, err = c.server.Handler.ServeCmpp(r, r.Packet, c.server.ErrorLog)
|
||||
if err1 := c.finishPacket(r); err1 != nil {
|
||||
c.server.ErrorLog.Printf(
|
||||
"send response packet failed remote=%v protocol=%s packet_type=%T seq=%d err_type=%T err=%v",
|
||||
c.Conn.RemoteAddr(), c.Conn.Typ, r.Packer, r.SeqId, err1, err1,
|
||||
)
|
||||
break
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.server.ErrorLog.Printf(
|
||||
"handler failed remote=%v protocol=%s packet_type=%T seq=%d err_type=%T err=%v",
|
||||
c.Conn.RemoteAddr(), c.Conn.Typ, r.Packet.Packer, r.SeqId, err, err,
|
||||
)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new connection from rwc.
|
||||
func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
|
||||
c = new(conn)
|
||||
c.server = srv
|
||||
c.Conn = NewConn(rwc, srv.Typ)
|
||||
c.Conn.SetState(CONN_CONNECTED)
|
||||
c.n = c.server.N
|
||||
c.t = c.server.T
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (srv *Server) listenAndServe() error {
|
||||
if srv.Addr == "" {
|
||||
return ErrEmptyServerAddr
|
||||
}
|
||||
ln, err := net.Listen("tcp", srv.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
|
||||
}
|
||||
|
||||
// ListenAndServe listens on the TCP network address addr
|
||||
// and then calls Serve with handler to handle requests.
|
||||
func ListenAndServe(addr string, typ Type, t time.Duration, n int32, logWriter io.Writer, handlers ...Handler) error {
|
||||
if addr == "" {
|
||||
return ErrEmptyServerAddr
|
||||
}
|
||||
|
||||
if handlers == nil {
|
||||
return ErrNoHandlers
|
||||
}
|
||||
|
||||
var handler Handler
|
||||
handler = HandlerFunc(func(r *Response, p *Packet, l *log.Logger) (bool, error) {
|
||||
for _, h := range handlers {
|
||||
next, err := h.ServeCmpp(r, p, l)
|
||||
if err != nil || !next {
|
||||
return next, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
|
||||
if logWriter == nil {
|
||||
logWriter = os.Stderr
|
||||
}
|
||||
server := &Server{Addr: addr, Handler: handler, Typ: typ,
|
||||
T: t, N: n,
|
||||
ErrorLog: log.New(logWriter, "cmppserver: ", log.LstdFlags)}
|
||||
return server.listenAndServe()
|
||||
}
|
||||
|
||||
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||
// connections. It's used by ListenAndServe so
|
||||
// dead TCP connections (e.g. closing laptop mid-download) eventually
|
||||
// go away. the tcpKeepAliveListener's implementation is copied from
|
||||
// http package.
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(1 * time.Minute) // 1min
|
||||
return tc, nil
|
||||
}
|
||||
Vendored
+453
@@ -0,0 +1,453 @@
|
||||
// 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 (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Packet length const for cmpp submit request and response packets.
|
||||
const (
|
||||
Cmpp2SubmitReqPktMaxLen uint32 = 12 + 2265 //2277d, 0x8e5
|
||||
Cmpp2SubmitRspPktLen uint32 = 12 + 8 + 1 //21d, 0x15
|
||||
|
||||
Cmpp3SubmitReqPktMaxLen uint32 = 12 + 3479 //3491d, 0xda3
|
||||
Cmpp3SubmitRspPktLen uint32 = 12 + 8 + 4 //24d, 0x18
|
||||
)
|
||||
|
||||
// Errors for result in submit resp.
|
||||
var (
|
||||
ErrnoSubmitInvalidStruct uint8 = 1
|
||||
ErrnoSubmitInvalidCommandId uint8 = 2
|
||||
ErrnoSubmitInvalidSequence uint8 = 3
|
||||
ErrnoSubmitInvalidMsgLength uint8 = 4
|
||||
ErrnoSubmitInvalidFeeCode uint8 = 5
|
||||
ErrnoSubmitExceedMaxMsgLength uint8 = 6
|
||||
ErrnoSubmitInvalidServiceId uint8 = 7
|
||||
ErrnoSubmitNotPassFlowControl uint8 = 8
|
||||
ErrnoSubmitNotServeFeeTerminalId uint8 = 9
|
||||
ErrnoSubmitInvalidSrcId uint8 = 10
|
||||
ErrnoSubmitInvalidMsgSrc uint8 = 11
|
||||
ErrnoSubmitInvalidFeeTerminalId uint8 = 12
|
||||
ErrnoSubmitInvalidDestTerminalId uint8 = 13
|
||||
|
||||
SubmitRspResultErrMap = map[uint8]error{
|
||||
ErrnoSubmitInvalidStruct: errSubmitInvalidStruct,
|
||||
ErrnoSubmitInvalidCommandId: errSubmitInvalidCommandId,
|
||||
ErrnoSubmitInvalidSequence: errSubmitInvalidSequence,
|
||||
ErrnoSubmitInvalidMsgLength: errSubmitInvalidMsgLength,
|
||||
ErrnoSubmitInvalidFeeCode: errSubmitInvalidFeeCode,
|
||||
ErrnoSubmitExceedMaxMsgLength: errSubmitExceedMaxMsgLength,
|
||||
ErrnoSubmitInvalidServiceId: errSubmitInvalidServiceId,
|
||||
ErrnoSubmitNotPassFlowControl: errSubmitNotPassFlowControl,
|
||||
ErrnoSubmitNotServeFeeTerminalId: errSubmitNotServeFeeTerminalId,
|
||||
ErrnoSubmitInvalidSrcId: errSubmitInvalidSrcId,
|
||||
ErrnoSubmitInvalidMsgSrc: errSubmitInvalidMsgSrc,
|
||||
ErrnoSubmitInvalidFeeTerminalId: errSubmitInvalidFeeTerminalId,
|
||||
ErrnoSubmitInvalidDestTerminalId: errSubmitInvalidDestTerminalId,
|
||||
}
|
||||
|
||||
errSubmitInvalidStruct = errors.New("submit response status: invalid protocol structure")
|
||||
errSubmitInvalidCommandId = errors.New("submit response status: invalid command id")
|
||||
errSubmitInvalidSequence = errors.New("submit response status: invalid message sequence")
|
||||
errSubmitInvalidMsgLength = errors.New("submit response status: invalid message length")
|
||||
errSubmitInvalidFeeCode = errors.New("submit response status: invalid fee code")
|
||||
errSubmitExceedMaxMsgLength = errors.New("submit response status: exceed max message length")
|
||||
errSubmitInvalidServiceId = errors.New("submit response status: invalid service id")
|
||||
errSubmitNotPassFlowControl = errors.New("submit response status: not pass the flow control")
|
||||
errSubmitNotServeFeeTerminalId = errors.New("submit response status: feeTerminalId is not served")
|
||||
errSubmitInvalidSrcId = errors.New("submit response status: invalid srcId")
|
||||
errSubmitInvalidMsgSrc = errors.New("submit response status: invalid msgSrc")
|
||||
errSubmitInvalidFeeTerminalId = errors.New("submit response status: invalid feeTerminalId")
|
||||
errSubmitInvalidDestTerminalId = errors.New("submit response status: invalid destTerminalId")
|
||||
)
|
||||
|
||||
type Cmpp2SubmitReqPkt struct {
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
RegisteredDelivery uint8
|
||||
MsgLevel uint8
|
||||
ServiceId string
|
||||
FeeUserType uint8
|
||||
FeeTerminalId string
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
MsgSrc string
|
||||
FeeType string
|
||||
FeeCode string
|
||||
ValidTime string
|
||||
AtTime string
|
||||
SrcId string
|
||||
DestUsrTl uint8
|
||||
DestTerminalId []string
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
Reserve string
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp2SubmitRspPkt struct {
|
||||
MsgId uint64
|
||||
Result uint8
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp3SubmitReqPkt struct {
|
||||
MsgId uint64
|
||||
PkTotal uint8
|
||||
PkNumber uint8
|
||||
RegisteredDelivery uint8
|
||||
MsgLevel uint8
|
||||
ServiceId string
|
||||
FeeUserType uint8
|
||||
FeeTerminalId string
|
||||
FeeTerminalType uint8
|
||||
TpPid uint8
|
||||
TpUdhi uint8
|
||||
MsgFmt uint8
|
||||
MsgSrc string
|
||||
FeeType string
|
||||
FeeCode string
|
||||
ValidTime string
|
||||
AtTime string
|
||||
SrcId string
|
||||
DestUsrTl uint8
|
||||
DestTerminalId []string
|
||||
DestTerminalType uint8
|
||||
MsgLength uint8
|
||||
MsgContent string
|
||||
LinkId string
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
type Cmpp3SubmitRspPkt struct {
|
||||
MsgId uint64
|
||||
Result uint32
|
||||
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2SubmitReqPkt to bytes stream for client side.
|
||||
// Before calling Pack, you should initialize a Cmpp2SubmitReqPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp2SubmitReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 117 + uint32(p.DestUsrTl)*21 + 1 + uint32(p.MsgLength) + 8
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_SUBMIT)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
|
||||
if p.PkTotal == 0 && p.PkNumber == 0 {
|
||||
p.PkTotal, p.PkNumber = 1, 1
|
||||
}
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
w.WriteByte(p.RegisteredDelivery)
|
||||
w.WriteByte(p.MsgLevel)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.FeeUserType)
|
||||
w.WriteFixedSizeString(p.FeeTerminalId, 21)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.MsgSrc, 6)
|
||||
w.WriteFixedSizeString(p.FeeType, 2)
|
||||
w.WriteFixedSizeString(p.FeeCode, 6)
|
||||
w.WriteFixedSizeString(p.ValidTime, 17)
|
||||
w.WriteFixedSizeString(p.AtTime, 17)
|
||||
w.WriteFixedSizeString(p.SrcId, 21)
|
||||
w.WriteByte(p.DestUsrTl)
|
||||
|
||||
for _, d := range p.DestTerminalId {
|
||||
w.WriteFixedSizeString(d, 21)
|
||||
}
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.Reserve, 8)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2SubmitReqPkt variable.
|
||||
// Usually it is used in server side. After unpack, you will get all value of fields in
|
||||
// Cmpp2SubmitReqPkt struct.
|
||||
func (p *Cmpp2SubmitReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
p.RegisteredDelivery = r.ReadByte()
|
||||
p.MsgLevel = r.ReadByte()
|
||||
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
|
||||
p.FeeUserType = r.ReadByte()
|
||||
|
||||
feeTerminalId := r.ReadCString(21)
|
||||
p.FeeTerminalId = string(feeTerminalId)
|
||||
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
msgSrc := r.ReadCString(6)
|
||||
p.MsgSrc = string(msgSrc)
|
||||
|
||||
feeType := make([]byte, 2)
|
||||
r.ReadBytes(feeType)
|
||||
p.FeeType = string(feeType)
|
||||
|
||||
feeCode := r.ReadCString(6)
|
||||
p.FeeCode = string(feeCode)
|
||||
|
||||
validTime := r.ReadCString(17)
|
||||
p.ValidTime = string(validTime)
|
||||
|
||||
atTime := r.ReadCString(17)
|
||||
p.AtTime = string(atTime)
|
||||
|
||||
srcId := r.ReadCString(21)
|
||||
p.SrcId = string(srcId)
|
||||
|
||||
p.DestUsrTl = r.ReadByte()
|
||||
|
||||
for i := 0; i < int(p.DestUsrTl); i++ {
|
||||
destTerminalId := r.ReadCString(21)
|
||||
p.DestTerminalId = append(p.DestTerminalId, string(destTerminalId))
|
||||
}
|
||||
|
||||
p.MsgLength = r.ReadByte()
|
||||
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
reserve := r.ReadCString(8)
|
||||
p.Reserve = string(reserve)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp2SubmitRspPkt to bytes stream for Server side.
|
||||
// Before calling Pack, you should initialize a Cmpp2SubmitRspPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp2SubmitRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 8 + 1
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_SUBMIT_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteByte(p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp2SubmitRspPkt variable.
|
||||
// Usually it is used in client side. After unpack, you will get all value of fields in
|
||||
// Cmpp2SubmitRspPkt struct.
|
||||
func (p *Cmpp2SubmitRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
p.Result = r.ReadByte()
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3SubmitReqPkt to bytes stream for client side.
|
||||
// Before calling Pack, you should initialize a Cmpp3SubmitReqPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp3SubmitReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 129 + uint32(p.DestUsrTl)*32 + 1 + 1 + uint32(p.MsgLength) + 20
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_SUBMIT)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
|
||||
if p.PkTotal == 0 && p.PkNumber == 0 {
|
||||
p.PkTotal, p.PkNumber = 1, 1
|
||||
}
|
||||
w.WriteByte(p.PkTotal)
|
||||
w.WriteByte(p.PkNumber)
|
||||
w.WriteByte(p.RegisteredDelivery)
|
||||
w.WriteByte(p.MsgLevel)
|
||||
w.WriteFixedSizeString(p.ServiceId, 10)
|
||||
w.WriteByte(p.FeeUserType)
|
||||
w.WriteFixedSizeString(p.FeeTerminalId, 32)
|
||||
w.WriteByte(p.FeeTerminalType)
|
||||
w.WriteByte(p.TpPid)
|
||||
w.WriteByte(p.TpUdhi)
|
||||
w.WriteByte(p.MsgFmt)
|
||||
w.WriteFixedSizeString(p.MsgSrc, 6)
|
||||
w.WriteFixedSizeString(p.FeeType, 2)
|
||||
w.WriteFixedSizeString(p.FeeCode, 6)
|
||||
w.WriteFixedSizeString(p.ValidTime, 17)
|
||||
w.WriteFixedSizeString(p.AtTime, 17)
|
||||
w.WriteFixedSizeString(p.SrcId, 21)
|
||||
w.WriteByte(p.DestUsrTl)
|
||||
|
||||
for _, d := range p.DestTerminalId {
|
||||
w.WriteFixedSizeString(d, 32)
|
||||
}
|
||||
w.WriteByte(p.DestTerminalType)
|
||||
w.WriteByte(p.MsgLength)
|
||||
w.WriteString(p.MsgContent)
|
||||
w.WriteFixedSizeString(p.LinkId, 20)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3SubmitReqPkt variable.
|
||||
// Usually it is used in server side. After unpack, you will get all value of fields in
|
||||
// Cmpp3SubmitReqPkt struct.
|
||||
func (p *Cmpp3SubmitReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
|
||||
p.PkTotal = r.ReadByte()
|
||||
p.PkNumber = r.ReadByte()
|
||||
p.RegisteredDelivery = r.ReadByte()
|
||||
p.MsgLevel = r.ReadByte()
|
||||
|
||||
serviceId := r.ReadCString(10)
|
||||
p.ServiceId = string(serviceId)
|
||||
|
||||
p.FeeUserType = r.ReadByte()
|
||||
|
||||
feeTerminalId := r.ReadCString(32)
|
||||
p.FeeTerminalId = string(feeTerminalId)
|
||||
|
||||
p.FeeTerminalType = r.ReadByte()
|
||||
p.TpPid = r.ReadByte()
|
||||
p.TpUdhi = r.ReadByte()
|
||||
p.MsgFmt = r.ReadByte()
|
||||
|
||||
msgSrc := r.ReadCString(6)
|
||||
p.MsgSrc = string(msgSrc)
|
||||
|
||||
feeType := make([]byte, 2)
|
||||
r.ReadBytes(feeType)
|
||||
p.FeeType = string(feeType)
|
||||
|
||||
feeCode := r.ReadCString(6)
|
||||
p.FeeCode = string(feeCode)
|
||||
|
||||
validTime := r.ReadCString(17)
|
||||
p.ValidTime = string(validTime)
|
||||
|
||||
atTime := r.ReadCString(17)
|
||||
p.AtTime = string(atTime)
|
||||
|
||||
srcId := r.ReadCString(21)
|
||||
p.SrcId = string(srcId)
|
||||
|
||||
p.DestUsrTl = r.ReadByte()
|
||||
|
||||
for i := 0; i < int(p.DestUsrTl); i++ {
|
||||
destTerminalId := r.ReadCString(32)
|
||||
p.DestTerminalId = append(p.DestTerminalId, string(destTerminalId))
|
||||
}
|
||||
|
||||
p.DestTerminalType = r.ReadByte()
|
||||
p.MsgLength = r.ReadByte()
|
||||
|
||||
msgContent := make([]byte, p.MsgLength)
|
||||
r.ReadBytes(msgContent)
|
||||
p.MsgContent = string(msgContent)
|
||||
|
||||
linkId := r.ReadCString(20)
|
||||
p.LinkId = string(linkId)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the Cmpp3SubmitRspPkt to bytes stream for Server side.
|
||||
// Before calling Pack, you should initialize a Cmpp3SubmitRspPkt variable
|
||||
// with correct field value.
|
||||
func (p *Cmpp3SubmitRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen uint32 = CMPP_HEADER_LEN + 8 + 4
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_SUBMIT_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
// Pack Body
|
||||
w.WriteInt(binary.BigEndian, p.MsgId)
|
||||
w.WriteInt(binary.BigEndian, p.Result)
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a Cmpp3SubmitRspPkt variable.
|
||||
// Usually it is used in client side. After unpack, you will get all value of fields in
|
||||
// Cmpp3SubmitRspPkt struct.
|
||||
func (p *Cmpp3SubmitRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
|
||||
r.ReadInt(binary.BigEndian, &p.MsgId)
|
||||
r.ReadInt(binary.BigEndian, &p.Result)
|
||||
|
||||
return r.Error()
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
// 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 "encoding/binary"
|
||||
|
||||
// Packet length const for cmpp terminate request and response packets.
|
||||
const (
|
||||
CmppTerminateReqPktLen uint32 = 12 //12d, 0xc
|
||||
CmppTerminateRspPktLen uint32 = 12 //12d, 0xc
|
||||
)
|
||||
|
||||
type CmppTerminateReqPkt struct {
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
type CmppTerminateRspPkt struct {
|
||||
// session info
|
||||
SeqId uint32
|
||||
}
|
||||
|
||||
// Pack packs the CmppTerminateReqPkt to bytes stream for client side.
|
||||
func (p *CmppTerminateReqPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = CmppTerminateReqPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_TERMINATE)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppTerminateReqPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// CmppTerminateReqPkt struct.
|
||||
func (p *CmppTerminateReqPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// Pack packs the CmppTerminateRspPkt to bytes stream for client side.
|
||||
func (p *CmppTerminateRspPkt) Pack(seqId uint32) ([]byte, error) {
|
||||
var pktLen = CmppTerminateRspPktLen
|
||||
|
||||
var w = newPacketWriter(pktLen)
|
||||
|
||||
// Pack header
|
||||
w.WriteInt(binary.BigEndian, pktLen)
|
||||
w.WriteInt(binary.BigEndian, CMPP_TERMINATE_RESP)
|
||||
w.WriteInt(binary.BigEndian, seqId)
|
||||
p.SeqId = seqId
|
||||
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// Unpack unpack the binary byte stream to a CmppTerminateRspPkt variable.
|
||||
// After unpack, you will get all value of fields in
|
||||
// CmppTerminateRspPkt struct.
|
||||
func (p *CmppTerminateRspPkt) Unpack(data []byte) error {
|
||||
var r = newPacketReader(data)
|
||||
|
||||
// Sequence Id
|
||||
r.ReadInt(binary.BigEndian, &p.SeqId)
|
||||
return r.Error()
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// 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 cmpputils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/text/encoding/simplifiedchinese"
|
||||
"golang.org/x/text/encoding/unicode"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
var ErrInvalidUtf8Rune = errors.New("Not Invalid Utf8 runes")
|
||||
|
||||
func IsBigEndian() bool {
|
||||
var i uint16 = 0x1234
|
||||
var p *[2]byte = (*[2]byte)(unsafe.Pointer(&i))
|
||||
if (*p)[0] == 0x12 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TimeStamp2Str converts a timestamp(MMDDHHMMSS) int to a string(10 bytes).
|
||||
func TimeStamp2Str(t uint32) string {
|
||||
return fmt.Sprintf("%010d", t)
|
||||
}
|
||||
|
||||
func Utf8ToUcs2(in string) (string, error) {
|
||||
if !utf8.ValidString(in) {
|
||||
return "", ErrInvalidUtf8Rune
|
||||
}
|
||||
|
||||
r := bytes.NewReader([]byte(in))
|
||||
t := transform.NewReader(r, unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewEncoder()) //UTF-16 bigendian, no-bom
|
||||
out, err := ioutil.ReadAll(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func Ucs2ToUtf8(in string) (string, error) {
|
||||
r := bytes.NewReader([]byte(in))
|
||||
t := transform.NewReader(r, unicode.UTF16(unicode.BigEndian, unicode.IgnoreBOM).NewDecoder()) //UTF-16 bigendian, no-bom
|
||||
out, err := ioutil.ReadAll(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func Utf8ToGB18030(in string) (string, error) {
|
||||
if !utf8.ValidString(in) {
|
||||
return "", ErrInvalidUtf8Rune
|
||||
}
|
||||
|
||||
r := bytes.NewReader([]byte(in))
|
||||
t := transform.NewReader(r, simplifiedchinese.GB18030.NewEncoder())
|
||||
out, err := ioutil.ReadAll(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func GB18030ToUtf8(in string) (string, error) {
|
||||
r := bytes.NewReader([]byte(in))
|
||||
t := transform.NewReader(r, simplifiedchinese.GB18030.NewDecoder())
|
||||
out, err := ioutil.ReadAll(t)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func OctetString(s string, fixedLength int) string {
|
||||
length := len(s)
|
||||
if length == fixedLength {
|
||||
return s
|
||||
}
|
||||
|
||||
if length > fixedLength {
|
||||
return s[length-fixedLength:]
|
||||
}
|
||||
|
||||
return strings.Join([]string{s, string(make([]byte, fixedLength-length))}, "")
|
||||
}
|
||||
Reference in New Issue
Block a user