message: send nacks for protobuf parsing failures

This commit is contained in:
Tulir Asokan
2025-10-05 11:31:10 +03:00
parent 359e39387a
commit da22a66ffe
4 changed files with 56 additions and 20 deletions
+2 -1
View File
@@ -22,10 +22,11 @@ import (
"go.mau.fi/whatsmeow/types/events"
)
func (cli *Client) handleDecryptedArmadillo(ctx context.Context, info *types.MessageInfo, decrypted []byte, retryCount int) (handlerFailed bool) {
func (cli *Client) handleDecryptedArmadillo(ctx context.Context, info *types.MessageInfo, decrypted []byte, retryCount int) (handlerFailed, protobufFailed bool) {
dec, err := decodeArmadillo(decrypted)
if err != nil {
cli.Log.Warnf("Failed to decode armadillo message from %s: %v", info.SourceString(), err)
protobufFailed = true
return
}
dec.Info = *info
Generated
+7 -3
View File
@@ -67,7 +67,7 @@ func (int *DangerousInternalClient) RequestAppStateKeys(ctx context.Context, raw
int.c.requestAppStateKeys(ctx, rawKeyIDs)
}
func (int *DangerousInternalClient) HandleDecryptedArmadillo(ctx context.Context, info *types.MessageInfo, decrypted []byte, retryCount int) (handlerFailed bool) {
func (int *DangerousInternalClient) HandleDecryptedArmadillo(ctx context.Context, info *types.MessageInfo, decrypted []byte, retryCount int) (handlerFailed, protobufFailed bool) {
return int.c.handleDecryptedArmadillo(ctx, info, decrypted, retryCount)
}
@@ -511,12 +511,16 @@ func (int *DangerousInternalClient) ParseReceipt(node *waBinary.Node) (*events.R
return int.c.parseReceipt(node)
}
func (int *DangerousInternalClient) BackgroundIfAsyncAck(fn func()) {
int.c.backgroundIfAsyncAck(fn)
}
func (int *DangerousInternalClient) MaybeDeferredAck(ctx context.Context, node *waBinary.Node) func(...*bool) {
return int.c.maybeDeferredAck(ctx, node)
}
func (int *DangerousInternalClient) SendAck(node *waBinary.Node) {
int.c.sendAck(node)
func (int *DangerousInternalClient) SendAck(node *waBinary.Node, error int) {
int.c.sendAck(node, error)
}
func (int *DangerousInternalClient) SendMessageReceipt(info *types.MessageInfo, node *waBinary.Node) {
+17 -13
View File
@@ -291,13 +291,10 @@ func (cli *Client) decryptMessages(ctx context.Context, info *types.MessageInfo,
if ok && len(node.GetChildrenByTag("enc")) == 0 {
uType := events.UnavailableType(unavailableNode.AttrGetter().String("type"))
cli.Log.Warnf("Unavailable message %s from %s (type: %q)", info.ID, info.SourceString(), uType)
if cli.SynchronousAck {
cli.backgroundIfAsyncAck(func() {
cli.immediateRequestMessageFromPhone(ctx, info)
cli.sendAck(node)
} else {
go cli.delayedRequestMessageFromPhone(info)
go cli.sendAck(node)
}
cli.sendAck(node, 0)
})
cli.dispatchEvent(&events.UndecryptableMessage{Info: *info, IsUnavailable: true, UnavailableType: uType})
return
}
@@ -320,10 +317,12 @@ func (cli *Client) decryptMessages(ctx context.Context, info *types.MessageInfo,
cli.Log.Warnf("No LID found for %s", info.Sender)
}
}
var recognizedStanza, protobufFailed bool
for _, child := range children {
if child.Tag != "enc" {
continue
}
recognizedStanza = true
ag := child.AttrGetter()
encType, ok := ag.GetString("type", false)
if !ok {
@@ -399,15 +398,16 @@ func (cli *Client) decryptMessages(ctx context.Context, info *types.MessageInfo,
var handlerFailed bool
switch ag.Int("v") {
case 2:
// TODO send nack instead of receipt for proto unmarshal errors (both this one and armadillo)
err = proto.Unmarshal(decrypted, &msg)
if err != nil {
cli.Log.Warnf("Error unmarshaling decrypted message from %s: %v", info.SourceString(), err)
protobufFailed = true
continue
}
protobufFailed = false
handlerFailed = cli.handleDecryptedMessage(ctx, info, &msg, retryCount)
case 3:
handlerFailed = cli.handleDecryptedArmadillo(ctx, info, decrypted, retryCount)
handlerFailed, protobufFailed = cli.handleDecryptedArmadillo(ctx, info, decrypted, retryCount)
default:
cli.Log.Warnf("Unknown version %d in decrypted message from %s", ag.Int("v"), info.SourceString())
}
@@ -441,11 +441,15 @@ func (cli *Client) decryptMessages(ctx context.Context, info *types.MessageInfo,
}
}
}
if cli.SynchronousAck {
cli.sendMessageReceipt(info, node)
} else {
go cli.sendMessageReceipt(info, node)
}
cli.backgroundIfAsyncAck(func() {
if !recognizedStanza {
cli.sendAck(node, NackUnrecognizedStanza)
} else if protobufFailed {
cli.sendAck(node, NackInvalidProtobuf)
} else {
cli.sendMessageReceipt(info, node)
}
})
return
}
+30 -3
View File
@@ -101,6 +101,14 @@ func (cli *Client) parseReceipt(node *waBinary.Node) (*events.Receipt, error) {
return &receipt, nil
}
func (cli *Client) backgroundIfAsyncAck(fn func()) {
if cli.SynchronousAck {
fn()
} else {
go fn()
}
}
func (cli *Client) maybeDeferredAck(ctx context.Context, node *waBinary.Node) func(cancelled ...*bool) {
if cli.SynchronousAck {
return func(cancelled ...*bool) {
@@ -113,15 +121,31 @@ func (cli *Client) maybeDeferredAck(ctx context.Context, node *waBinary.Node) fu
Msg("Not sending ack for node")
return
}
cli.sendAck(node)
cli.sendAck(node, 0)
}
} else {
go cli.sendAck(node)
go cli.sendAck(node, 0)
return func(...*bool) {}
}
}
func (cli *Client) sendAck(node *waBinary.Node) {
const (
NackParsingError = 487
NackUnrecognizedStanza = 488
NackUnrecognizedStanzaClass = 489
NackUnrecognizedStanzaType = 490
NackInvalidProtobuf = 491
NackInvalidHostedCompanionStanza = 493
NackMissingMessageSecret = 495
NackSignalErrorOldCounter = 496
NackMessageDeletedOnPeer = 499
NackUnhandledError = 500
NackUnsupportedAdminRevoke = 550
NackUnsupportedLIDGroup = 551
NackDBOperationFailed = 552
)
func (cli *Client) sendAck(node *waBinary.Node, error int) {
attrs := waBinary.Attrs{
"class": node.Tag,
"id": node.Attrs["id"],
@@ -145,6 +169,9 @@ func (cli *Client) sendAck(node *waBinary.Node) {
if receiptType, ok := node.Attrs["type"]; node.Tag != "message" && ok {
attrs["type"] = receiptType
}
if error != 0 {
attrs["error"] = error
}
err := cli.sendNode(waBinary.Node{
Tag: "ack",
Attrs: attrs,