fix: complete first version issue remediation

This commit is contained in:
hectorzhao
2026-07-11 10:14:37 +08:00
parent 709ac97764
commit 208a6c23f8
73 changed files with 1549 additions and 245 deletions
+127 -22
View File
@@ -7,6 +7,7 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net"
"net/http"
@@ -86,6 +87,7 @@ type DownstreamUplink struct {
type downstreamSession struct {
messageID string
account string
protocol string
srcID string
phoneNumber string
gatewayMsgID uint64
@@ -143,6 +145,7 @@ func (s Server) handleLogin(response *cmpp.Response, packet *cmpp.Packet, logger
resp.AuthIsmg = string(authISMG[:])
session := downstreamSession{
account: strings.TrimSpace(defaultString(auth.Account, account)),
protocol: cmppVersionName(req.Version),
srcID: strings.TrimSpace(auth.Account),
remoteIP: remoteIP(packet.Conn.Conn.RemoteAddr()),
connectedAt: time.Now().UTC(),
@@ -153,59 +156,150 @@ func (s Server) handleLogin(response *cmpp.Response, packet *cmpp.Packet, logger
}
rememberAccount(session)
go s.flushPending(defaultString(auth.Account, account), logger)
logger.Printf("cmpp inbound account=%s login ok remote=%s", account, packet.Conn.Conn.RemoteAddr())
logger.Printf(
"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(),
)
return false, nil
}
func (s Server) handleSubmit(response *cmpp.Response, packet *cmpp.Packet, logger *log.Logger) (bool, error) {
req, ok := packet.Packer.(*cmpp.Cmpp3SubmitReqPkt)
req, ok := normalizeInboundSubmit(packet.Packer)
if !ok {
return true, nil
}
resp := response.Packer.(*cmpp.Cmpp3SubmitRspPkt)
account := strings.TrimRight(req.MsgSrc, "\x00")
account := strings.TrimRight(req.msgSrc, "\x00")
phone := ""
if len(req.DestTerminalId) > 0 {
phone = strings.TrimRight(req.DestTerminalId[0], "\x00")
if len(req.destTerminalIDs) > 0 {
phone = strings.TrimRight(req.destTerminalIDs[0], "\x00")
}
content, err := decodeContent(req.MsgFmt, req.MsgContent)
remote := packet.Conn.Conn.RemoteAddr()
clientProtocol := inboundClientProtocol(account, packet.Conn, req.protocol)
logger.Printf(
"cmpp inbound event=submit_received protocol=%s packet_type=%s account=%s remote=%s seq=%d phone=%s src_id=%s msg_fmt=%d pk=%d/%d dest_count=%d content_bytes=%d",
clientProtocol, req.protocol, account, remote, req.sequenceID, phone, strings.TrimSpace(req.srcID), req.msgFmt,
req.pkNumber, req.pkTotal, len(req.destTerminalIDs), len(req.msgContent),
)
content, err := decodeContent(req.msgFmt, req.msgContent)
if err != nil {
logger.Printf("cmpp inbound decode submit failed account=%s seq=%d err=%v", account, req.SeqId, err)
resp.Result = 9
logger.Printf(
"cmpp inbound event=submit_rejected protocol=%s packet_type=%s account=%s remote=%s seq=%d phone=%s result=9 stage=decode reason=%q",
clientProtocol, req.protocol, account, remote, req.sequenceID, phone, err,
)
setInboundSubmitResponse(response.Packer, 0, 9)
return false, nil
}
result, err := s.submit(packet.Conn.Conn.RemoteAddr(), submitRequest{
contentHash := fmt.Sprintf("%x", md5.Sum([]byte(content)))
startedAt := time.Now()
result, err := s.submit(remote, submitRequest{
Account: account,
PhoneNumber: phone,
Content: content,
SrcID: req.SrcId,
SrcID: req.srcID,
DestID: phone,
SequenceID: req.SeqId,
RemoteIP: remoteIP(packet.Conn.Conn.RemoteAddr()),
SequenceID: req.sequenceID,
RemoteIP: remoteIP(remote),
})
if err != nil || !result.Accepted {
logger.Printf("cmpp inbound submit rejected account=%s phone=%s seq=%d err=%v", account, phone, req.SeqId, err)
resp.Result = 9
reason := "api returned accepted=false"
if err != nil {
reason = err.Error()
}
logger.Printf(
"cmpp inbound event=submit_rejected protocol=%s packet_type=%s account=%s remote=%s seq=%d phone=%s result=9 stage=business duration_ms=%d content_chars=%d content_hash=%s reason=%q",
clientProtocol, req.protocol, account, remote, req.sequenceID, phone, time.Since(startedAt).Milliseconds(), len([]rune(content)), contentHash, reason,
)
setInboundSubmitResponse(response.Packer, 0, 9)
return false, nil
}
resp.MsgId = messageIDFrom(result.MessageID, req.SeqId)
resp.Result = 0
gatewayMsgID := messageIDFrom(result.MessageID, req.sequenceID)
setInboundSubmitResponse(response.Packer, gatewayMsgID, 0)
rememberDownstream(downstreamSession{
messageID: result.MessageID,
account: account,
srcID: strings.TrimSpace(req.SrcId),
protocol: clientProtocol,
srcID: strings.TrimSpace(req.srcID),
phoneNumber: phone,
gatewayMsgID: resp.MsgId,
remoteIP: remoteIP(packet.Conn.Conn.RemoteAddr()),
gatewayMsgID: gatewayMsgID,
remoteIP: remoteIP(remote),
connectedAt: time.Now().UTC(),
conn: packet.Conn,
mu: &sync.Mutex{},
presence: s.PresenceStore,
instanceID: s.gatewayInstanceID(),
})
logger.Printf(
"cmpp inbound event=submit_accepted protocol=%s packet_type=%s account=%s remote=%s seq=%d phone=%s result=0 message_id=%s gateway_message_id=%d duration_ms=%d content_chars=%d content_hash=%s",
clientProtocol, req.protocol, account, remote, req.sequenceID, phone, result.MessageID, gatewayMsgID, time.Since(startedAt).Milliseconds(), len([]rune(content)), contentHash,
)
return false, nil
}
type inboundSubmitPacket struct {
protocol string
pkTotal uint8
pkNumber uint8
msgFmt uint8
msgSrc string
srcID string
destTerminalIDs []string
msgContent string
sequenceID uint32
}
func normalizeInboundSubmit(packet any) (inboundSubmitPacket, bool) {
switch req := packet.(type) {
case *cmpp.Cmpp2SubmitReqPkt:
return inboundSubmitPacket{
protocol: "cmpp20", pkTotal: req.PkTotal, pkNumber: req.PkNumber, msgFmt: req.MsgFmt,
msgSrc: req.MsgSrc, srcID: req.SrcId, destTerminalIDs: req.DestTerminalId,
msgContent: req.MsgContent, sequenceID: req.SeqId,
}, true
case *cmpp.Cmpp3SubmitReqPkt:
return inboundSubmitPacket{
protocol: "cmpp30", pkTotal: req.PkTotal, pkNumber: req.PkNumber, msgFmt: req.MsgFmt,
msgSrc: req.MsgSrc, srcID: req.SrcId, destTerminalIDs: req.DestTerminalId,
msgContent: req.MsgContent, sequenceID: req.SeqId,
}, true
default:
return inboundSubmitPacket{}, false
}
}
func setInboundSubmitResponse(packet any, messageID uint64, result uint32) {
switch resp := packet.(type) {
case *cmpp.Cmpp2SubmitRspPkt:
resp.MsgId = messageID
resp.Result = uint8(result)
case *cmpp.Cmpp3SubmitRspPkt:
resp.MsgId = messageID
resp.Result = result
}
}
func inboundClientProtocol(account string, conn *cmpp.Conn, fallback string) string {
downstreamRegistry.RLock()
defer downstreamRegistry.RUnlock()
session := downstreamRegistry.byAccount[account]
if session != nil && session.conn == conn && session.protocol != "" {
return session.protocol
}
return fallback
}
func cmppVersionName(version cmpp.Type) string {
switch version {
case cmpp.V20:
return "cmpp20"
case cmpp.V21:
return "cmpp21"
case cmpp.V30:
return "cmpp30"
default:
return fmt.Sprintf("unknown_0x%02x", uint8(version))
}
}
func (s Server) authenticate(remote net.Addr, account string, authSource string, timestamp uint32) (authResponse, error) {
payload := authRequest{
Account: account,
@@ -320,11 +414,22 @@ func (s Server) post(ctx context.Context, path string, payload any, result any)
return err
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
if err != nil {
return fmt.Errorf("read api response: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("api returned %s", resp.Status)
detail := strings.TrimSpace(string(responseBody))
if detail == "" {
return fmt.Errorf("api returned %s", resp.Status)
}
return fmt.Errorf("api returned %s: %s", resp.Status, detail)
}
if result != nil {
return json.NewDecoder(resp.Body).Decode(result)
if len(responseBody) == 0 {
return io.EOF
}
return json.Unmarshal(responseBody, result)
}
return nil
}