nim-webrtc/webrtc/stunattributes.nim

174 lines
6.1 KiB
Nim

# Nim-WebRTC
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import sequtils, typetraits
import binary_serialization,
stew/byteutils,
chronos
import utils
type
StunAttributeEncodingError* = object of CatchableError
# 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) ....
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
RawStunAttribute* = object
attributeType*: uint16
length* {.bin_value: it.value.len.}: uint16
value* {.bin_len: it.length.}: seq[byte]
StunAttributeEnum* = enum
AttrMappedAddress = 0x0001
AttrChangeRequest = 0x0003 # RFC5780 Nat Behavior Discovery
AttrSourceAddress = 0x0004 # Deprecated
AttrChangedAddress = 0x0005 # Deprecated
AttrUsername = 0x0006
AttrMessageIntegrity = 0x0008
AttrErrorCode = 0x0009
AttrUnknownAttributes = 0x000A
AttrChannelNumber = 0x000C # RFC5766 TURN
AttrLifetime = 0x000D # RFC5766 TURN
AttrXORPeerAddress = 0x0012 # RFC5766 TURN
AttrData = 0x0013 # RFC5766 TURN
AttrRealm = 0x0014
AttrNonce = 0x0015
AttrXORRelayedAddress = 0x0016 # RFC5766 TURN
AttrRequestedAddressFamily = 0x0017 # RFC6156
AttrEvenPort = 0x0018 # RFC5766 TURN
AttrRequestedTransport = 0x0019 # RFC5766 TURN
AttrDontFragment = 0x001A # RFC5766 TURN
AttrMessageIntegritySHA256 = 0x001C # RFC8489 STUN (v2)
AttrPasswordAlgorithm = 0x001D # RFC8489 STUN (v2)
AttrUserhash = 0x001E # RFC8489 STUN (v2)
AttrXORMappedAddress = 0x0020
AttrReservationToken = 0x0022 # RFC5766 TURN
AttrPriority = 0x0024 # RFC5245 ICE
AttrUseCandidate = 0x0025 # RFC5245 ICE
AttrPadding = 0x0026 # RFC5780 Nat Behavior Discovery
AttrResponsePort = 0x0027 # RFC5780 Nat Behavior Discovery
AttrConnectionID = 0x002a # RFC6062 TURN Extensions
AttrPasswordAlgorithms = 0x8002 # RFC8489 STUN (v2)
AttrAlternateDomain = 0x8003 # RFC8489 STUN (v2)
AttrSoftware = 0x8022
AttrAlternateServer = 0x8023
AttrCacheTimeout = 0x8027 # RFC5780 Nat Behavior Discovery
AttrFingerprint = 0x8028
AttrICEControlled = 0x8029 # RFC5245 ICE
AttrICEControlling = 0x802A # RFC5245 ICE
AttrResponseOrigin = 0x802b # RFC5780 Nat Behavior Discovery
AttrOtherAddress = 0x802C # RFC5780 Nat Behavior Discovery
AttrOrigin = 0x802F
proc isRequired*(typ: uint16): bool = typ <= 0x7FFF'u16
proc isOptional*(typ: uint16): bool = typ >= 0x8000'u16
# Error Code
type
ErrorCodeEnum* = enum
ECTryAlternate = 300
ECBadRequest = 400
ECUnauthenticated = 401
ECUnknownAttribute = 420
ECStaleNonce = 438
ECServerError = 500
ErrorCode* = object
reserved1: uint16 # should be 0
reserved2 {.bin_bitsize: 5.}: uint8 # should be 0
class {.bin_bitsize: 3.}: uint8
number: uint8
reason: seq[byte]
proc encode*(T: typedesc[ErrorCode], code: ErrorCodeEnum, reason: string = ""): RawStunAttribute =
let
ec = T(class: (code.uint16 div 100'u16).uint8,
number: (code.uint16 mod 100'u16).uint8,
reason: reason.toBytes())
value = Binary.encode(ec)
result = RawStunAttribute(attributeType: AttrErrorCode.uint16,
length: value.len().uint16,
value: value)
# Unknown Attribute
type
UnknownAttribute* = object
unknownAttr: seq[uint16]
proc encode*(T: typedesc[UnknownAttribute], unknownAttr: seq[uint16]): RawStunAttribute =
let
ua = T(unknownAttr: unknownAttr)
value = Binary.encode(ua)
result = RawStunAttribute(attributeType: AttrUnknownAttributes.uint16,
length: value.len().uint16,
value: value)
# Fingerprint
type
Fingerprint* = object
crc32: uint32
proc encode*(T: typedesc[Fingerprint], msg: seq[byte]): RawStunAttribute =
let value = Binary.encode(T(crc32: crc32(msg) xor 0x5354554e'u32))
result = RawStunAttribute(attributeType: AttrFingerprint.uint16,
length: value.len().uint16,
value: value)
# Xor Mapped Address
type
MappedAddressFamily {.size: 1.} = enum
MAFIPv4 = 0x01
MAFIPv6 = 0x02
XorMappedAddress* = object
reserved: uint8 # should be 0
family: MappedAddressFamily
port: uint16
address: seq[byte]
proc encode*(T: typedesc[XorMappedAddress], ta: TransportAddress,
tid: array[12, byte]): RawStunAttribute =
const magicCookie = @[ 0x21'u8, 0x12, 0xa4, 0x42 ]
let
address =
if ta.family == AddressFamily.IPv4:
var s = newSeq[uint8](4)
for i in 0..3:
s[i] = ta.address_v4[i] xor magicCookie[i]
s
else:
let magicCookieTid = magicCookie.concat(@tid)
var s = newSeq[uint8](16)
for i in 0..15:
s[i] = ta.address_v6[i] xor magicCookieTid[i]
s
xma = T(family: if ta.family == AddressFamily.IPv4: MAFIPv4 else: MAFIPv6,
port: ta.port.distinctBase xor 0x2112'u16, address: address)
value = Binary.encode(xma)
result = RawStunAttribute(attributeType: AttrXORMappedAddress.uint16,
length: value.len().uint16,
value: value)
# Message Integrity
type
MessageIntegrity* = object
msgInt: seq[byte]
proc encode*(T: typedesc[MessageIntegrity], msg: seq[byte], key: seq[byte]): RawStunAttribute =
let value = Binary.encode(T(msgInt: hmacSha1(key, msg)))
result = RawStunAttribute(attributeType: AttrMessageIntegrity.uint16,
length: value.len().uint16,
value: value)