From 5d05cfcfe6144a7f49f57cb376863edd0059d9ca Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Nov 2019 19:35:04 -0300 Subject: [PATCH 01/12] rename IpAddress to Address, add Address.unix --- lib/std/net.zig | 85 ++++++++++++++++++++++---------------------- lib/std/net/test.zig | 30 ++++++++-------- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 95036707b6..d805aeb238 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -10,15 +10,16 @@ test "" { _ = @import("net/test.zig"); } -pub const IpAddress = extern union { +pub const Address = extern union { any: os.sockaddr, in: os.sockaddr_in, in6: os.sockaddr_in6, + unix: os.sockaddr_un, // TODO this crashed the compiler //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); - pub fn parse(name: []const u8, port: u16) !IpAddress { + pub fn parseIp(name: []const u8, port: u16) !Address { if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { error.Overflow, error.InvalidEnd, @@ -39,7 +40,7 @@ pub const IpAddress = extern union { return error.InvalidIPAddressFormat; } - pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !IpAddress { + pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { switch (family) { os.AF_INET => return parseIp4(name, port), os.AF_INET6 => return parseIp6(name, port), @@ -48,8 +49,8 @@ pub const IpAddress = extern union { } } - pub fn parseIp6(buf: []const u8, port: u16) !IpAddress { - var result = IpAddress{ + pub fn parseIp6(buf: []const u8, port: u16) !Address { + var result = Address{ .in6 = os.sockaddr_in6{ .scope_id = 0, .port = mem.nativeToBig(u16, port), @@ -154,8 +155,8 @@ pub const IpAddress = extern union { } } - pub fn parseIp4(buf: []const u8, port: u16) !IpAddress { - var result = IpAddress{ + pub fn parseIp4(buf: []const u8, port: u16) !Address { + var result = Address{ .in = os.sockaddr_in{ .port = mem.nativeToBig(u16, port), .addr = undefined, @@ -194,8 +195,8 @@ pub const IpAddress = extern union { return error.Incomplete; } - pub fn initIp4(addr: [4]u8, port: u16) IpAddress { - return IpAddress{ + pub fn initIp4(addr: [4]u8, port: u16) Address { + return Address{ .in = os.sockaddr_in{ .port = mem.nativeToBig(u16, port), .addr = @ptrCast(*align(1) const u32, &addr).*, @@ -203,8 +204,8 @@ pub const IpAddress = extern union { }; } - pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) IpAddress { - return IpAddress{ + pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address { + return Address{ .in6 = os.sockaddr_in6{ .addr = addr, .port = mem.nativeToBig(u16, port), @@ -215,7 +216,7 @@ pub const IpAddress = extern union { } /// Returns the port in native endian. - pub fn getPort(self: IpAddress) u16 { + pub fn getPort(self: Address) u16 { const big_endian_port = switch (self.any.family) { os.AF_INET => self.in.port, os.AF_INET6 => self.in6.port, @@ -225,7 +226,7 @@ pub const IpAddress = extern union { } /// `port` is native-endian. - pub fn setPort(self: *IpAddress, port: u16) void { + pub fn setPort(self: *Address, port: u16) void { const ptr = switch (self.any.family) { os.AF_INET => &self.in.port, os.AF_INET6 => &self.in6.port, @@ -237,16 +238,16 @@ pub const IpAddress = extern union { /// Asserts that `addr` is an IP address. /// This function will read past the end of the pointer, with a size depending /// on the address family. - pub fn initPosix(addr: *align(4) const os.sockaddr) IpAddress { + pub fn initPosix(addr: *align(4) const os.sockaddr) Address { switch (addr.family) { - os.AF_INET => return IpAddress{ .in = @ptrCast(*const os.sockaddr_in, addr).* }, - os.AF_INET6 => return IpAddress{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* }, + os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* }, + os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* }, else => unreachable, } } pub fn format( - self: IpAddress, + self: Address, comptime fmt: []const u8, options: std.fmt.FormatOptions, context: var, @@ -271,7 +272,7 @@ pub const IpAddress = extern union { }, os.AF_INET6 => { const port = mem.bigToNative(u16, self.in6.port); - if (mem.eql(u8, self.in6.addr[0..12], [_]u8{0,0,0,0,0,0,0,0,0,0,0xff,0xff})) { + if (mem.eql(u8, self.in6.addr[0..12], [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) { try std.fmt.format( context, Errors, @@ -318,13 +319,13 @@ pub const IpAddress = extern union { } } - pub fn eql(a: IpAddress, b: IpAddress) bool { + pub fn eql(a: Address, b: Address) bool { const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()]; const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()]; return mem.eql(u8, a_bytes, b_bytes); } - fn getOsSockLen(self: IpAddress) os.socklen_t { + fn getOsSockLen(self: Address) os.socklen_t { switch (self.any.family) { os.AF_INET => return @sizeOf(os.sockaddr_in), os.AF_INET6 => return @sizeOf(os.sockaddr_in6), @@ -358,7 +359,7 @@ pub fn connectUnixSocket(path: []const u8) !fs.File { pub const AddressList = struct { arena: std.heap.ArenaAllocator, - addrs: []IpAddress, + addrs: []Address, canon_name: ?[]u8, fn deinit(self: *AddressList) void { @@ -381,7 +382,7 @@ pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) return tcpConnectToAddress(addrs[0], port); } -pub fn tcpConnectToAddress(address: IpAddress) !fs.File { +pub fn tcpConnectToAddress(address: Address) !fs.File { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); @@ -456,13 +457,13 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* } break :blk count; }; - result.addrs = try arena.alloc(IpAddress, addr_count); + result.addrs = try arena.alloc(Address, addr_count); var it: ?*os.addrinfo = res; var i: usize = 0; while (it) |info| : (it = info.next) { const addr = info.addr orelse continue; - result.addrs[i] = IpAddress.initPosix(@alignCast(4, addr)); + result.addrs[i] = Address.initPosix(@alignCast(4, addr)); if (info.canonname) |n| { if (result.canon_name == null) { @@ -485,7 +486,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port); - result.addrs = try arena.alloc(IpAddress, lookup_addrs.len); + result.addrs = try arena.alloc(Address, lookup_addrs.len); if (!canon.isNull()) { result.canon_name = canon.toOwnedSlice(); } @@ -501,7 +502,7 @@ pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !* } const LookupAddr = struct { - addr: IpAddress, + addr: Address, sortkey: i32 = 0, }; @@ -524,7 +525,7 @@ fn linuxLookupName( if (opt_name) |name| { // reject empty name and check len so it fits into temp bufs try canon.replaceContents(name); - if (IpAddress.parseExpectingFamily(name, family, port)) |addr| { + if (Address.parseExpectingFamily(name, family, port)) |addr| { try addrs.append(LookupAddr{ .addr = addr }); } else |name_err| if ((flags & std.c.AI_NUMERICHOST) != 0) { return name_err; @@ -751,23 +752,23 @@ fn linuxLookupNameFromNull( if ((flags & std.c.AI_PASSIVE) != 0) { if (family != os.AF_INET6) { (try addrs.addOne()).* = LookupAddr{ - .addr = IpAddress.initIp4([1]u8{0} ** 4, port), + .addr = Address.initIp4([1]u8{0} ** 4, port), }; } if (family != os.AF_INET) { (try addrs.addOne()).* = LookupAddr{ - .addr = IpAddress.initIp6([1]u8{0} ** 16, port, 0, 0), + .addr = Address.initIp6([1]u8{0} ** 16, port, 0, 0), }; } } else { if (family != os.AF_INET6) { (try addrs.addOne()).* = LookupAddr{ - .addr = IpAddress.initIp4([4]u8{ 127, 0, 0, 1 }, port), + .addr = Address.initIp4([4]u8{ 127, 0, 0, 1 }, port), }; } if (family != os.AF_INET) { (try addrs.addOne()).* = LookupAddr{ - .addr = IpAddress.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), + .addr = Address.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), }; } } @@ -812,7 +813,7 @@ fn linuxLookupNameFromHosts( } } else continue; - const addr = IpAddress.parseExpectingFamily(ip_text, family, port) catch |err| switch (err) { + const addr = Address.parseExpectingFamily(ip_text, family, port) catch |err| switch (err) { error.Overflow, error.InvalidEnd, error.InvalidCharacter, @@ -1033,7 +1034,7 @@ fn linuxLookupNameFromNumericUnspec( name: []const u8, port: u16, ) !void { - const addr = try IpAddress.parse(name, port); + const addr = try Address.parse(name, port); (try addrs.addOne()).* = LookupAddr{ .addr = addr }; } @@ -1049,7 +1050,7 @@ fn resMSendRc( var sl: os.socklen_t = @sizeOf(os.sockaddr_in); var family: os.sa_family_t = os.AF_INET; - var ns_list = std.ArrayList(IpAddress).init(rc.ns.allocator); + var ns_list = std.ArrayList(Address).init(rc.ns.allocator); defer ns_list.deinit(); try ns_list.resize(rc.ns.len); @@ -1065,8 +1066,8 @@ fn resMSendRc( } // Get local address and open/bind a socket - var sa: IpAddress = undefined; - @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(IpAddress)); + var sa: Address = undefined; + @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(Address)); sa.any.family = family; const flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK; const fd = os.socket(family, flags, 0) catch |err| switch (err) { @@ -1224,7 +1225,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) const new_addr = try ctx.addrs.addOne(); new_addr.* = LookupAddr{ // TODO slice [0..4] to make this *[4]u8 without @ptrCast - .addr = IpAddress.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), + .addr = Address.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), }; }, os.RR_AAAA => { @@ -1232,7 +1233,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) const new_addr = try ctx.addrs.addOne(); new_addr.* = LookupAddr{ // TODO slice [0..16] to make this *[16]u8 without @ptrCast - .addr = IpAddress.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), + .addr = Address.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), }; }, os.RR_CNAME => { @@ -1253,7 +1254,7 @@ pub const TcpServer = struct { kernel_backlog: u32, /// `undefined` until `listen` returns successfully. - listen_address: IpAddress, + listen_address: Address, sockfd: ?os.fd_t, @@ -1280,7 +1281,7 @@ pub const TcpServer = struct { self.* = undefined; } - pub fn listen(self: *TcpServer, address: IpAddress) !void { + pub fn listen(self: *TcpServer, address: Address) !void { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; const sockfd = try os.socket(os.AF_INET, sock_flags, os.IPPROTO_TCP); @@ -1330,8 +1331,8 @@ pub const TcpServer = struct { pub fn accept(self: *TcpServer) AcceptError!fs.File { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const accept_flags = nonblock | os.SOCK_CLOEXEC; - var accepted_addr: IpAddress = undefined; - var adr_len: os.socklen_t = @sizeOf(IpAddress); + var accepted_addr: Address = undefined; + var adr_len: os.socklen_t = @sizeOf(Address); if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { return fs.File.openHandle(fd); } else |err| switch (err) { diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 90986049da..8ea3218e01 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -28,17 +28,17 @@ test "parse and render IPv6 addresses" { "::ffff:123.5.123.5", }; for (ips) |ip, i| { - var addr = net.IpAddress.parseIp6(ip, 0) catch unreachable; + var addr = net.Address.parseIp6(ip, 0) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable; std.testing.expect(std.mem.eql(u8, printed[i], newIp[1 .. newIp.len - 3])); } - testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6(":::", 0)); - testing.expectError(error.Overflow, net.IpAddress.parseIp6("FF001::FB", 0)); - testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp6("FF01::Fb:zig", 0)); - testing.expectError(error.InvalidEnd, net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); - testing.expectError(error.Incomplete, net.IpAddress.parseIp6("FF01:", 0)); - testing.expectError(error.InvalidIpv4Mapping, net.IpAddress.parseIp6("::123.123.123.123", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp6(":::", 0)); + testing.expectError(error.Overflow, net.Address.parseIp6("FF001::FB", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp6("FF01::Fb:zig", 0)); + testing.expectError(error.InvalidEnd, net.Address.parseIp6("FF01:0:0:0:0:0:0:FB:", 0)); + testing.expectError(error.Incomplete, net.Address.parseIp6("FF01:", 0)); + testing.expectError(error.InvalidIpv4Mapping, net.Address.parseIp6("::123.123.123.123", 0)); } test "parse and render IPv4 addresses" { @@ -50,16 +50,16 @@ test "parse and render IPv4 addresses" { "123.255.0.91", "127.0.0.1", }) |ip| { - var addr = net.IpAddress.parseIp4(ip, 0) catch unreachable; + var addr = net.Address.parseIp4(ip, 0) catch unreachable; var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable; std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); } - testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0)); - testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0)); - testing.expectError(error.InvalidEnd, net.IpAddress.parseIp4("127.0.0.1.1", 0)); - testing.expectError(error.Incomplete, net.IpAddress.parseIp4("127.0.0.", 0)); - testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("100..0.1", 0)); + testing.expectError(error.Overflow, net.Address.parseIp4("256.0.0.1", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp4("x.0.0.1", 0)); + testing.expectError(error.InvalidEnd, net.Address.parseIp4("127.0.0.1.1", 0)); + testing.expectError(error.Incomplete, net.Address.parseIp4("127.0.0.", 0)); + testing.expectError(error.InvalidCharacter, net.Address.parseIp4("100..0.1", 0)); } test "resolve DNS" { @@ -91,7 +91,7 @@ test "listen on a port, send bytes, receive bytes" { } // TODO doing this at comptime crashed the compiler - const localhost = net.IpAddress.parse("127.0.0.1", 0); + const localhost = net.Address.parse("127.0.0.1", 0); var server = net.TcpServer.init(net.TcpServer.Options{}); defer server.deinit(); @@ -104,7 +104,7 @@ test "listen on a port, send bytes, receive bytes" { try await client_frame; } -fn testClient(addr: net.IpAddress) anyerror!void { +fn testClient(addr: net.Address) anyerror!void { const socket_file = try net.tcpConnectToAddress(addr); defer socket_file.close(); From 9458620e18cb89b71cd0ad2755b9a7ae4f63e846 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Nov 2019 19:59:30 -0300 Subject: [PATCH 02/12] replace Address.parse Address.parseIp --- lib/std/net.zig | 4 ++-- lib/std/net/test.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index d805aeb238..215c0ed6fa 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -44,7 +44,7 @@ pub const Address = extern union { switch (family) { os.AF_INET => return parseIp4(name, port), os.AF_INET6 => return parseIp6(name, port), - os.AF_UNSPEC => return parse(name, port), + os.AF_UNSPEC => return parseIp(name, port), else => unreachable, } } @@ -1034,7 +1034,7 @@ fn linuxLookupNameFromNumericUnspec( name: []const u8, port: u16, ) !void { - const addr = try Address.parse(name, port); + const addr = try Address.parseIp(name, port); (try addrs.addOne()).* = LookupAddr{ .addr = addr }; } diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 8ea3218e01..1bdd209837 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -91,7 +91,7 @@ test "listen on a port, send bytes, receive bytes" { } // TODO doing this at comptime crashed the compiler - const localhost = net.Address.parse("127.0.0.1", 0); + const localhost = net.Address.parseIp("127.0.0.1", 0); var server = net.TcpServer.init(net.TcpServer.Options{}); defer server.deinit(); From c2325053a86f4350e20ea3b476c7efeb942d8438 Mon Sep 17 00:00:00 2001 From: Luna Date: Fri, 8 Nov 2019 21:44:17 -0300 Subject: [PATCH 03/12] add Address.parseUnix and Address.format support for AF_UNIX --- lib/std/net.zig | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 215c0ed6fa..af3ac5475d 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -14,7 +14,7 @@ pub const Address = extern union { any: os.sockaddr, in: os.sockaddr_in, in6: os.sockaddr_in6, - unix: os.sockaddr_un, + un: os.sockaddr_un, // TODO this crashed the compiler //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); @@ -40,6 +40,18 @@ pub const Address = extern union { return error.InvalidIPAddressFormat; } + pub fn parseUnix(path: []const u8) !Address { + var sock_addr = os.sockaddr_un{ + .family = os.AF_UNIX, + .path = undefined, + }; + + if (path.len > sock_addr.path.len) return error.NameTooLong; + mem.copy(u8, &sock_addr.path, path); + + return Address{ .un = sock_addr }; + } + pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { switch (family) { os.AF_INET => return parseIp4(name, port), @@ -315,6 +327,9 @@ pub const Address = extern union { } try std.fmt.format(context, Errors, output, "]:{}", port); }, + os.AF_UNIX => { + try std.fmt.format(context, Errors, output, "{}", self.un.path); + }, else => unreachable, } } From f4d8dc278b312fc3eccf33a37cfe89c7c012d6fd Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 9 Nov 2019 12:40:56 -0300 Subject: [PATCH 04/12] rename TcpServer -> StreamServer - add AF_UNIX support to getOsSockLen --- lib/std/net.zig | 23 +++++++++++++++-------- lib/std/net/test.zig | 4 ++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index af3ac5475d..0f7c76438f 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -46,6 +46,9 @@ pub const Address = extern union { .path = undefined, }; + // this enables us to have the proper length of the socket in getOsSockLen + mem.zero(&sock_addr.path); + if (path.len > sock_addr.path.len) return error.NameTooLong; mem.copy(u8, &sock_addr.path, path); @@ -344,6 +347,10 @@ pub const Address = extern union { switch (self.any.family) { os.AF_INET => return @sizeOf(os.sockaddr_in), os.AF_INET6 => return @sizeOf(os.sockaddr_in6), + os.AF_UNIX => blk: { + const path_len = std.mem.len(self.un.path.len); + break :blk @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); + }, else => unreachable, } } @@ -1264,7 +1271,7 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) } } -pub const TcpServer = struct { +pub const StreamServer = struct { /// Copied from `Options` on `init`. kernel_backlog: u32, @@ -1282,21 +1289,21 @@ pub const TcpServer = struct { /// After this call succeeds, resources have been acquired and must /// be released with `deinit`. - pub fn init(options: Options) TcpServer { - return TcpServer{ + pub fn init(options: Options) StreamServer { + return StreamServer{ .sockfd = null, .kernel_backlog = options.kernel_backlog, .listen_address = undefined, }; } - /// Release all resources. The `TcpServer` memory becomes `undefined`. - pub fn deinit(self: *TcpServer) void { + /// Release all resources. The `StreamServer` memory becomes `undefined`. + pub fn deinit(self: *StreamServer) void { self.close(); self.* = undefined; } - pub fn listen(self: *TcpServer, address: Address) !void { + pub fn listen(self: *StreamServer, address: Address) !void { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; const sockfd = try os.socket(os.AF_INET, sock_flags, os.IPPROTO_TCP); @@ -1315,7 +1322,7 @@ pub const TcpServer = struct { /// Stop listening. It is still necessary to call `deinit` after stopping listening. /// Calling `deinit` will automatically call `close`. It is safe to call `close` when /// not listening. - pub fn close(self: *TcpServer) void { + pub fn close(self: *StreamServer) void { if (self.sockfd) |fd| { os.close(fd); self.sockfd = null; @@ -1343,7 +1350,7 @@ pub const TcpServer = struct { } || os.UnexpectedError; /// If this function succeeds, the returned `fs.File` is a caller-managed resource. - pub fn accept(self: *TcpServer) AcceptError!fs.File { + pub fn accept(self: *StreamServer) AcceptError!fs.File { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const accept_flags = nonblock | os.SOCK_CLOEXEC; var accepted_addr: Address = undefined; diff --git a/lib/std/net/test.zig b/lib/std/net/test.zig index 1bdd209837..49dbdbb218 100644 --- a/lib/std/net/test.zig +++ b/lib/std/net/test.zig @@ -93,7 +93,7 @@ test "listen on a port, send bytes, receive bytes" { // TODO doing this at comptime crashed the compiler const localhost = net.Address.parseIp("127.0.0.1", 0); - var server = net.TcpServer.init(net.TcpServer.Options{}); + var server = net.StreamServer.init(net.StreamServer.Options{}); defer server.deinit(); try server.listen(localhost); @@ -114,7 +114,7 @@ fn testClient(addr: net.Address) anyerror!void { testing.expect(mem.eql(u8, msg, "hello from server\n")); } -fn testServer(server: *net.TcpServer) anyerror!void { +fn testServer(server: *net.StreamServer) anyerror!void { var client_file = try server.accept(); const stream = &client_file.outStream().stream; From 05ae21b78ed58e621fd2c10456a3f100a8107e3a Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 9 Nov 2019 12:51:33 -0300 Subject: [PATCH 05/12] make StreamServer.listen family-agnostic - rename Address.parseUnix to Address.initUnix --- lib/std/net.zig | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 0f7c76438f..1f680cd3ec 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -40,21 +40,6 @@ pub const Address = extern union { return error.InvalidIPAddressFormat; } - pub fn parseUnix(path: []const u8) !Address { - var sock_addr = os.sockaddr_un{ - .family = os.AF_UNIX, - .path = undefined, - }; - - // this enables us to have the proper length of the socket in getOsSockLen - mem.zero(&sock_addr.path); - - if (path.len > sock_addr.path.len) return error.NameTooLong; - mem.copy(u8, &sock_addr.path, path); - - return Address{ .un = sock_addr }; - } - pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !Address { switch (family) { os.AF_INET => return parseIp4(name, port), @@ -230,6 +215,21 @@ pub const Address = extern union { }; } + pub fn initUnix(path: []const u8) !Address { + var sock_addr = os.sockaddr_un{ + .family = os.AF_UNIX, + .path = undefined, + }; + + // this enables us to have the proper length of the socket in getOsSockLen + mem.zero(&sock_addr.path); + + if (path.len > sock_addr.path.len) return error.NameTooLong; + mem.copy(u8, &sock_addr.path, path); + + return Address{ .un = sock_addr }; + } + /// Returns the port in native endian. pub fn getPort(self: Address) u16 { const big_endian_port = switch (self.any.family) { @@ -1306,7 +1306,7 @@ pub const StreamServer = struct { pub fn listen(self: *StreamServer, address: Address) !void { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; - const sockfd = try os.socket(os.AF_INET, sock_flags, os.IPPROTO_TCP); + const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); self.sockfd = sockfd; errdefer { os.close(sockfd); From 348c0232a5612eb9bdb445e80e6e201d24911dcc Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 9 Nov 2019 14:53:48 -0300 Subject: [PATCH 06/12] miscellaneous fixes - make connextUnixSocket use std.net.Address - fix StreamServer.listen giving wrong protocol for unix sockets --- lib/std/net.zig | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 1f680cd3ec..24d312e3f6 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -222,7 +222,7 @@ pub const Address = extern union { }; // this enables us to have the proper length of the socket in getOsSockLen - mem.zero(&sock_addr.path); + mem.set(u8, &sock_addr.path, 0); if (path.len > sock_addr.path.len) return error.NameTooLong; mem.copy(u8, &sock_addr.path, path); @@ -347,9 +347,9 @@ pub const Address = extern union { switch (self.any.family) { os.AF_INET => return @sizeOf(os.sockaddr_in), os.AF_INET6 => return @sizeOf(os.sockaddr_in6), - os.AF_UNIX => blk: { - const path_len = std.mem.len(self.un.path.len); - break :blk @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); + os.AF_UNIX => { + const path_len = std.mem.len(u8, &self.un.path); + return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); }, else => unreachable, } @@ -365,16 +365,13 @@ pub fn connectUnixSocket(path: []const u8) !fs.File { ); errdefer os.close(sockfd); - var sock_addr = os.sockaddr_un{ - .family = os.AF_UNIX, - .path = undefined, - }; + var addr = try std.net.Address.initUnix(path); - if (path.len > sock_addr.path.len) return error.NameTooLong; - mem.copy(u8, &sock_addr.path, path); - - const size = @intCast(u32, @sizeOf(os.sockaddr_un) - sock_addr.path.len + path.len); - try os.connect(sockfd, &sock_addr, size); + try os.connect( + sockfd, + &addr.any, + addr.getOsSockLen(), + ); return fs.File.openHandle(sockfd); } @@ -1306,7 +1303,8 @@ pub const StreamServer = struct { pub fn listen(self: *StreamServer, address: Address) !void { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; - const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); + const proto = if (address.any.family == os.AF_UNIX) u32(0) else os.IPPROTO_TCP; + const sockfd = try os.socket(address.any.family, sock_flags, proto); self.sockfd = sockfd; errdefer { os.close(sockfd); From e4704f68f850021543ceab86c253806fdded7733 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 9 Nov 2019 15:10:39 -0300 Subject: [PATCH 07/12] use at-as --- lib/std/net.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index d6dba254e5..98400f23cb 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1303,7 +1303,7 @@ pub const StreamServer = struct { pub fn listen(self: *StreamServer, address: Address) !void { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; - const proto = if (address.any.family == os.AF_UNIX) u32(0) else os.IPPROTO_TCP; + const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP; const sockfd = try os.socket(address.any.family, sock_flags, proto); self.sockfd = sockfd; errdefer { From 25423eb453e1c0cece457f4012106dbcd97f29f3 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Nov 2019 10:50:22 -0300 Subject: [PATCH 08/12] add errors/panics for unsupported OSes --- lib/std/net.zig | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 98400f23cb..bbfa47f407 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -14,7 +14,10 @@ pub const Address = extern union { any: os.sockaddr, in: os.sockaddr_in, in6: os.sockaddr_in6, - un: os.sockaddr_un, + un: switch (builtin.os) { + .linux, .macosx, .freebsd, .netbsd => os.sockaddr_un, + else => void, + }, // TODO this crashed the compiler //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); @@ -216,6 +219,11 @@ pub const Address = extern union { } pub fn initUnix(path: []const u8) !Address { + switch (builtin.os) { + .linux, .macosx, .freebsd, .netbsd => {}, + else => return error.UnsupportedOS, + } + var sock_addr = os.sockaddr_un{ .family = os.AF_UNIX, .path = undefined, @@ -331,6 +339,10 @@ pub const Address = extern union { try std.fmt.format(context, Errors, output, "]:{}", port); }, os.AF_UNIX => { + if (@typeOf(self.un) == void) { + @panic("unsupported OS"); + } + try std.fmt.format(context, Errors, output, "{}", self.un.path); }, else => unreachable, @@ -348,6 +360,10 @@ pub const Address = extern union { os.AF_INET => return @sizeOf(os.sockaddr_in), os.AF_INET6 => return @sizeOf(os.sockaddr_in6), os.AF_UNIX => { + if (@typeOf(self.un) == void) { + @panic("unsupported OS"); + } + const path_len = std.mem.len(u8, &self.un.path); return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len); }, @@ -1304,6 +1320,13 @@ pub const StreamServer = struct { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP; + if (address.any.family == os.AF_UNIX and @typeOf(address.un) == void) { + return error{ + /// When the OS does not have support for AF_UNIX sockets. + UnsupportedOS, + }.UnsupportedOS; + } + const sockfd = try os.socket(address.any.family, sock_flags, proto); self.sockfd = sockfd; errdefer { From 2d02920a905e20b7f43991657cdc028e444b9df7 Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Nov 2019 14:04:52 -0300 Subject: [PATCH 09/12] use hasDecl instead of switch on builtin.os --- lib/std/net.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index bbfa47f407..7ee8b9a756 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -10,14 +10,13 @@ test "" { _ = @import("net/test.zig"); } +const has_unix_sockets = @hasDecl(os, "sockaddr_un"); + pub const Address = extern union { any: os.sockaddr, in: os.sockaddr_in, in6: os.sockaddr_in6, - un: switch (builtin.os) { - .linux, .macosx, .freebsd, .netbsd => os.sockaddr_un, - else => void, - }, + un: if (has_unix_sockets) os.sockaddr_un else void, // TODO this crashed the compiler //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); @@ -239,6 +238,7 @@ pub const Address = extern union { } /// Returns the port in native endian. + /// Asserts that the address is ip4 or ip6. pub fn getPort(self: Address) u16 { const big_endian_port = switch (self.any.family) { os.AF_INET => self.in.port, @@ -249,6 +249,7 @@ pub const Address = extern union { } /// `port` is native-endian. + /// Asserts that the address is ip4 or ip6. pub fn setPort(self: *Address, port: u16) void { const ptr = switch (self.any.family) { os.AF_INET => &self.in.port, From d99ecef9433d677b4f834d466d7057b0f35e4dbf Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Nov 2019 14:17:39 -0300 Subject: [PATCH 10/12] replace panic to unreachable - remove error.UnsupportedOS from StreamServer.listen --- lib/std/net.zig | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index 7ee8b9a756..af79f22757 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -340,8 +340,8 @@ pub const Address = extern union { try std.fmt.format(context, Errors, output, "]:{}", port); }, os.AF_UNIX => { - if (@typeOf(self.un) == void) { - @panic("unsupported OS"); + if (!has_unix_sockets) { + unreachable; } try std.fmt.format(context, Errors, output, "{}", self.un.path); @@ -361,8 +361,8 @@ pub const Address = extern union { os.AF_INET => return @sizeOf(os.sockaddr_in), os.AF_INET6 => return @sizeOf(os.sockaddr_in6), os.AF_UNIX => { - if (@typeOf(self.un) == void) { - @panic("unsupported OS"); + if (!has_unix_sockets) { + unreachable; } const path_len = std.mem.len(u8, &self.un.path); @@ -1321,12 +1321,6 @@ pub const StreamServer = struct { const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; const proto = if (address.any.family == os.AF_UNIX) @as(u32, 0) else os.IPPROTO_TCP; - if (address.any.family == os.AF_UNIX and @typeOf(address.un) == void) { - return error{ - /// When the OS does not have support for AF_UNIX sockets. - UnsupportedOS, - }.UnsupportedOS; - } const sockfd = try os.socket(address.any.family, sock_flags, proto); self.sockfd = sockfd; From d1eabe81a99c3fe6c0e551e9bab90a3bbae509fe Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Nov 2019 14:38:33 -0300 Subject: [PATCH 11/12] add sockaddr_un to os/bits/windows --- lib/std/os/bits/windows.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/std/os/bits/windows.zig b/lib/std/os/bits/windows.zig index 178811bb1e..b5b8a9c2d4 100644 --- a/lib/std/os/bits/windows.zig +++ b/lib/std/os/bits/windows.zig @@ -186,6 +186,11 @@ pub const sockaddr_in6 = extern struct { pub const in6_addr = [16]u8; pub const in_addr = u32; +pub const sockaddr_un = extern struct { + family: sa_family_t = AF_UNIX, + path: [108]u8, +}; + pub const AF_UNSPEC = 0; pub const AF_UNIX = 1; pub const AF_INET = 2; From c8a8da28049d2e9aece857359725b2a8f6d3732b Mon Sep 17 00:00:00 2001 From: Luna Date: Sun, 10 Nov 2019 16:44:18 -0300 Subject: [PATCH 12/12] remove builtin.os check in Address.initUnix --- lib/std/net.zig | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/std/net.zig b/lib/std/net.zig index af79f22757..531b8ce797 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -218,11 +218,6 @@ pub const Address = extern union { } pub fn initUnix(path: []const u8) !Address { - switch (builtin.os) { - .linux, .macosx, .freebsd, .netbsd => {}, - else => return error.UnsupportedOS, - } - var sock_addr = os.sockaddr_un{ .family = os.AF_UNIX, .path = undefined,