| author | |
| committer | |
| log | 8044ed4c66840503186fc3bbeac4328c6cf76349 |
| tree | e2292041959ea606e7f0d97b087d11a428534aa6 |
| parent | 194e29adfccbd5076a3523572c7e119d4bfa647d |
7 files changed, 193 insertions(+), 34 deletions(-)
lib/std/net.zig+17-11| ... | @@ -11,11 +11,7 @@ const mem = std.mem; | ... | @@ -11,11 +11,7 @@ const mem = std.mem; |
| 11 | const os = std.os; | 11 | const os = std.os; |
| 12 | const fs = std.fs; | 12 | const fs = std.fs; |
| 13 | 13 | ||
| 14 | test "" { | 14 | pub const has_unix_sockets = @hasDecl(os, "sockaddr_un"); |
| 15 | _ = @import("net/test.zig"); | ||
| 16 | } | ||
| 17 | |||
| 18 | const has_unix_sockets = @hasDecl(os, "sockaddr_un"); | ||
| 19 | 15 | ||
| 20 | pub const Address = extern union { | 16 | pub const Address = extern union { |
| 21 | any: os.sockaddr, | 17 | any: os.sockaddr, |
| ... | @@ -1546,16 +1542,14 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) | ... | @@ -1546,16 +1542,14 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) |
| 1546 | if (data.len != 4) return error.InvalidDnsARecord; | 1542 | if (data.len != 4) return error.InvalidDnsARecord; |
| 1547 | const new_addr = try ctx.addrs.addOne(); | 1543 | const new_addr = try ctx.addrs.addOne(); |
| 1548 | new_addr.* = LookupAddr{ | 1544 | new_addr.* = LookupAddr{ |
| 1549 | // TODO slice [0..4] to make this *[4]u8 without @ptrCast | 1545 | .addr = Address.initIp4(data[0..4].*, ctx.port), |
| 1550 | .addr = Address.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), | ||
| 1551 | }; | 1546 | }; |
| 1552 | }, | 1547 | }, |
| 1553 | os.RR_AAAA => { | 1548 | os.RR_AAAA => { |
| 1554 | if (data.len != 16) return error.InvalidDnsAAAARecord; | 1549 | if (data.len != 16) return error.InvalidDnsAAAARecord; |
| 1555 | const new_addr = try ctx.addrs.addOne(); | 1550 | const new_addr = try ctx.addrs.addOne(); |
| 1556 | new_addr.* = LookupAddr{ | 1551 | new_addr.* = LookupAddr{ |
| 1557 | // TODO slice [0..16] to make this *[16]u8 without @ptrCast | 1552 | .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0), |
| 1558 | .addr = Address.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), | ||
| 1559 | }; | 1553 | }; |
| 1560 | }, | 1554 | }, |
| 1561 | os.RR_CNAME => { | 1555 | os.RR_CNAME => { |
| ... | @@ -1579,7 +1573,7 @@ pub const StreamServer = struct { | ... | @@ -1579,7 +1573,7 @@ pub const StreamServer = struct { |
| 1579 | /// `undefined` until `listen` returns successfully. | 1573 | /// `undefined` until `listen` returns successfully. |
| 1580 | listen_address: Address, | 1574 | listen_address: Address, |
| 1581 | 1575 | ||
| 1582 | sockfd: ?os.fd_t, | 1576 | sockfd: ?os.socket_t, |
| 1583 | 1577 | ||
| 1584 | pub const Options = struct { | 1578 | pub const Options = struct { |
| 1585 | /// How many connections the kernel will accept on the application's behalf. | 1579 | /// How many connections the kernel will accept on the application's behalf. |
| ... | @@ -1622,7 +1616,7 @@ pub const StreamServer = struct { | ... | @@ -1622,7 +1616,7 @@ pub const StreamServer = struct { |
| 1622 | 1616 | ||
| 1623 | if (self.reuse_address) { | 1617 | if (self.reuse_address) { |
| 1624 | try os.setsockopt( | 1618 | try os.setsockopt( |
| 1625 | self.sockfd.?, | 1619 | sockfd, |
| 1626 | os.SOL_SOCKET, | 1620 | os.SOL_SOCKET, |
| 1627 | os.SO_REUSEADDR, | 1621 | os.SO_REUSEADDR, |
| 1628 | &mem.toBytes(@as(c_int, 1)), | 1622 | &mem.toBytes(@as(c_int, 1)), |
| ... | @@ -1670,6 +1664,14 @@ pub const StreamServer = struct { | ... | @@ -1670,6 +1664,14 @@ pub const StreamServer = struct { |
| 1670 | /// Permission to create a socket of the specified type and/or | 1664 | /// Permission to create a socket of the specified type and/or |
| 1671 | /// protocol is denied. | 1665 | /// protocol is denied. |
| 1672 | PermissionDenied, | 1666 | PermissionDenied, |
| 1667 | |||
| 1668 | FileDescriptorNotASocket, | ||
| 1669 | |||
| 1670 | ConnectionResetByPeer, | ||
| 1671 | |||
| 1672 | NetworkSubsystemFailed, | ||
| 1673 | |||
| 1674 | OperationNotSupported, | ||
| 1673 | } || os.UnexpectedError; | 1675 | } || os.UnexpectedError; |
| 1674 | 1676 | ||
| 1675 | pub const Connection = struct { | 1677 | pub const Connection = struct { |
| ... | @@ -1701,3 +1703,7 @@ pub const StreamServer = struct { | ... | @@ -1701,3 +1703,7 @@ pub const StreamServer = struct { |
| 1701 | } | 1703 | } |
| 1702 | } | 1704 | } |
| 1703 | }; | 1705 | }; |
| 1706 | |||
| 1707 | test "" { | ||
| 1708 | _ = @import("net/test.zig"); | ||
| 1709 | } |
lib/std/net/test.zig+65-8| ... | @@ -95,22 +95,79 @@ test "parse and render IPv4 addresses" { | ... | @@ -95,22 +95,79 @@ test "parse and render IPv4 addresses" { |
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | test "resolve DNS" { | 97 | test "resolve DNS" { |
| 98 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 99 | |||
| 98 | if (std.builtin.os.tag == .windows) { | 100 | if (std.builtin.os.tag == .windows) { |
| 99 | _ = try std.os.windows.WSAStartup(2, 2); | 101 | _ = try std.os.windows.WSAStartup(2, 2); |
| 100 | } | 102 | } |
| 101 | if (builtin.os.tag == .wasi) { | 103 | defer { |
| 102 | // DNS resolution not implemented on Windows yet. | 104 | if (std.builtin.os.tag == .windows) { |
| 103 | return error.SkipZigTest; | 105 | std.os.windows.WSACleanup() catch unreachable; |
| 106 | } | ||
| 107 | } | ||
| 108 | |||
| 109 | // Resolve localhost, this should not fail. | ||
| 110 | { | ||
| 111 | const localhost_v4 = try net.Address.parseIp("127.0.0.1", 80); | ||
| 112 | const localhost_v6 = try net.Address.parseIp("::2", 80); | ||
| 113 | |||
| 114 | const result = try net.getAddressList(testing.allocator, "localhost", 80); | ||
| 115 | defer result.deinit(); | ||
| 116 | for (result.addrs) |addr| { | ||
| 117 | if (addr.eql(localhost_v4) or addr.eql(localhost_v6)) break; | ||
| 118 | } else @panic("unexpected address for localhost"); | ||
| 104 | } | 119 | } |
| 105 | 120 | ||
| 106 | const address_list = net.getAddressList(testing.allocator, "example.com", 80) catch |err| switch (err) { | 121 | { |
| 107 | // The tests are required to work even when there is no Internet connection, | 122 | // The tests are required to work even when there is no Internet connection, |
| 108 | // so some of these errors we must accept and skip the test. | 123 | // so some of these errors we must accept and skip the test. |
| 109 | error.UnknownHostName => return error.SkipZigTest, | 124 | const result = net.getAddressList(testing.allocator, "example.com", 80) catch |err| switch (err) { |
| 110 | error.TemporaryNameServerFailure => return error.SkipZigTest, | 125 | error.UnknownHostName => return error.SkipZigTest, |
| 111 | else => return err, | 126 | error.TemporaryNameServerFailure => return error.SkipZigTest, |
| 127 | else => return err, | ||
| 128 | }; | ||
| 129 | result.deinit(); | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | test "listen on a port, send bytes, receive bytes" { | ||
| 134 | if (builtin.single_threaded) return error.SkipZigTest; | ||
| 135 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 136 | |||
| 137 | if (std.builtin.os.tag == .windows) { | ||
| 138 | _ = try std.os.windows.WSAStartup(2, 2); | ||
| 139 | } | ||
| 140 | defer { | ||
| 141 | if (std.builtin.os.tag == .windows) { | ||
| 142 | std.os.windows.WSACleanup() catch unreachable; | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | // Try only the IPv4 variant as some CI builders have no IPv6 localhost | ||
| 147 | // configured. | ||
| 148 | const localhost = try net.Address.parseIp("127.0.0.1", 8080); | ||
| 149 | |||
| 150 | var server = net.StreamServer.init(.{}); | ||
| 151 | try server.listen(localhost); | ||
| 152 | |||
| 153 | const S = struct { | ||
| 154 | fn clientFn(server_address: net.Address) !void { | ||
| 155 | const socket = try net.tcpConnectToAddress(server_address); | ||
| 156 | defer socket.close(); | ||
| 157 | |||
| 158 | _ = try socket.writer().writeAll("Hello world!"); | ||
| 159 | } | ||
| 112 | }; | 160 | }; |
| 113 | address_list.deinit(); | 161 | |
| 162 | const t = try std.Thread.spawn(server.listen_address, S.clientFn); | ||
| 163 | defer t.wait(); | ||
| 164 | |||
| 165 | var client = try server.accept(); | ||
| 166 | var buf: [16]u8 = undefined; | ||
| 167 | const n = try client.file.reader().read(&buf); | ||
| 168 | |||
| 169 | testing.expectEqual(@as(usize, 12), n); | ||
| 170 | testing.expectEqualSlices(u8, "Hello world!", buf[0..n]); | ||
| 114 | } | 171 | } |
| 115 | 172 | ||
| 116 | test "listen on a port, send bytes, receive bytes" { | 173 | test "listen on a port, send bytes, receive bytes" { |
lib/std/os.zig+32-13| ... | @@ -5378,22 +5378,41 @@ pub const SetSockOptError = error{ | ... | @@ -5378,22 +5378,41 @@ pub const SetSockOptError = error{ |
| 5378 | 5378 | ||
| 5379 | /// Insufficient resources are available in the system to complete the call. | 5379 | /// Insufficient resources are available in the system to complete the call. |
| 5380 | SystemResources, | 5380 | SystemResources, |
| 5381 | |||
| 5382 | NetworkSubsystemFailed, | ||
| 5383 | FileDescriptorNotASocket, | ||
| 5384 | SocketNotBound, | ||
| 5381 | } || UnexpectedError; | 5385 | } || UnexpectedError; |
| 5382 | 5386 | ||
| 5383 | /// Set a socket's options. | 5387 | /// Set a socket's options. |
| 5384 | pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { | 5388 | pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { |
| 5385 | switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { | 5389 | if (builtin.os.tag == .windows) { |
| 5386 | 0 => {}, | 5390 | const rc = windows.ws2_32.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)); |
| 5387 | EBADF => unreachable, // always a race condition | 5391 | if (rc == windows.ws2_32.SOCKET_ERROR) { |
| 5388 | ENOTSOCK => unreachable, // always a race condition | 5392 | switch (windows.ws2_32.WSAGetLastError()) { |
| 5389 | EINVAL => unreachable, | 5393 | .WSANOTINITIALISED => unreachable, |
| 5390 | EFAULT => unreachable, | 5394 | .WSAENETDOWN => return error.NetworkSubsystemFailed, |
| 5391 | EDOM => return error.TimeoutTooBig, | 5395 | .WSAEFAULT => unreachable, |
| 5392 | EISCONN => return error.AlreadyConnected, | 5396 | .WSAENOTSOCK => return error.FileDescriptorNotASocket, |
| 5393 | ENOPROTOOPT => return error.InvalidProtocolOption, | 5397 | .WSAEINVAL => return error.SocketNotBound, |
| 5394 | ENOMEM => return error.SystemResources, | 5398 | else => |err| return windows.unexpectedWSAError(err), |
| 5395 | ENOBUFS => return error.SystemResources, | 5399 | } |
| 5396 | else => |err| return unexpectedErrno(err), | 5400 | } |
| 5401 | return; | ||
| 5402 | } else { | ||
| 5403 | switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { | ||
| 5404 | 0 => {}, | ||
| 5405 | EBADF => unreachable, // always a race condition | ||
| 5406 | ENOTSOCK => unreachable, // always a race condition | ||
| 5407 | EINVAL => unreachable, | ||
| 5408 | EFAULT => unreachable, | ||
| 5409 | EDOM => return error.TimeoutTooBig, | ||
| 5410 | EISCONN => return error.AlreadyConnected, | ||
| 5411 | ENOPROTOOPT => return error.InvalidProtocolOption, | ||
| 5412 | ENOMEM => return error.SystemResources, | ||
| 5413 | ENOBUFS => return error.SystemResources, | ||
| 5414 | else => |err| return unexpectedErrno(err), | ||
| 5415 | } | ||
| 5397 | } | 5416 | } |
| 5398 | } | 5417 | } |
| 5399 | 5418 |
lib/std/os/bits/windows.zig+35| ... | @@ -256,6 +256,41 @@ pub const POLLERR = ws2_32.POLLERR; | ... | @@ -256,6 +256,41 @@ pub const POLLERR = ws2_32.POLLERR; |
| 256 | pub const POLLHUP = ws2_32.POLLHUP; | 256 | pub const POLLHUP = ws2_32.POLLHUP; |
| 257 | pub const POLLNVAL = ws2_32.POLLNVAL; | 257 | pub const POLLNVAL = ws2_32.POLLNVAL; |
| 258 | 258 | ||
| 259 | pub const SOL_SOCKET = ws2_32.SOL_SOCKET; | ||
| 260 | |||
| 261 | pub const SO_DEBUG = ws2_32.SO_DEBUG; | ||
| 262 | pub const SO_ACCEPTCONN = ws2_32.SO_ACCEPTCONN; | ||
| 263 | pub const SO_REUSEADDR = ws2_32.SO_REUSEADDR; | ||
| 264 | pub const SO_KEEPALIVE = ws2_32.SO_KEEPALIVE; | ||
| 265 | pub const SO_DONTROUTE = ws2_32.SO_DONTROUTE; | ||
| 266 | pub const SO_BROADCAST = ws2_32.SO_BROADCAST; | ||
| 267 | pub const SO_USELOOPBACK = ws2_32.SO_USELOOPBACK; | ||
| 268 | pub const SO_LINGER = ws2_32.SO_LINGER; | ||
| 269 | pub const SO_OOBINLINE = ws2_32.SO_OOBINLINE; | ||
| 270 | |||
| 271 | pub const SO_DONTLINGER = ws2_32.SO_DONTLINGER; | ||
| 272 | pub const SO_EXCLUSIVEADDRUSE = ws2_32.SO_EXCLUSIVEADDRUSE; | ||
| 273 | |||
| 274 | pub const SO_SNDBUF = ws2_32.SO_SNDBUF; | ||
| 275 | pub const SO_RCVBUF = ws2_32.SO_RCVBUF; | ||
| 276 | pub const SO_SNDLOWAT = ws2_32.SO_SNDLOWAT; | ||
| 277 | pub const SO_RCVLOWAT = ws2_32.SO_RCVLOWAT; | ||
| 278 | pub const SO_SNDTIMEO = ws2_32.SO_SNDTIMEO; | ||
| 279 | pub const SO_RCVTIMEO = ws2_32.SO_RCVTIMEO; | ||
| 280 | pub const SO_ERROR = ws2_32.SO_ERROR; | ||
| 281 | pub const SO_TYPE = ws2_32.SO_TYPE; | ||
| 282 | |||
| 283 | pub const SO_GROUP_ID = ws2_32.SO_GROUP_ID; | ||
| 284 | pub const SO_GROUP_PRIORITY = ws2_32.SO_GROUP_PRIORITY; | ||
| 285 | pub const SO_MAX_MSG_SIZE = ws2_32.SO_MAX_MSG_SIZE; | ||
| 286 | pub const SO_PROTOCOL_INFOA = ws2_32.SO_PROTOCOL_INFOA; | ||
| 287 | pub const SO_PROTOCOL_INFOW = ws2_32.SO_PROTOCOL_INFOW; | ||
| 288 | |||
| 289 | pub const PVD_CONFIG = ws2_32.PVD_CONFIG; | ||
| 290 | pub const SO_CONDITIONAL_ACCEPT = ws2_32.SO_CONDITIONAL_ACCEPT; | ||
| 291 | |||
| 292 | pub const TCP_NODELAY = ws2_32.TCP_NODELAY; | ||
| 293 | |||
| 259 | pub const O_RDONLY = 0o0; | 294 | pub const O_RDONLY = 0o0; |
| 260 | pub const O_WRONLY = 0o1; | 295 | pub const O_WRONLY = 0o1; |
| 261 | pub const O_RDWR = 0o2; | 296 | pub const O_RDWR = 0o2; |
lib/std/os/linux.zig+1-1| ... | @@ -1279,7 +1279,7 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, | ... | @@ -1279,7 +1279,7 @@ pub fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: ?*const rlimit, |
| 1279 | @bitCast(usize, @as(isize, pid)), | 1279 | @bitCast(usize, @as(isize, pid)), |
| 1280 | @bitCast(usize, @as(isize, @enumToInt(resource))), | 1280 | @bitCast(usize, @as(isize, @enumToInt(resource))), |
| 1281 | @ptrToInt(new_limit), | 1281 | @ptrToInt(new_limit), |
| 1282 | @ptrToInt(old_limit) | 1282 | @ptrToInt(old_limit), |
| 1283 | ); | 1283 | ); |
| 1284 | } | 1284 | } |
| 1285 | 1285 |
lib/std/os/test.zig+1-1| ... | @@ -594,7 +594,7 @@ test "fsync" { | ... | @@ -594,7 +594,7 @@ test "fsync" { |
| 594 | 594 | ||
| 595 | test "getrlimit and setrlimit" { | 595 | test "getrlimit and setrlimit" { |
| 596 | // TODO enable for other systems when implemented | 596 | // TODO enable for other systems when implemented |
| 597 | if(builtin.os.tag != .linux){ | 597 | if (builtin.os.tag != .linux) { |
| 598 | return error.SkipZigTest; | 598 | return error.SkipZigTest; |
| 599 | } | 599 | } |
| 600 | 600 |
lib/std/os/windows/ws2_32.zig+42| ... | @@ -718,6 +718,41 @@ const IOC_WS2 = 0x08000000; | ... | @@ -718,6 +718,41 @@ const IOC_WS2 = 0x08000000; |
| 718 | 718 | ||
| 719 | pub const SIO_BASE_HANDLE = IOC_OUT | IOC_WS2 | 34; | 719 | pub const SIO_BASE_HANDLE = IOC_OUT | IOC_WS2 | 34; |
| 720 | 720 | ||
| 721 | pub const SOL_SOCKET = 0xffff; | ||
| 722 | |||
| 723 | pub const SO_DEBUG = 0x0001; | ||
| 724 | pub const SO_ACCEPTCONN = 0x0002; | ||
| 725 | pub const SO_REUSEADDR = 0x0004; | ||
| 726 | pub const SO_KEEPALIVE = 0x0008; | ||
| 727 | pub const SO_DONTROUTE = 0x0010; | ||
| 728 | pub const SO_BROADCAST = 0x0020; | ||
| 729 | pub const SO_USELOOPBACK = 0x0040; | ||
| 730 | pub const SO_LINGER = 0x0080; | ||
| 731 | pub const SO_OOBINLINE = 0x0100; | ||
| 732 | |||
| 733 | pub const SO_DONTLINGER = ~@as(u32, SO_LINGER); | ||
| 734 | pub const SO_EXCLUSIVEADDRUSE = ~@as(u32, SO_REUSEADDR); | ||
| 735 | |||
| 736 | pub const SO_SNDBUF = 0x1001; | ||
| 737 | pub const SO_RCVBUF = 0x1002; | ||
| 738 | pub const SO_SNDLOWAT = 0x1003; | ||
| 739 | pub const SO_RCVLOWAT = 0x1004; | ||
| 740 | pub const SO_SNDTIMEO = 0x1005; | ||
| 741 | pub const SO_RCVTIMEO = 0x1006; | ||
| 742 | pub const SO_ERROR = 0x1007; | ||
| 743 | pub const SO_TYPE = 0x1008; | ||
| 744 | |||
| 745 | pub const SO_GROUP_ID = 0x2001; | ||
| 746 | pub const SO_GROUP_PRIORITY = 0x2002; | ||
| 747 | pub const SO_MAX_MSG_SIZE = 0x2003; | ||
| 748 | pub const SO_PROTOCOL_INFOA = 0x2004; | ||
| 749 | pub const SO_PROTOCOL_INFOW = 0x2005; | ||
| 750 | |||
| 751 | pub const PVD_CONFIG = 0x3001; | ||
| 752 | pub const SO_CONDITIONAL_ACCEPT = 0x3002; | ||
| 753 | |||
| 754 | pub const TCP_NODELAY = 0x0001; | ||
| 755 | |||
| 721 | pub extern "ws2_32" fn WSAStartup( | 756 | pub extern "ws2_32" fn WSAStartup( |
| 722 | wVersionRequired: WORD, | 757 | wVersionRequired: WORD, |
| 723 | lpWSAData: *WSADATA, | 758 | lpWSAData: *WSADATA, |
| ... | @@ -835,3 +870,10 @@ pub extern "ws2_32" fn getsockname( | ... | @@ -835,3 +870,10 @@ pub extern "ws2_32" fn getsockname( |
| 835 | name: *sockaddr, | 870 | name: *sockaddr, |
| 836 | namelen: *c_int, | 871 | namelen: *c_int, |
| 837 | ) callconv(.Stdcall) c_int; | 872 | ) callconv(.Stdcall) c_int; |
| 873 | pub extern "ws2_32" fn setsockopt( | ||
| 874 | s: SOCKET, | ||
| 875 | level: u32, | ||
| 876 | optname: u32, | ||
| 877 | optval: ?*const c_void, | ||
| 878 | optlen: socklen_t, | ||
| 879 | ) callconv(.Stdcall) c_int; |