Unhandled error when sender sends message in wrong order? (may be I'm not sure)
copyninja opened this issue · 1 comments
Hi,
I was experimenting with magic-wormhole protocol using golang
and was experimenting send functionality. Then I encountered error on my side and noticed on mailbox server side there is this stack trace dumped on screen.
2018-08-24T09:40:52+0530 [_GenericHTTPChannelProtocol,2,127.0.0.1] ws client connecting: tcp4:127.0.0.1:57960
2018-08-24T09:40:53+0530 [_GenericHTTPChannelProtocol,2,127.0.0.1] creating nameplate#6 for app_id lothar.com/wormhole/text-or-file-xfer
2018-08-24T09:40:53+0530 [_GenericHTTPChannelProtocol,2,127.0.0.1] spawning #5mcm5y333z6ze for app_id lothar.com/wormhole/text-or-file-xfer
2018-08-24T09:41:11+0530 [_GenericHTTPChannelProtocol,2,127.0.0.1] Unhandled Error
Traceback (most recent call last):
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\python\log.py", line 103, in callWithLogger
return callWithContext({"system": lp}, func, *args, **kw)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\python\log.py", line 86, in callWithContext
return context.call({ILogContext: newCtx}, func, *args, **kw)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\python\context.py", line 122, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\python\context.py", line 85, in callWithContext
return func(*args,**kw)
--- <exception caught here> ---
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\internet\selectreactor.py", line 149, in _doReadOrWrite
why = getattr(selectable, method)()
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\internet\tcp.py", line 205, in doRead
return self._dataReceived(data)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\twisted\internet\tcp.py", line 211, in _dataReceived
rval = self.protocol.dataReceived(data)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\twisted\websocket.py", line 131, in dataReceived
self._dataReceived(data)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\websocket\protocol.py", line 1175, in _dataReceived
self.consumeData()
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\websocket\protocol.py", line 1187, in consumeData
while self.processData() and self.state != WebSocketProtocol.STATE_CLOSED:
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\websocket\protocol.py", line 1553, in processData
fr = self.onFrameEnd()
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\websocket\protocol.py", line 1674, in onFrameEnd
self._onMessageEnd()
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\twisted\websocket.py", line 159, in _onMessageEnd
self.onMessageEnd()
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\websocket\protocol.py", line 627, in onMessageEnd
self._onMessage(payload, self.message_is_binary)
File "c:\users\invakam2\appdata\local\programs\python\python36\lib\site-packages\autobahn\twisted\websocket.py", line 162, in _onMessage
self.onMessage(payload, isBinary)
File "c:\users\invakam2\documents\playarea\python\magic-wormhole-mailbox-server\src\wormhole_mailbox_server\server_websocket.py", line 125, in onMessage
msg = bytes_to_dict(payload)
File "c:\users\invakam2\documents\playarea\python\magic-wormhole-mailbox-server\src\wormhole_mailbox_server\util.py", line 25, in bytes_to_dict
assert isinstance(d, dict)
builtins.AssertionError:
Here is the go code which causes this.
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/gorilla/websocket"
"log"
"net/url"
"salsa.debian.org/vasudev/magicwormhole/internal/core"
peer "salsa.debian.org/vasudev/magicwormhole/internal/peermessages"
server "salsa.debian.org/vasudev/magicwormhole/internal/servermessages"
"time"
)
func startSend(incoming chan server.IncommingMessage, c *websocket.Conn) {
k := core.NewKey("lothar,com/wormhole/text-or-file-xfer", "side0", make(map[string]string))
var sharedKey []byte
var nameplate, mailbox, code, phase, text string
nameplateRelease := false
for {
select {
case msg := <-incoming:
switch msg.(type) {
case *server.Welcome:
w := msg.(*server.Welcome)
if motd, ok := w.Welcome["motd"]; ok {
fmt.Printf("Message from server: %s\n", motd)
}
if err := c.WriteJSON(server.Bind{"lothar.com/wormhole/text-or-file-xfer", "side0"}); err != nil {
log.Fatal("Failed to bind with server, ", err)
}
if err := c.WriteJSON(server.Allocate{}); err != nil {
log.Fatal("Failed to request allocate nameplate: ", err)
}
case *server.Allocated:
a := msg.(*server.Allocated)
nameplate = *a.Nameplate
if err := c.WriteJSON(server.Claim{nameplate}); err != nil {
log.Fatal("Failed to claim the nameplate: ", err)
}
fmt.Printf("Please enter a code (without nameplate): ")
var x string
fmt.Scanf("%s", &x)
code = fmt.Sprintf("%s-%s", nameplate, x)
fmt.Printf("Please run `wormhole rx %s` on other end\n", code)
case *server.Claimed:
claimed := msg.(*server.Claimed)
mailbox = *claimed.Mailbox
if err := c.WriteJSON(server.Open{mailbox}); err != nil {
log.Fatal("Failed to open mailbox: ", err)
}
fmt.Print("Please enter message: ")
fmt.Scanf("%s", &text)
pake, err := k.BuildPake(code)
if err != nil {
log.Fatal("Failed to build pake: ", err)
}
time.Sleep(time.Duration(10 * time.Second))
if err = c.WriteJSON(server.Add{"pake", hex.EncodeToString(pake)}); err != nil {
log.Fatal("Failed to send pake message: ", err)
}
myVersion, err := k.PrepareVersionMessage(sharedKey)
if err != nil {
log.Fatal("Failed to create version image: ", err)
}
if err = c.WriteJSON(myVersion); err != nil {
log.Fatal("Failed to send my version: ", err)
}
case *server.Message:
m := msg.(*server.Message)
if *m.Side != "side0" {
if !nameplateRelease {
if err := c.WriteJSON(server.Release{nameplate}); err != nil {
log.Fatal("Failed to release nameplate: ", err)
}
}
switch *m.Phase {
case "pake":
var err error
sharedKey, err = k.ComputeKey(*m.Body)
if err != nil {
log.Fatal("Failed to compute key: ", err)
}
fmt.Printf("shared key: %x\n", sharedKey)
default:
fmt.Printf("phase: %s side: %s\n", *m.Phase, *m.Side)
phaseKey := core.DerivePhaseKey(sharedKey, *m.Side, *m.Phase)
body, err := hex.DecodeString(*m.Body)
if err != nil {
log.Fatal("failed to decode body bytes: ", err)
}
data, err := core.DecryptData(phaseKey, body)
if err != nil {
log.Fatal("failed to decrypt payload: ", err)
}
switch *m.Phase {
case "version":
var version peer.Version
if err := json.Unmarshal(data, &version); err != nil {
log.Fatal("payload unmarshaling for version failed: ", err)
}
if len(version.AppVersion) != 0 {
fmt.Printf("%s\n", version.AppVersion)
}
fmt.Printf("key verified. Sending data... \n")
phase = "0"
phaseKey := core.DerivePhaseKey(sharedKey, *m.Side, phase)
encrypted, err := core.EncryptData(phaseKey, []byte(text))
if err != nil {
log.Fatal("Failed to encrypt data: ", err)
}
if err = c.WriteJSON(server.Add{phase, hex.EncodeToString(encrypted)}); err != nil {
log.Fatal("Failed to send message to other side: ", err)
}
default:
var answer peer.Answer
if err := json.Unmarshal(data, &answer); err != nil {
log.Fatalf("Failed to unmarshal the answer: %s, unrecognized message: %s", err, string(data))
}
switch answer.Answer.(type) {
case peer.MessageAck:
m := answer.Answer.(peer.MessageAck)
if m.Ack == "ok" {
fmt.Println("text message sent succesfully")
close := server.Close{mailbox, "happy"}
if err := c.WriteJSON(close); err != nil {
log.Fatal("Failed to close mailbox: ", err)
}
return
}
}
}
}
}
case *server.Released:
nameplateRelease = true
}
}
}
}
func main() {
u := url.URL{Scheme: "ws", Host: "localhost:4000", Path: "v1"}
log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
if err != nil {
log.Fatal("dial: ", err)
}
defer c.Close()
input := make(chan server.IncommingMessage)
inputDone := make(chan bool)
inputTimer := time.NewTicker(time.Second)
defer inputTimer.Stop()
go func() {
defer close(input)
var in server.IncommingMessage
for {
select {
case <-inputDone:
return
case <-inputTimer.C:
_, message, err := c.ReadMessage()
if err != nil {
log.Println("Read: ", err)
}
in, err = server.Decode(json.RawMessage(message))
if err != nil {
log.Fatal("Decode: ", err, string(message))
}
input <- in
}
}
}()
startSend(input, c)
}
You would need to use salsa.debian.org/vasudev/magicwormhole
for building this binary. Once built just execute and enter the code without nameplate (nameplate got from server). And you will see abrupt connection closure error and above trace back on mailbox server.
I was expecting an error message instead suggesting what might have gone wrong. Ofcourse its my code at fault but the above error is not handled in server side.
Can you please give a JSON trace of the communication happening between both sides until the crash?