diff --git a/src/sdp/connection.zig b/src/sdp/connection.zig index 5f97e6d..9671d57 100644 --- a/src/sdp/connection.zig +++ b/src/sdp/connection.zig @@ -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: "
" -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; @@ -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); @@ -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" { @@ -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()); } diff --git a/src/sdp/session.zig b/src/sdp/session.zig index 3cc6166..592817d 100644 --- a/src/sdp/session.zig +++ b/src/sdp/session.zig @@ -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 { @@ -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, }; } @@ -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"); @@ -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();