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
86 changes: 45 additions & 41 deletions src/sdp/connection.zig
Original file line number Diff line number Diff line change
@@ -1,32 +1,47 @@
const std = @import("std");

const Self = @This();
const IpAddress = std.Io.net.IpAddress;
const Connection = @This();

pub const NetType = enum { in };
pub const AddrType = enum { ip4, ip6 };

net_type: NetType,
addr_type: AddrType,
address: []const u8,
address: IpAddress,

/// Parses a connection string in the format: "<net_type> <addr_type> <address>"
pub fn parse(buffer: []const u8) !Self {
var parts = std.mem.splitAny(u8, buffer, " ");
pub fn parse(buffer: []const u8) !Connection {
var parts = std.mem.tokenizeScalar(u8, buffer, ' ');

const net_type_str = parts.next() orelse return error.InvalidConnection;
const addr_type_str = parts.next() orelse return error.InvalidConnection;
const address_str = parts.next() orelse return error.InvalidConnection;

const net_type = try parseNetType(net_type_str);
const addr_type = try parseAddrType(addr_type_str);

return Self{
const address = if (std.ascii.eqlIgnoreCase(addr_type_str, "ip4"))
try IpAddress.parseIp4(address_str, 0)
else if (std.ascii.eqlIgnoreCase(addr_type_str, "ip6"))
try IpAddress.parseIp6(address_str, 0)
else
return error.InvalidConnection;

return Connection{
.net_type = net_type,
.addr_type = addr_type,
.address = address_str,
.address = address,
};
}

pub fn write(c: *const Connection, w: *std.Io.Writer) !void {
try w.writeAll("c=IN ");
switch (c.address) {
.ip4 => |addr| try w.print("IP4 {}.{}.{}.{}\r\n", .{ addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3] }),
.ip6 => |addr| {
const u: std.Io.net.Ip6Address.Unresolved = .{ .bytes = addr.bytes, .interface_name = null };
try w.print("IP6 {f}\r\n", .{u});
},
}
}

pub fn parseNetType(input: []const u8) !NetType {
if (std.mem.eql(u8, "IN", input)) {
return .in;
Expand All @@ -35,16 +50,6 @@ pub fn parseNetType(input: []const u8) !NetType {
}
}

pub fn parseAddrType(input: []const u8) !AddrType {
if (std.mem.eql(u8, "IP4", input)) {
return .ip4;
} else if (std.mem.eql(u8, "IP6", input)) {
return .ip6;
} else {
return error.InvalidAddrType;
}
}

test "parseNetType: valid IN" {
const result = try parseNetType("IN");
try std.testing.expectEqual(NetType.in, result);
Expand All @@ -56,34 +61,18 @@ test "parseNetType: invalid returns error" {
try std.testing.expectError(error.InvalidNetType, parseNetType("in"));
}

test "parseAddrType: valid IP4" {
const result = try parseAddrType("IP4");
try std.testing.expectEqual(AddrType.ip4, result);
}

test "parseAddrType: valid IP6" {
const result = try parseAddrType("IP6");
try std.testing.expectEqual(AddrType.ip6, result);
}

test "parseAddrType: invalid returns error" {
try std.testing.expectError(error.InvalidAddrType, parseAddrType("IP5"));
try std.testing.expectError(error.InvalidAddrType, parseAddrType(""));
try std.testing.expectError(error.InvalidAddrType, parseAddrType("ip4"));
}

test "parse: IPv4 connection" {
const result = try parse("IN IP4 192.168.1.1");
try std.testing.expectEqual(NetType.in, result.net_type);
try std.testing.expectEqual(AddrType.ip4, result.addr_type);
try std.testing.expectEqualStrings("192.168.1.1", result.address);
try std.testing.expect(result.address.eql(&.{ .ip4 = .{ .bytes = [_]u8{ 192, 168, 1, 1 }, .port = 0 } }));
}

test "parse: IPv6 connection" {
const expected_addr: IpAddress = .{ .ip6 = .loopback(0) };

const result = try parse("IN IP6 ::1");
try std.testing.expectEqual(NetType.in, result.net_type);
try std.testing.expectEqual(AddrType.ip6, result.addr_type);
try std.testing.expectEqualStrings("::1", result.address);
try std.testing.expect(result.address.eql(&expected_addr));
}

test "parse: missing fields returns error" {
Expand All @@ -97,5 +86,20 @@ test "parse: invalid net_type returns error" {
}

test "parse: invalid addr_type returns error" {
try std.testing.expectError(error.InvalidAddrType, parse("IN IP5 192.168.1.1"));
try std.testing.expectError(error.InvalidConnection, parse("IN IP5 192.168.1.1"));
}

test "write connection" {
var buf: [1024]u8 = @splat(0);
var w: std.Io.Writer = .fixed(&buf);

const ip4: Connection = .{ .net_type = .in, .address = .{ .ip4 = .loopback(0) } };
const ip6: Connection = .{ .net_type = .in, .address = try .parseIp6("2a01:4f8:2220:3128::2", 0) };

try ip4.write(&w);
try std.testing.expectEqualStrings("c=IN IP4 127.0.0.1\r\n", w.buffered());
_ = w.consumeAll();

try ip6.write(&w);
try std.testing.expectEqualStrings("c=IN IP6 2a01:4f8:2220:3128::2\r\n", w.buffered());
}
23 changes: 14 additions & 9 deletions src/sdp/session.zig
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ pub const Origin = struct {
session_id: u64,
session_version: u64,
nettype: Connection.NetType,
addrtype: Connection.AddrType,
unicast_address: []const u8,
unicast_address: std.Io.net.IpAddress,
};

pub const MediaIterator = struct {
Expand Down Expand Up @@ -353,16 +352,20 @@ fn parseOrigin(line: []const u8) !Origin {
const nettype = try Connection.parseNetType(nettype_str);

const addrtype_str = parts.next() orelse return Error.InvalidOrigin;
const addr_type = try Connection.parseAddrType(addrtype_str);
const unicast_address_str = parts.next() orelse return Error.InvalidOrigin;

const unicast_address = parts.next() orelse return Error.InvalidOrigin;
const unicast_address = if (std.ascii.eqlIgnoreCase(addrtype_str, "ip4"))
try std.Io.net.IpAddress.parseIp4(unicast_address_str, 0)
else if (std.ascii.eqlIgnoreCase(addrtype_str, "ip6"))
try std.Io.net.IpAddress.parseIp6(unicast_address_str, 0)
else
return error.InvalidOrigin;

return Origin{
.username = username,
.session_id = session_id,
.session_version = session_version,
.nettype = nettype,
.addrtype = addr_type,
.unicast_address = unicast_address,
};
}
Expand Down Expand Up @@ -402,8 +405,9 @@ test "parse minimal SDP" {
try std.testing.expectEqual(3724394400, origin.session_id);
try std.testing.expectEqual(3724394405, origin.session_version);
try std.testing.expect(origin.nettype == Connection.NetType.in);
try std.testing.expect(origin.addrtype == Connection.AddrType.ip4);
try std.testing.expectEqualStrings(origin.unicast_address, "198.51.100.1");

var expected_addr: std.Io.net.IpAddress = .{ .ip4 = .{ .bytes = [_]u8{ 198, 51, 100, 1 }, .port = 0 } };
try std.testing.expect(origin.unicast_address.eql(&expected_addr));

try std.testing.expectEqualStrings(sdp.session_name, "Call to John Smith");

Expand All @@ -416,8 +420,9 @@ test "parse minimal SDP" {
try std.testing.expect(sdp.connection != null);
const conn = sdp.connection.?;
try std.testing.expect(conn.net_type == Connection.NetType.in);
try std.testing.expect(conn.addr_type == Connection.AddrType.ip4);
try std.testing.expectEqualStrings("198.51.100.1", conn.address);

expected_addr = .{ .ip4 = .{ .bytes = [_]u8{ 198, 51, 100, 1 }, .port = 0 } };
try std.testing.expect(conn.address.eql(&expected_addr));

// Session Attributes
var attributes_iter = sdp.attributeIterator();
Expand Down
Loading