| 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 | const os = std.os; |
| 12 | 12 | const fs = std.fs; |
| 13 | 13 | |
| 14 | test "" { | |
| 15 | _ = @import("net/test.zig"); | |
| 16 | } | |
| 17 | ||
| 18 | const has_unix_sockets = @hasDecl(os, "sockaddr_un"); | |
| 14 | pub const has_unix_sockets = @hasDecl(os, "sockaddr_un"); | |
| 19 | 15 | |
| 20 | 16 | pub const Address = extern union { |
| 21 | 17 | any: os.sockaddr, |
| ... | ... | @@ -1546,16 +1542,14 @@ fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) |
| 1546 | 1542 | if (data.len != 4) return error.InvalidDnsARecord; |
| 1547 | 1543 | const new_addr = try ctx.addrs.addOne(); |
| 1548 | 1544 | new_addr.* = LookupAddr{ |
| 1549 | // TODO slice [0..4] to make this *[4]u8 without @ptrCast | |
| 1550 | .addr = Address.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), | |
| 1545 | .addr = Address.initIp4(data[0..4].*, ctx.port), | |
| 1551 | 1546 | }; |
| 1552 | 1547 | }, |
| 1553 | 1548 | os.RR_AAAA => { |
| 1554 | 1549 | if (data.len != 16) return error.InvalidDnsAAAARecord; |
| 1555 | 1550 | const new_addr = try ctx.addrs.addOne(); |
| 1556 | 1551 | new_addr.* = LookupAddr{ |
| 1557 | // TODO slice [0..16] to make this *[16]u8 without @ptrCast | |
| 1558 | .addr = Address.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), | |
| 1552 | .addr = Address.initIp6(data[0..16].*, ctx.port, 0, 0), | |
| 1559 | 1553 | }; |
| 1560 | 1554 | }, |
| 1561 | 1555 | os.RR_CNAME => { |
| ... | ... | @@ -1579,7 +1573,7 @@ pub const StreamServer = struct { |
| 1579 | 1573 | /// `undefined` until `listen` returns successfully. |
| 1580 | 1574 | listen_address: Address, |
| 1581 | 1575 | |
| 1582 | sockfd: ?os.fd_t, | |
| 1576 | sockfd: ?os.socket_t, | |
| 1583 | 1577 | |
| 1584 | 1578 | pub const Options = struct { |
| 1585 | 1579 | /// How many connections the kernel will accept on the application's behalf. |
| ... | ... | @@ -1622,7 +1616,7 @@ pub const StreamServer = struct { |
| 1622 | 1616 | |
| 1623 | 1617 | if (self.reuse_address) { |
| 1624 | 1618 | try os.setsockopt( |
| 1625 | self.sockfd.?, | |
| 1619 | sockfd, | |
| 1626 | 1620 | os.SOL_SOCKET, |
| 1627 | 1621 | os.SO_REUSEADDR, |
| 1628 | 1622 | &mem.toBytes(@as(c_int, 1)), |
| ... | ... | @@ -1670,6 +1664,14 @@ pub const StreamServer = struct { |
| 1670 | 1664 | /// Permission to create a socket of the specified type and/or |
| 1671 | 1665 | /// protocol is denied. |
| 1672 | 1666 | PermissionDenied, |
| 1667 | ||
| 1668 | FileDescriptorNotASocket, | |
| 1669 | ||
| 1670 | ConnectionResetByPeer, | |
| 1671 | ||
| 1672 | NetworkSubsystemFailed, | |
| 1673 | ||
| 1674 | OperationNotSupported, | |
| 1673 | 1675 | } || os.UnexpectedError; |
| 1674 | 1676 | |
| 1675 | 1677 | pub const Connection = 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 | 95 | } |
| 96 | 96 | |
| 97 | 97 | test "resolve DNS" { |
| 98 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 99 | ||
| 98 | 100 | if (std.builtin.os.tag == .windows) { |
| 99 | 101 | _ = try std.os.windows.WSAStartup(2, 2); |
| 100 | 102 | } |
| 101 | if (builtin.os.tag == .wasi) { | |
| 102 | // DNS resolution not implemented on Windows yet. | |
| 103 | return error.SkipZigTest; | |
| 103 | defer { | |
| 104 | if (std.builtin.os.tag == .windows) { | |
| 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 | 122 | // The tests are required to work even when there is no Internet connection, |
| 108 | 123 | // so some of these errors we must accept and skip the test. |
| 109 | error.UnknownHostName => return error.SkipZigTest, | |
| 110 | error.TemporaryNameServerFailure => return error.SkipZigTest, | |
| 111 | else => return err, | |
| 124 | const result = net.getAddressList(testing.allocator, "example.com", 80) catch |err| switch (err) { | |
| 125 | error.UnknownHostName => return error.SkipZigTest, | |
| 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 | 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 | 5378 | |
| 5379 | 5379 | /// Insufficient resources are available in the system to complete the call. |
| 5380 | 5380 | SystemResources, |
| 5381 | ||
| 5382 | NetworkSubsystemFailed, | |
| 5383 | FileDescriptorNotASocket, | |
| 5384 | SocketNotBound, | |
| 5381 | 5385 | } || UnexpectedError; |
| 5382 | 5386 | |
| 5383 | 5387 | /// Set a socket's options. |
| 5384 | pub fn setsockopt(fd: fd_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)))) { | |
| 5386 | 0 => {}, | |
| 5387 | EBADF => unreachable, // always a race condition | |
| 5388 | ENOTSOCK => unreachable, // always a race condition | |
| 5389 | EINVAL => unreachable, | |
| 5390 | EFAULT => unreachable, | |
| 5391 | EDOM => return error.TimeoutTooBig, | |
| 5392 | EISCONN => return error.AlreadyConnected, | |
| 5393 | ENOPROTOOPT => return error.InvalidProtocolOption, | |
| 5394 | ENOMEM => return error.SystemResources, | |
| 5395 | ENOBUFS => return error.SystemResources, | |
| 5396 | else => |err| return unexpectedErrno(err), | |
| 5388 | pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { | |
| 5389 | if (builtin.os.tag == .windows) { | |
| 5390 | const rc = windows.ws2_32.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)); | |
| 5391 | if (rc == windows.ws2_32.SOCKET_ERROR) { | |
| 5392 | switch (windows.ws2_32.WSAGetLastError()) { | |
| 5393 | .WSANOTINITIALISED => unreachable, | |
| 5394 | .WSAENETDOWN => return error.NetworkSubsystemFailed, | |
| 5395 | .WSAEFAULT => unreachable, | |
| 5396 | .WSAENOTSOCK => return error.FileDescriptorNotASocket, | |
| 5397 | .WSAEINVAL => return error.SocketNotBound, | |
| 5398 | else => |err| return windows.unexpectedWSAError(err), | |
| 5399 | } | |
| 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 | 256 | pub const POLLHUP = ws2_32.POLLHUP; |
| 257 | 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 | 294 | pub const O_RDONLY = 0o0; |
| 260 | 295 | pub const O_WRONLY = 0o1; |
| 261 | 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 | 1279 | @bitCast(usize, @as(isize, pid)), |
| 1280 | 1280 | @bitCast(usize, @as(isize, @enumToInt(resource))), |
| 1281 | 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 | 594 | |
| 595 | 595 | test "getrlimit and setrlimit" { |
| 596 | 596 | // TODO enable for other systems when implemented |
| 597 | if(builtin.os.tag != .linux){ | |
| 597 | if (builtin.os.tag != .linux) { | |
| 598 | 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 | 718 | |
| 719 | 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 | 756 | pub extern "ws2_32" fn WSAStartup( |
| 722 | 757 | wVersionRequired: WORD, |
| 723 | 758 | lpWSAData: *WSADATA, |
| ... | ... | @@ -835,3 +870,10 @@ pub extern "ws2_32" fn getsockname( |
| 835 | 870 | name: *sockaddr, |
| 836 | 871 | namelen: *c_int, |
| 837 | 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; |