fix: log cmpp packet decode failures
This commit is contained in:
@@ -1009,6 +1009,7 @@
|
|||||||
- submit 被接受后返回 CMPP SubmitResp 成功,并在真实数据库创建 `sourceType=cmpp` 的发送记录,进入真实发送链路。
|
- submit 被接受后返回 CMPP SubmitResp 成功,并在真实数据库创建 `sourceType=cmpp` 的发送记录,进入真实发送链路。
|
||||||
- submit 内容不匹配审核模板、余额不足、短信接口关闭、无可用通道时返回明确失败,不得伪造成功。
|
- submit 内容不匹配审核模板、余额不足、短信接口关闭、无可用通道时返回明确失败,不得伪造成功。
|
||||||
- Gateway 对每次 submit 记录 `submit_received` 和 `submit_accepted`/`submit_rejected`;日志可按账号、IP、sequenceId、号码和 messageId 定位,拒绝时包含 NestJS 真实业务原因和 CMPP result,但不包含明文短信正文。
|
- Gateway 对每次 submit 记录 `submit_received` 和 `submit_accepted`/`submit_rejected`;日志可按账号、IP、sequenceId、号码和 messageId 定位,拒绝时包含 NestJS 真实业务原因和 CMPP result,但不包含明文短信正文。
|
||||||
|
- CMPP 包在进入 handler 前因长度、命令字、读包或 Unpack 失败时,Gateway 记录 `read/unpack packet failed`、远端地址、协议模式、错误类型和原始错误,不得静默断开。
|
||||||
|
|
||||||
### TC-GW-007 CMPP 客户到上游 SMSC 完整闭环
|
### TC-GW-007 CMPP 客户到上游 SMSC 完整闭环
|
||||||
|
|
||||||
|
|||||||
@@ -657,12 +657,14 @@ npm run verify:phase8
|
|||||||
- Gateway 入站 Submit 日志增加 `submit_received`、`submit_accepted`、`submit_rejected` 结构化事件,同时记录 CONNECT 声明的客户协议版本与 Go 实际解包类型,并记录账号、客户 IP、sequenceId、号码、srcId、编码、分片、CMPP result、平台 messageId、CMPP Msg_Id 和处理耗时。
|
- Gateway 入站 Submit 日志增加 `submit_received`、`submit_accepted`、`submit_rejected` 结构化事件,同时记录 CONNECT 声明的客户协议版本与 Go 实际解包类型,并记录账号、客户 IP、sequenceId、号码、srcId、编码、分片、CMPP result、平台 messageId、CMPP Msg_Id 和处理耗时。
|
||||||
- Gateway HTTP 回调在 NestJS 返回非 2xx 时保留最多 64KB 响应体,客户 Submit 失败日志可直接显示模板不匹配、IP 白名单、余额或路由等真实业务原因,不再只显示 HTTP 状态码。
|
- Gateway HTTP 回调在 NestJS 返回非 2xx 时保留最多 64KB 响应体,客户 Submit 失败日志可直接显示模板不匹配、IP 白名单、余额或路由等真实业务原因,不再只显示 HTTP 状态码。
|
||||||
- 日志不记录明文短信正文,仅记录字符数和 MD5 哈希,便于比对同一内容且避免日志泄露。
|
- 日志不记录明文短信正文,仅记录字符数和 MD5 哈希,便于比对同一内容且避免日志泄露。
|
||||||
|
- 将当前 gocmpp 版本固定为仓库内小型 fork,仅在 server 循环补充底层诊断:包在进入业务 handler 之前发生长度、命令字、包体读取或 Unpack 失败时,记录 `read/unpack packet failed`、远端地址、库解析协议模式、Go 错误类型和原始错误;正常 EOF 断开不记为解包失败。
|
||||||
|
|
||||||
### 验证状态
|
### 验证状态
|
||||||
|
|
||||||
- `go test ./internal/inbound -count=1`:通过。
|
- `go test ./internal/inbound -count=1`:通过。
|
||||||
- `go test ./... -count=1`:通过。
|
- `go test ./... -count=1`:通过。
|
||||||
- `go build ./cmd/gateway`:通过。
|
- `go build ./cmd/gateway`:通过。
|
||||||
|
- 真实 TCP 非法包用例:向入站端口写入非法 `total_length`,确认业务 handler 未执行时仍产生 `read/unpack packet failed` 日志。
|
||||||
|
|
||||||
## 2026-07-07 Gateway 上游提交与下游 Deliver 闭环补齐
|
## 2026-07-07 Gateway 上游提交与下游 Deliver 闭环补齐
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ module cmpp-platform/gateway
|
|||||||
|
|
||||||
go 1.26
|
go 1.26
|
||||||
|
|
||||||
|
replace github.com/bigwhite/gocmpp => ./third_party/gocmpp
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
github.com/alicebob/gopher-json v0.0.0-20230218143504-906a9b012302 // indirect
|
||||||
github.com/alicebob/miniredis/v2 v2.34.0 // indirect
|
github.com/alicebob/miniredis/v2 v2.34.0 // indirect
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type Server struct {
|
|||||||
Addr string
|
Addr string
|
||||||
APIBaseURL string
|
APIBaseURL string
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
|
LogWriter io.Writer
|
||||||
PendingFlushInterval time.Duration
|
PendingFlushInterval time.Duration
|
||||||
PresenceStore PresenceStore
|
PresenceStore PresenceStore
|
||||||
RecoveryStore RecoveryStore
|
RecoveryStore RecoveryStore
|
||||||
@@ -116,7 +117,7 @@ func (s Server) ListenAndServe() error {
|
|||||||
s.logRecoveryCandidates(log.Default())
|
s.logRecoveryCandidates(log.Default())
|
||||||
go s.recoverPendingCandidates(log.Default())
|
go s.recoverPendingCandidates(log.Default())
|
||||||
go s.runPendingFlusher(log.Default())
|
go s.runPendingFlusher(log.Default())
|
||||||
return cmpp.ListenAndServe(addr, cmpp.V30, 30*time.Second, 3, nil,
|
return cmpp.ListenAndServe(addr, cmpp.V30, 30*time.Second, 3, s.LogWriter,
|
||||||
cmpp.HandlerFunc(s.handleLogin),
|
cmpp.HandlerFunc(s.handleLogin),
|
||||||
cmpp.HandlerFunc(s.handleSubmit),
|
cmpp.HandlerFunc(s.handleSubmit),
|
||||||
)
|
)
|
||||||
@@ -158,7 +159,7 @@ func (s Server) handleLogin(response *cmpp.Response, packet *cmpp.Packet, logger
|
|||||||
go s.flushPending(defaultString(auth.Account, account), logger)
|
go s.flushPending(defaultString(auth.Account, account), logger)
|
||||||
logger.Printf(
|
logger.Printf(
|
||||||
"cmpp inbound event=login_accepted protocol=%s requested_version=0x%02x response_version=0x30 account=%s remote=%s",
|
"cmpp inbound event=login_accepted protocol=%s requested_version=0x%02x response_version=0x30 account=%s remote=%s",
|
||||||
cmppVersionName(req.Version), req.Version, account, packet.Conn.Conn.RemoteAddr(),
|
cmppVersionName(req.Version), uint8(req.Version), account, packet.Conn.Conn.RemoteAddr(),
|
||||||
)
|
)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package inbound
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -26,6 +28,23 @@ type memoryRecoveryStore struct {
|
|||||||
completed []DownstreamRecoveryStatus
|
completed []DownstreamRecoveryStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type synchronizedBuffer struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
buffer bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *synchronizedBuffer) Write(data []byte) (int, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
return b.buffer.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *synchronizedBuffer) String() string {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
return b.buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
func (m *memoryPresenceStore) TouchAccount(_ context.Context, snapshot DownstreamPresence) error {
|
func (m *memoryPresenceStore) TouchAccount(_ context.Context, snapshot DownstreamPresence) error {
|
||||||
if m.snapshots == nil {
|
if m.snapshots == nil {
|
||||||
m.snapshots = map[string]DownstreamPresence{}
|
m.snapshots = map[string]DownstreamPresence{}
|
||||||
@@ -181,6 +200,33 @@ func TestPostIncludesAPIErrorResponseBody(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInboundServerLogsReadUnpackFailure(t *testing.T) {
|
||||||
|
addr := reserveTCPAddr(t)
|
||||||
|
var logs synchronizedBuffer
|
||||||
|
go func() {
|
||||||
|
_ = (Server{Addr: addr, LogWriter: &logs}).ListenAndServe()
|
||||||
|
}()
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
|
||||||
|
conn, err := net.DialTimeout("tcp", addr, time.Second)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("connect inbound server: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err := binary.Write(conn, binary.BigEndian, uint32(1)); err != nil {
|
||||||
|
t.Fatalf("write invalid packet length: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
deadline := time.Now().Add(2 * time.Second)
|
||||||
|
for time.Now().Before(deadline) {
|
||||||
|
if strings.Contains(logs.String(), "read/unpack packet failed") && strings.Contains(logs.String(), "total_length") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(20 * time.Millisecond)
|
||||||
|
}
|
||||||
|
t.Fatalf("missing read/unpack failure log: %s", logs.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestNormalizeInboundSubmitSupportsCMPP2AndCMPP3(t *testing.T) {
|
func TestNormalizeInboundSubmitSupportsCMPP2AndCMPP3(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
|||||||
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