Stun protocol encoding / decoding
This commit is contained in:
parent
09a664dbad
commit
2e26deb377
|
@ -0,0 +1,14 @@
|
||||||
|
import ../webrtc/stun
|
||||||
|
import ./asyncunit
|
||||||
|
import binary_serialization
|
||||||
|
|
||||||
|
suite "Stun suite":
|
||||||
|
test "Stun encoding/decoding with padding":
|
||||||
|
let msg = @[ 0x00'u8, 0x01, 0x00, 0xa4, 0x21, 0x12, 0xa4, 0x42, 0x75, 0x6a, 0x58, 0x46, 0x42, 0x58, 0x4e, 0x72, 0x6a, 0x50, 0x4d, 0x2b, 0x00, 0x06, 0x00, 0x63, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2b, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x2b, 0x76, 0x31, 0x2f, 0x62, 0x71, 0x36, 0x67, 0x69, 0x43, 0x75, 0x4a, 0x38, 0x6e, 0x78, 0x59, 0x46, 0x4a, 0x36, 0x43, 0x63, 0x67, 0x45, 0x59, 0x58, 0x58, 0x2f, 0x78, 0x51, 0x58, 0x56, 0x4c, 0x74, 0x39, 0x71, 0x7a, 0x3a, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2b, 0x77, 0x65, 0x62, 0x72, 0x74, 0x63, 0x2b, 0x76, 0x31, 0x2f, 0x62, 0x71, 0x36, 0x67, 0x69, 0x43, 0x75, 0x4a, 0x38, 0x6e, 0x78, 0x59, 0x46, 0x4a, 0x36, 0x43, 0x63, 0x67, 0x45, 0x59, 0x58, 0x58, 0x2f, 0x78, 0x51, 0x58, 0x56, 0x4c, 0x74, 0x39, 0x71, 0x7a, 0x00, 0xc0, 0x57, 0x00, 0x04, 0x00, 0x00, 0x03, 0xe7, 0x80, 0x2a, 0x00, 0x08, 0x86, 0x63, 0xfd, 0x45, 0xa9, 0xe5, 0x4c, 0xdb, 0x00, 0x24, 0x00, 0x04, 0x6e, 0x00, 0x1e, 0xff, 0x00, 0x08, 0x00, 0x14, 0x16, 0xff, 0x70, 0x8d, 0x97, 0x0b, 0xd6, 0xa3, 0x5b, 0xac, 0x8f, 0x4c, 0x85, 0xe6, 0xa6, 0xac, 0xaa, 0x7a, 0x68, 0x27, 0x80, 0x28, 0x00, 0x04, 0x79, 0x5e, 0x03, 0xd8 ]
|
||||||
|
check msg == encode(StunMessage.decode(msg))
|
||||||
|
|
||||||
|
test "Error while encoding":
|
||||||
|
let msgLengthFailed = @[ 0x00'u8, 0x01, 0x00, 0xa4, 0x21, 0x12, 0xa4, 0x42, 0x75, 0x6a, 0x58, 0x46, 0x42, 0x58, 0x4e, 0x72, 0x6a, 0x50, 0x4d ]
|
||||||
|
expect AssertionDefect: discard StunMessage.decode(msgLengthFailed)
|
||||||
|
let msgAttrFailed = @[ 0x00'u8, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42, 0x75, 0x6a, 0x58, 0x46, 0x42, 0x58, 0x4e, 0x72, 0x6a, 0x50, 0x4d, 0x2b, 0x28, 0x00, 0x05, 0x79, 0x5e, 0x03, 0xd8 ]
|
||||||
|
expect AssertionDefect: discard StunMessage.decode(msgAttrFailed)
|
|
@ -7,4 +7,5 @@ license = "MIT"
|
||||||
|
|
||||||
requires "nim >= 1.2.0",
|
requires "nim >= 1.2.0",
|
||||||
"chronicles >= 0.10.2",
|
"chronicles >= 0.10.2",
|
||||||
"chronos >= 3.0.6"
|
"chronos >= 3.0.6",
|
||||||
|
"https://github.com/status-im/nim-binary-serialization.git"
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import bitops
|
||||||
|
import chronos, chronicles
|
||||||
|
import binary_serialization
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "webrtc stun"
|
||||||
|
|
||||||
|
const
|
||||||
|
msgHeaderSize = 20
|
||||||
|
magicCookieSeq = @[ 0x21'u8, 0x12, 0xa4, 0x42 ]
|
||||||
|
magicCookie = 0x2112a442
|
||||||
|
|
||||||
|
type
|
||||||
|
# Stun Attribute
|
||||||
|
# 0 1 2 3
|
||||||
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
# | Type | Length |
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
# | Value (variable) ....
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
StunAttribute* = object
|
||||||
|
attributeType*: uint16
|
||||||
|
length* {.bin_value: it.value.len.}: uint16
|
||||||
|
value* {.bin_len: it.length.}: seq[byte]
|
||||||
|
|
||||||
|
proc decode(T: typedesc[StunAttribute], cnt: seq[byte]): seq[StunAttribute] =
|
||||||
|
const val = @[0, 3, 2, 1]
|
||||||
|
var padding = 0
|
||||||
|
while padding < cnt.len():
|
||||||
|
let attr = Binary.decode(cnt[padding ..^ 1], StunAttribute)
|
||||||
|
result.add(attr)
|
||||||
|
padding += 4 + attr.value.len()
|
||||||
|
padding += val[padding mod 4]
|
||||||
|
|
||||||
|
proc seqAttrLen(s: seq[StunAttribute]): uint16 =
|
||||||
|
for it in s:
|
||||||
|
result = it.length + 4
|
||||||
|
|
||||||
|
type
|
||||||
|
# Stun Header
|
||||||
|
# 0 1 2 3
|
||||||
|
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
# |0 0| STUN Message Type | Message Length |
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
# | Magic Cookie |
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
# | |
|
||||||
|
# | Transaction ID (96 bits) |
|
||||||
|
# | |
|
||||||
|
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
StunMessageInner = object
|
||||||
|
msgType: uint16
|
||||||
|
length* {.bin_value: it.content.len().}: uint16
|
||||||
|
magicCookie: uint32
|
||||||
|
transactionId: array[12, byte]
|
||||||
|
content* {.bin_len: it.length.}: seq[byte]
|
||||||
|
|
||||||
|
StunMessage* = object
|
||||||
|
msgType*: uint16
|
||||||
|
transactionId*: array[12, byte]
|
||||||
|
attributes*: seq[StunAttribute]
|
||||||
|
|
||||||
|
Stun* = object
|
||||||
|
|
||||||
|
proc isMessage*(T: typedesc[Stun], msg: seq[byte]): bool =
|
||||||
|
msg.len >= msgHeaderSize and msg[4..<8] == magicCookie and bitand(0xC0'u8, msg[0]) == 0'u8
|
||||||
|
|
||||||
|
proc decode*(T: typedesc[StunMessage], msg: seq[byte]): StunMessage =
|
||||||
|
let smi = Binary.decode(msg, StunMessageInner)
|
||||||
|
return T(msgType: smi.msgType,
|
||||||
|
transactionId: smi.transactionId,
|
||||||
|
attributes: StunAttribute.decode(smi.content))
|
||||||
|
|
||||||
|
proc encode*(msg: StunMessage): seq[byte] =
|
||||||
|
const val = @[0, 3, 2, 1]
|
||||||
|
var smi = StunMessageInner(msgType: msg.msgType,
|
||||||
|
magicCookie: magicCookie,
|
||||||
|
transactionId: msg.transactionId)
|
||||||
|
for attr in msg.attributes:
|
||||||
|
smi.content.add(Binary.encode(attr))
|
||||||
|
smi.content.add(newSeq[byte](val[smi.content.len() mod 4]))
|
||||||
|
|
||||||
|
return Binary.encode(smi)
|
||||||
|
|
||||||
|
proc new*(T: typedesc[Stun]): T =
|
||||||
|
result = T()
|
Loading…
Reference in New Issue