D Rats Internals Protocols - wb8tyw/D-Rats GitHub Wiki
D-Rats Internals - Protocols
This is still a work in progress.
DDT2 Frame
The DDT2 frame is what is sent over the wire between D-rats clients and ratflectors.
The DDT2 Frame starts with the ASCII text "[SOB]" not including the quotes.
The DDT2 frame ends with the ASCII text "[EOB]" not including the quotes.
The data in the payload is Yencoded (Described below) to prevent data from being interpreted as control information.
DDT2 Header
The header is the first 23 bytes of of the frame. The payload is the rest of the frame.
- Byte 1 is a "magic" number. It is 0xDD if the payload is zlib compressed before being yencoded.
- Bytes 2 and 3 is a 16 bit sequence number.
- Byte 4 is a session number.
- Byte 5 is a type. Still don't know the types.
- Bytes 6 and 7, 16 bits, is the checksum of the data after any compression.
- Bytes 8 and 9, 16 bits, is the length of the data.
- bytes 10-18, 8 bits, are the source call sign.
- bytes 19-25, 8 bits, are the destination call sign.
If a call sign is less than 8 characters, it is padded to fill the space with the "~" tilde character.
Frame types: (Some from sessions/chat.py)
- 0 - T_DEF
- 1 - T_PING_REQ - Ping request (Used in Test frame)
- 2 - T_PING_RSP - Ping response
- 3 - T_PING_ERS - Ping error status?
- 4 - T_STATUS - Status frame
- 5 - File Transfer
- 8 - Used in Test frame
- 254 - Apparently a warm up frame.
For a station status frame, the first byte of the message is an ASCII station status value.
- '0' - Unknown
- '1' - Online
- '2' - Unattended
- '9' - Offline
Yencode
The data stream for the payload is sent "Yencoded" over the wire.
What that means is that the if certain characters show up on the original data stream they are replaced with two characters.
The first character is an '=' or equals sign.
The second character is the original with an offset of 64 added to it and then truncated back to fit in 8 bits.
There is a set of characters that are always encoded, that set in python is b"\x11\x13\x1A\00\xFD\xFE\xFF".
Other characters can also be encoded.
Decoding is simply a matter of detecting the equal signs, discarding them and subtracting the offset, and since the computers that run d-rats use two's complement negative numbers, if the result is negative add 256 to it. The result should fit in 8 bits.
Protocol path
Send file
Attempted to get a wireshark capture, but could only get the 1/2 of the conversion, and the 1/2 that I did not need.
45 104.217011546 192.168.0.139 192.168.0.117 TCP 116 9000 → 49528 [PSH, ACK] Seq=34 Ack=64 Win=29200 Len=62
0000 2c 41 38 5d 80 63 00 1c 23 a0 0a 9d 08 00 45 00 ,A8].c..# ....E.
0010 00 66 15 eb 40 00 40 06 a2 56 c0 a8 00 8b c0 a8 .f.ë@.@.¢VÀ¨..À¨
0020 00 75 23 28 c1 78 04 ef d5 91 41 b2 50 2e 50 18 .u#(Áx.ïÕ.A²P.P.
0030 72 10 82 a9 00 00 5b 53 4f 42 5d dd 3d 40 3d 40 r..©..[SOB]Ý=@=@
0040 3d 40 08 32 3e 3d 40 15 57 42 38 54 59 57 2d 32 [email protected]>[email protected]
0050 57 42 38 54 59 57 2d 34 78 da 63 49 2a cd cc 49 WB8TYW-4xÚcI*ÍÌI
0060 89 2f c8 2f d1 2b ce 3d 40 3d 40 1f f2 04 d0 5b ./È/Ñ+Î=@=@.ò.Ð[
0070 45 4f 42 5d EOB]
49 105.741676792 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=96 Ack=116 Win=29200 Len=0
Transmission Control Protocol, Src Port: 9000, Dst Port: 49528, Seq: 96, Ack: 116, Len: 0
50 107.501795524 192.168.0.139 192.168.0.117 TCP 118 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=96 Ack=116 Win=29200 Len=64
0000 5b 53 4f 42 5d dd 3d 40 3d 40 04 04 f0 d2 3d 40 [SOB]Ý=@=@..ðÒ=@
0010 18 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 2d .WB8TYW-2WB8TYW-
0020 34 78 da 63 60 64 60 48 2a cd cc 49 89 2f c8 2f 4xÚc`d`H*ÍÌI./È/
0030 d1 2b ce 3d 40 3d 40 1f d0 04 cd 5b 45 4f 42 5d Ñ+Î=@=@.Ð.Í[EOB]
50 107.501795524 192.168.0.139 192.168.0.117 TCP 118 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=96 Ack=116 Win=29200 Len=64
0000 5b 53 4f 42 5d dd 3d 40 3d 40 04 04 f0 d2 3d 40 [SOB]Ý=@=@..ðÒ=@
0010 18 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 2d .WB8TYW-2WB8TYW-
0020 34 78 da 63 60 64 60 48 2a cd cc 49 89 2f c8 2f 4xÚc`d`H*ÍÌI./È/
0030 d1 2b ce 3d 40 3d 40 1f d0 04 cd 5b 45 4f 42 5d Ñ+Î=@=@.Ð.Í[EOB]
51 107.703298159 192.168.0.139 192.168.0.117 TCP 105 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=160 Ack=116 Win=29200 Len=51
0000 5b 53 4f 42 5d dd 3d 40 3d 40 04 05 42 0f 3d 40 [SOB]Ý=@[email protected].=@
0010 09 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 2d .WB8TYW-2WB8TYW-
0020 34 78 da 63 3d 40 3d 40 3d 40 01 3d 40 01 5b 45 4xÚc=@=@=@.=@.[E
0030 4f 42 5d OB]
53 109.968250269 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=211 Ack=167 Win=29200 Len=0
54 109.969492063 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=211 Ack=218 Win=29200 Len=0
55 109.970721146 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=211 Ack=269 Win=29200 Len=0
57 111.004207374 192.168.0.139 192.168.0.117 TCP 105 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=211 Ack=269 Win=29200 Len=51
0000 5b 53 4f 42 5d dd 3d 40 3d 40 04 01 66 73 3d 40 [SOB]Ý=@[email protected]=@
0010 09 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 2d .WB8TYW-2WB8TYW-
0020 34 78 da 63 3d 40 3d 40 3d 40 01 3d 40 01 5b 45 4xÚc=@=@=@.=@.[E
0030 4f 42 5d OB]
58 111.208398933 192.168.0.139 192.168.0.117 TCP 417 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=262 Ack=269 Win=29200 Len=363
0000 5b 53 4f 42 5d dd 3d 40 01 04 04 c4 eb 01 0b 57 [SOB]Ý=@...Äë..W
0010 42 38 54 59 57 2d 32 57 42 38 54 59 57 2d 34 78 B8TYW-2WB8TYW-4x
0020 da 01 3d 40 01 3d 3f 3d 3e 78 da 9d 90 c1 4a c4 Ú.=@.=?=>xÚ..ÁJÄ
0030 30 10 86 ef 7d 8a 5f 7a 3d 53 d2 a2 50 58 8f 2b 0..ï}._z=SÒ¢PX.+
0040 cb 8a 20 0a bb de 65 9a 4c db 68 37 53 92 14 db Ë. .»Þe.LÛh7S..Û
0050 b7 37 5d 65 f5 b8 eb 2d 43 3d 3e 6f e6 9b c9 af ·7]eõ¸ë-C=>oæ.ɯ
0060 ca da ba b2 a6 d0 65 59 e0 08 35 f2 94 65 39 b6 ÊÚº²¦ÐeYà.5ò.e9¶
0070 d6 87 08 ed 99 22 23 72 87 14 e1 62 90 88 c6 f6 Ö..í."#r..áb..Æö
0080 9c 4d 2d c7 c8 53 ca 1b 6c d4 6e 3d 3d ba 87 12 .M-ÇÈSÊ.lÔn==º..
0090 f4 a2 a9 e7 f2 14 55 1b 14 b8 2e 86 79 e9 f8 bc ô¢©çò.U..¸..yéø¼
00a0 c4 8d f5 ac a3 f8 19 0a f7 ac 69 0c 0c 69 40 0e Ä.õ¬£ø..÷¬i..i@.
00b0 62 0c 42 f4 d6 b5 b0 0e 34 dd 56 70 cc 06 51 10 b.BôÖµ°.4ÝVpÌ.Q.
00c0 06 d6 b6 99 41 50 aa f1 72 50 5a cc d9 0a ef 7f .Ö¶.APªñrPZÌÙ.ï.
00d0 21 3c ee 5f d4 6a 55 dd a9 9b 0a e6 cd 53 0c e5 !<î_ÔjUÝ©..æÍS.å
00e0 d1 2f c7 8e 87 b4 2b 3d 5a f1 60 d2 dd af 69 fa Ñ/Ç..´+=Zñ`Òݯiú
00f0 5b bb 19 9f e4 5d 92 0b 18 bc d4 54 f7 f3 49 af [»..ä]...¼ÔT÷óI¯
0100 e6 34 55 3e 52 41 b1 38 5f eb 67 fc 81 86 6f 85 æ4U>RA±8_ëgü..o.
0110 4b c1 c0 21 58 71 e1 7f f4 68 2f e4 d4 3d 53 1e KÁÀ!Xqá.ôh/äÔ=S.
0120 7a 4a 27 3c 92 ed f2 cc be 3d 40 ab 1c bb 8a 5e zJ'<.íò̾=@«.».^
0130 3d 3e 85 a3 5b 45 4f 42 5d 5b 53 4f 42 5d dd 3d =>.£[EOB][SOB]Ý=
0140 40 3d 40 04 05 2d 9d 3d 40 09 57 42 38 54 59 57 @[email protected][email protected]
0150 2d 32 57 42 38 54 59 57 2d 34 78 da 63 04 3d 40 -2WB8TYW-4xÚc.=@
0160 3d 40 02 3d 40 02 5b 45 4f 42 5d =@.=@.[EOB]
60 112.619335379 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=625 Ack=319 Win=29200 Len=0
61 115.132787637 192.168.0.139 192.168.0.117 TCP 105 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=625 Ack=319 Win=29200 Len=51
0000 5b 53 4f 42 5d dd 3d 40 3d 40 3d 40 01 be 10 3d [SOB]Ý=@=@=@.¾.=
0010 40 09 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 @.WB8TYW-2WB8TYW
0020 2d 34 78 da 33 01 3d 40 3d 40 35 3d 40 35 5b 45 -4xÚ3.=@=@5=@5[E
0030 4f 42 5d OB]
62 116.494969967 192.168.0.139 192.168.0.117 TCP 54 [TCP ACKed unseen segment] 9000 → 49528 [ACK] Seq=676 Ack=370 Win=29200 Len=0
63 117.254101304 192.168.0.139 192.168.0.117 TCP 105 [TCP ACKed unseen segment] 9000 → 49528 [PSH, ACK] Seq=676 Ack=370 Win=29200 Len=51
0000 5b 53 4f 42 5d dd 3d 40 3d 40 3d 40 01 be 10 3d [SOB]Ý=@=@=@.¾.=
0010 40 09 57 42 38 54 59 57 2d 32 57 42 38 54 59 57 @.WB8TYW-2WB8TYW
0020 2d 34 78 da 33 01 3d 40 3d 40 35 3d 40 35 5b 45 -4xÚ3.=@=@5=@5[E
0030 4f 42 5d OB]
from console logs:
Receiver get gets a block
DDT2+: 0:0:8 src->dest ('b\05build_pot.sh'...[13])
control: New Session 5 from remote
control Sending Ack for request number 5 (Apparently not received)
queue_next: new limit is 4 (8/4), queueing 4
SessionManger: received block 0:8 for session 'control'
FileTransferSession:worker deep_sleep
Awoke from deep sleep to some data
Some queue nessages
Looks like it then re-trys the transfer.
On the sending side:
12/26/2021 18:03:21:INFO:SessionCoordinator:send_file: Outgoing files: ['/home/malmberg/work/d-rats/D-Rats/build_pot.sh']
12/26/2021 18:03:21:INFO:SessionCoordinator:send_file: Started Session
12/26/2021 18:03:21:INFO:StatefulSession:queue_next: New limit is 4 (4/8), queueing 4
12/26/2021 18:03:21:INFO:StatefulSession:worker: Session loop (None:build_pot.sh)
12/26/2021 18:03:21:INFO:StatefulSession:worker: Deep sleep
12/26/2021 18:03:21:INFO:FileTransferSession:XFER STATUS:
12/26/2021 18:03:21:INFO:FileTransferSession:worker: Awoke from deep sleep to some data
12/26/2021 18:03:21:INFO:FileTransferSession:queue_next: New limit is 4 (4/8), queueing 4
12/26/2021 18:03:21:INFO:FileTransferSession:worker: Session loop (6:build_pot.sh)
12/26/2021 18:03:21:INFO:FileTransferSession:worker: Deep sleep
12/26/2021 18:03:21:INFO:Transporter:send_frame: Waiting 1.3 sec before transmitting
12/26/2021 18:03:22:INFO:SessionCoordinator:New session (out) of type: <class 'd_rats.sessions.file.FileTransferSession'>
---Difference from failed transfer:
12/26/2021 18:03:22:INFO:MainApp:Session Started In: [SESSION 6]: <class 'type'>
+++ Old program, good transfer
[SESSION 4]: FileTransfer
===== Above issue found and fixed.
12/26/2021 18:03:22:INFO:MainApp:Session Started In: [SESSION 6]: File transfer of build_pot.sh started with WB8TYW-4
12/26/2021 18:03:22:INFO:FileTransferSession:write: Waiting for session to open
12/26/2021 18:03:23:INFO:Transporter:send_frame: Sending block: DDT2+: 0:0:8 WB8TYW-1->WB8TYW-4 (b'\x06build_pot.sh'...[13])
Control : Sent request, blocking...
12/26/2021 18:03:25:INFO:Transporter:parse_blocks: Got a block: DDT2+: 0:0:2 WB8TYW-4->WB8TYW-1 (b'\x06\x05'...[2])
sniff._handler frame.session 0 data -b'\x06\x05'- <class 'bytes'>
Control : Signaled waiting session thread (l=6 r=5)
12/26/2021 18:03:25:INFO:SessionManager:incoming: Received block 0:2 for session `control'
Control : Established session 6:5
12/26/2021 18:03:25:INFO:FileTransferSession:queue_next: New limit is 4 (4/8), queueing 4
12/26/2021 18:03:25:INFO:FileTransferSession:queue_next : Queuing 0 for send (4)
12/26/2021 18:03:25:INFO:FileTransferSession:write: Waiting for block 0 ACK to be received
12/26/2021 18:03:25:INFO:FileTransferSession:worker: Awoke from deep sleep to some data
12/26/2021 18:03:25:INFO:FileTransferSession:queue_next: New limit is 4 (4/8), queueing 3
12/26/2021 18:03:25:INFO:FileTransferSession:send_blocks: Sending 0
12/26/2021 18:03:25:INFO:FileTransferSession:send_reqack: Requesting ACK of blocks [0]
12/26/2021 18:03:25:INFO:FileTransferSession:send_blocks: Waiting for block to be sent
12/26/2021 18:03:25:INFO:Transporter:send_frame: Waiting 1.2 sec before transmitting
--- From failed transfer
12/26/2021 18:03:27:INFO:Transporter:send_frame: Sending block: DDT2+: 0:5:4 WB8TYW-1->WB8TYW-4 (b'\x00\x01\x00\x00build_pot.sh'...[16])
12/26/2021 18:03:27:INFO:Transporter:send_frame: Sending block: DDT2+: 0:5:5 WB8TYW-1->WB8TYW-4 (...[1])
12/26/2021 18:03:27:INFO:FileTransferSession:write: Block 0 is sent, waiting for ack
12/26/2021 18:03:27:INFO:FileTransferSession:write: 0 No ACK received (probably canceled)
12/26/2021 18:03:27:INFO:FileTransferSession:send_file: Waiting for start
12/26/2021 18:03:27:INFO:FileTransferSession:update_xmt: Average transmit rate: 26978 bps
12/26/2021 18:03:27:INFO:FileTransferSession:send_blocks: Block sent after: 1.477457
+++ From good transfer older
12/27/2021 09:46:35 Transport : Sending block: DDT2+: 0:4:4 WB8TYW-2->WB8TYW-4 (build_pot.sh...[16])
Stateful : Average transmit rate: 14113 bps
Stateful : Block 0 is sent, waiting for ack
Stateful : 0 Not ACKED (probably canceled)
Stateful : Block sent after: 1.413584
Stateful : Session loop (4:build_pot.sh)
Waiting for start
+++
12/26/2021 18:03:27:INFO:MainApp:Session Started In: [SESSION 6]: [00%] (Total 16 B)
12/26/2021 18:03:27:INFO:FileTransferSession:worker: Session loop (6:build_pot.sh)
12/26/2021 18:03:27:INFO:FileTransferSession:worker: Outstanding data, short sleep
12/26/2021 18:03:27:INFO:MainApp:Session Started In: [SESSION 6]: Waiting for response [00%] (Total 16 B)
messages
High level
Outgoing
-
Message gets created in Drafts folder. ui/main_messages.py
-
Message gets moved from a folder to the OutBox folder or message is directly queued for sending. ui/MainMessages/MessagesTab/_snd_msg()
- Requires a filename.
- Gets an recipient (string object) for the filename.
- Tries to lock message, abort if message is locked.
- Emits a get-station-list signal which returns a dictionary of station objects keyed by the radio port.
- The emit method is from Gobject.
- The station objects are extracted from
- If the recipient is in stations, a remove is done. Then an insert is done to bring it to the front list. This is confusing if recipient is not a station object.
- unlocks the message
- Emits a user-send-form signal (station, port, file_name, "foo")
msgrouting/MessageRouter/_send_form
- Emits a user-send-form signal (call, port, filename, "Foo")
-
Something finds the messages in the Outbox folder and attempts to send them? Still unknown what triggers this.
-
Mainapp/__user_send_form handles the signal from the above. It looks up a SessionManager object for a port name. mainapp stores SessionManager objects in an dict of tuples indexed by the radio portname. The tuple members are a SessionManager object and a SessionCoordinator object. This is setup sometime earlier. This calls the send_form(station, filename, sessionname) option for the SessionCoordinator object for the radio port.
-
SessionCoordinator/send_form This creates a form.FormTransferSession and starts a thread with it. The filename for the form is added the head of the list A thread is started with sm.start_session(name: name, dest, cls: formTransferSession object) start_session creates a cls(name) object calls _register_session(session, dest, "new,out") to return a session id. Then calls control.new_session(session) and if that fails calls _deregister_session(session id)
-
FormTransferSessionClass inherits from FileTransferSessionClass, which inherits from StatefulSession. StatefulSession inherits from Session.
new_session creates a frame with a thread object. And starts a loop of up to 10 times. It calls outgoing() which sends the frame on the transport. waits up to 10 seconds for the internal flag to be true. Then clears the internal flag. session.wait_for_state_change(wait_time) 5 seconds for first time, 15 for subsequent times. Get the state of the session. closed: Give up. sync: set wait to 15 and retry else assume good, set state to be "OPEN" return True If not good after 10 tries, set state to close and returns false.
-
Once the message is sent it is put in the sent folder.
Incoming:
- Something detects an incoming message.
- Something starts putting the message in the Inbox folder
- Something tells the sender that the message is received.
Application module transport(pipe, params)
chunk = transport.get_input
parse_blocks:
Looks for ddt2 encoded header and decodes it.
(blocks without a ddt2 encoded header are not processed by this module)
blocks are put in a DDT2EnciodedFrame object.