Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ The projects is structured into modules, each module is a separate library that
* `rtp` - [RTP (Real-time Transport Protocol)](https://datatracker.ietf.org/doc/html/rfc3550) implementation for media streaming based on RFC 3550.
* `sdp` - [SDP (Session Description Protocol)](https://datatracker.ietf.org/doc/html/rfc4566) implementation for describing multimedia sessions based on RFC 4566.
* `rtsp` - [RTSP (Real Time Streaming Protocol)](https://datatracker.ietf.org/doc/html/rfc2326) implementation for controlling streaming media servers based on RFC 2326.
* `stun` - [STUN (Session Traversal Utilities for NAT)](https://datatracker.ietf.org/doc/html/rfc8489) implementation for NAT traversal based on RFC 5389.
* `stun` - [STUN (Session Traversal Utilities for NAT)](https://datatracker.ietf.org/doc/html/rfc8489) implementation for NAT traversal based on RFC 8489.
27 changes: 24 additions & 3 deletions src/stun/stun.zig
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,25 @@ pub const Header = packed struct {
_pad: u2 = 0,
};

/// Describes a Stun message.
pub const Message = struct {
header: Header,
bytes: []const u8,

pub const Error = error{
/// Magic cookie in the header doesn't match the Stun defined one.
WrongMagicCookie,
/// The length reported in the header is not equal to the body of the stun message.
InvalidLength,
};

pub fn iterateAttributes(message: *const Message, passwd: []const u8) AttributeIterator {
var reader = std.Io.Reader.fixed(message.bytes);
reader.toss(header_size);
return .{ .reader = reader, .password = passwd };
}

pub fn parse(msg: []const u8) !Message {
pub fn parse(msg: []const u8) Error!Message {
std.debug.assert(msg.len >= header_size);

const header_int = std.mem.readInt(@typeInfo(Header).@"struct".backing_integer.?, msg[0..header_size], .big);
Expand All @@ -74,6 +82,10 @@ pub const Message = struct {
return error.WrongMagicCookie;
}

if (header.message_length != msg.len - header_size) {
return error.InvalidLength;
}

return .{
.header = header,
.bytes = msg,
Expand All @@ -86,11 +98,13 @@ pub const AttributeType = enum(u16) {
username = 0x0006,
message_integrity = 0x0008,
xor_mapped_address = 0x0020,
use_candidate = 0x0025,
userhash = 0x001E,
priority = 0x0024,
software = 0x8022,
fingerprint = 0x8028,
ice_controlled = 0x8029,
ice_controlling = 0x802A,
unknown = 0xFFFF,
_,
};
Expand All @@ -100,11 +114,13 @@ pub const Attribute = union(AttributeType) {
username: []const u8,
message_integrity: []const u8,
xor_mapped_address: Io.net.IpAddress,
use_candidate: void,
userhash: []const u8,
priority: u32,
software: []const u8,
fingerprint: u32,
ice_controlled: u64,
ice_controlling: u64,
unknown: struct { AttributeType, []const u8 },
};

Expand Down Expand Up @@ -143,9 +159,14 @@ pub const AttributeIterator = struct {
if (attr_len != 4) return error.InvalidAttribute;
break :blk .{ .priority = std.mem.readInt(u32, attr_value[0..4], .big) };
},
.ice_controlled => blk: {
.ice_controlled, .ice_controlling => blk: {
if (attr_len != 8) return error.InvalidAttribute;
break :blk .{ .ice_controlled = std.mem.readInt(u64, attr_value[0..8], .big) };
const tie_breaker = std.mem.readInt(u64, attr_value[0..8], .big);
break :blk if (attr_type == .ice_controlled) .{ .ice_controlled = tie_breaker } else .{ .ice_controlling = tie_breaker };
},
.use_candidate => blk: {
if (attr_value.len != 0) return error.InvalidAttribute;
break :blk .use_candidate;
},
.message_integrity => blk: {
if (attr_len != 20) break :blk error.InvalidAttribute;
Expand Down
Loading