| author | |
| committer | |
| log | 77f8a9ae223370d43b4c02ff30f936b41e412535 |
| tree | 13ef1b903a008e12b8537c37b4aaad4fd5e77d61 |
| parent | 3d946ef5eb15d2333a5e376ab95dad2e70e0dfb9 |
Implement loading Winsock extensions.
Add missing Winsock extension GUID's.
Implement readVectorized() for POSIX sockets and
readVectorized() / writeVectorized() for Windows
sockets.
Inverse how mixins are used to implement platform-independent syscalls
for the std.x.os.Socket abstraction. This cleans up the API as suggested
by @komuw.5 files changed, 826 insertions(+), 681 deletions(-)
lib/std/os/windows.zig+32| ... | ... | @@ -1749,6 +1749,38 @@ fn MAKELANGID(p: c_ushort, s: c_ushort) callconv(.Inline) LANGID { |
| 1749 | 1749 | return (s << 10) | p; |
| 1750 | 1750 | } |
| 1751 | 1751 | |
| 1752 | /// Loads a Winsock extension function in runtime specified by a GUID. | |
| 1753 | pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: GUID) !T { | |
| 1754 | var function: T = undefined; | |
| 1755 | var num_bytes: DWORD = undefined; | |
| 1756 | ||
| 1757 | const rc = ws2_32.WSAIoctl( | |
| 1758 | sock, | |
| 1759 | ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER, | |
| 1760 | @ptrCast(*const c_void, &guid), | |
| 1761 | @sizeOf(GUID), | |
| 1762 | &function, | |
| 1763 | @sizeOf(T), | |
| 1764 | &num_bytes, | |
| 1765 | null, | |
| 1766 | null, | |
| 1767 | ); | |
| 1768 | ||
| 1769 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 1770 | return switch (ws2_32.WSAGetLastError()) { | |
| 1771 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 1772 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 1773 | else => |err| unexpectedWSAError(err), | |
| 1774 | }; | |
| 1775 | } | |
| 1776 | ||
| 1777 | if (num_bytes != @sizeOf(T)) { | |
| 1778 | return error.ShortRead; | |
| 1779 | } | |
| 1780 | ||
| 1781 | return function; | |
| 1782 | } | |
| 1783 | ||
| 1752 | 1784 | /// Call this when you made a windows DLL call or something that does SetLastError |
| 1753 | 1785 | /// and you get an unexpected error. |
| 1754 | 1786 | pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError { |
lib/std/os/windows/ws2_32.zig+50-5| ... | ... | @@ -266,10 +266,54 @@ pub const SENDER_DEFAULT_LATE_JOINER_PERCENTAGE = 0; |
| 266 | 266 | pub const SENDER_MAX_LATE_JOINER_PERCENTAGE = 75; |
| 267 | 267 | pub const BITS_PER_BYTE = 8; |
| 268 | 268 | pub const LOG2_BITS_PER_BYTE = 3; |
| 269 | ||
| 269 | 270 | pub const SOCKET_DEFAULT2_QM_POLICY = GUID.parse("{aec2ef9c-3a4d-4d3e-8842-239942e39a47}"); |
| 270 | 271 | pub const REAL_TIME_NOTIFICATION_CAPABILITY = GUID.parse("{6b59819a-5cae-492d-a901-2a3c2c50164f}"); |
| 271 | 272 | pub const REAL_TIME_NOTIFICATION_CAPABILITY_EX = GUID.parse("{6843da03-154a-4616-a508-44371295f96b}"); |
| 272 | 273 | pub const ASSOCIATE_NAMERES_CONTEXT = GUID.parse("{59a38b67-d4fe-46e1-ba3c-87ea74ca3049}"); |
| 274 | ||
| 275 | pub const WSAID_CONNECTEX = GUID{ | |
| 276 | .Data1 = 0x25a207b9, | |
| 277 | .Data2 = 0xddf3, | |
| 278 | .Data3 = 0x4660, | |
| 279 | .Data4 = [8]u8{ 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e }, | |
| 280 | }; | |
| 281 | ||
| 282 | pub const WSAID_ACCEPTEX = GUID{ | |
| 283 | .Data1 = 0xb5367df1, | |
| 284 | .Data2 = 0xcbac, | |
| 285 | .Data3 = 0x11cf, | |
| 286 | .Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 }, | |
| 287 | }; | |
| 288 | ||
| 289 | pub const WSAID_GETACCEPTEXSOCKADDRS = GUID{ | |
| 290 | .Data1 = 0xb5367df2, | |
| 291 | .Data2 = 0xcbac, | |
| 292 | .Data3 = 0x11cf, | |
| 293 | .Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 }, | |
| 294 | }; | |
| 295 | ||
| 296 | pub const WSAID_WSARECVMSG = GUID{ | |
| 297 | .Data1 = 0xf689d7c8, | |
| 298 | .Data2 = 0x6f1f, | |
| 299 | .Data3 = 0x436b, | |
| 300 | .Data4 = [8]u8{ 0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22 }, | |
| 301 | }; | |
| 302 | ||
| 303 | pub const WSAID_WSAPOLL = GUID{ | |
| 304 | .Data1 = 0x18C76F85, | |
| 305 | .Data2 = 0xDC66, | |
| 306 | .Data3 = 0x4964, | |
| 307 | .Data4 = [8]u8{ 0x97, 0x2E, 0x23, 0xC2, 0x72, 0x38, 0x31, 0x2B }, | |
| 308 | }; | |
| 309 | ||
| 310 | pub const WSAID_WSASENDMSG = GUID{ | |
| 311 | .Data1 = 0xa441e712, | |
| 312 | .Data2 = 0x754f, | |
| 313 | .Data3 = 0x43ca, | |
| 314 | .Data4 = [8]u8{ 0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d }, | |
| 315 | }; | |
| 316 | ||
| 273 | 317 | pub const TCP_INITIAL_RTO_DEFAULT_RTT = 0; |
| 274 | 318 | pub const TCP_INITIAL_RTO_DEFAULT_MAX_SYN_RETRANSMISSIONS = 0; |
| 275 | 319 | pub const SOCKET_SETTINGS_GUARANTEE_ENCRYPTION = 1; |
| ... | ... | @@ -485,6 +529,7 @@ pub const IOC_UNIX = 0; |
| 485 | 529 | pub const IOC_WS2 = 134217728; |
| 486 | 530 | pub const IOC_PROTOCOL = 268435456; |
| 487 | 531 | pub const IOC_VENDOR = 402653184; |
| 532 | pub const SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_OUT | IOC_IN | IOC_WS2 | 6; | |
| 488 | 533 | pub const SIO_BSP_HANDLE = IOC_OUT | IOC_WS2 | 27; |
| 489 | 534 | pub const SIO_BSP_HANDLE_SELECT = IOC_OUT | IOC_WS2 | 28; |
| 490 | 535 | pub const SIO_BSP_HANDLE_POLL = IOC_OUT | IOC_WS2 | 29; |
| ... | ... | @@ -1115,9 +1160,9 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn ( |
| 1115 | 1160 | RemoteSockaddrLength: *i32, |
| 1116 | 1161 | ) callconv(WINAPI) void; |
| 1117 | 1162 | |
| 1118 | pub const LFN_WSASENDMSG = fn ( | |
| 1163 | pub const LPFN_WSASENDMSG = fn ( | |
| 1119 | 1164 | s: SOCKET, |
| 1120 | lpMsg: *WSAMSG_const, | |
| 1165 | lpMsg: *const WSAMSG_const, | |
| 1121 | 1166 | dwFlags: u32, |
| 1122 | 1167 | lpNumberOfBytesSent: ?*u32, |
| 1123 | 1168 | lpOverlapped: ?*OVERLAPPED, |
| ... | ... | @@ -1927,7 +1972,7 @@ pub extern "ws2_32" fn WSAHtons( |
| 1927 | 1972 | pub extern "ws2_32" fn WSAIoctl( |
| 1928 | 1973 | s: SOCKET, |
| 1929 | 1974 | dwIoControlCode: u32, |
| 1930 | lpvInBuffer: ?*c_void, | |
| 1975 | lpvInBuffer: ?*const c_void, | |
| 1931 | 1976 | cbInBuffer: u32, |
| 1932 | 1977 | lpvOutbuffer: ?*c_void, |
| 1933 | 1978 | cbOutbuffer: u32, |
| ... | ... | @@ -1992,7 +2037,7 @@ pub extern "ws2_32" fn WSASend( |
| 1992 | 2037 | s: SOCKET, |
| 1993 | 2038 | lpBuffers: [*]WSABUF, |
| 1994 | 2039 | dwBufferCount: u32, |
| 1995 | lpNumberOfBytesSent: ?*U32, | |
| 2040 | lpNumberOfBytesSent: ?*u32, | |
| 1996 | 2041 | dwFlags: u32, |
| 1997 | 2042 | lpOverlapped: ?*OVERLAPPED, |
| 1998 | 2043 | lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE, |
| ... | ... | @@ -2000,7 +2045,7 @@ pub extern "ws2_32" fn WSASend( |
| 2000 | 2045 | |
| 2001 | 2046 | pub extern "ws2_32" fn WSASendMsg( |
| 2002 | 2047 | s: SOCKET, |
| 2003 | lpMsg: *WSAMSG_const, | |
| 2048 | lpMsg: *const WSAMSG_const, | |
| 2004 | 2049 | dwFlags: u32, |
| 2005 | 2050 | lpNumberOfBytesSent: ?*u32, |
| 2006 | 2051 | lpOverlapped: ?*OVERLAPPED, |
lib/std/x/os/socket.zig+95-89| ... | ... | @@ -13,105 +13,111 @@ const mem = std.mem; |
| 13 | 13 | const time = std.time; |
| 14 | 14 | const builtin = std.builtin; |
| 15 | 15 | |
| 16 | /// Import in a `Socket` abstraction depending on the platform we are compiling against. | |
| 17 | pub usingnamespace switch (builtin.os.tag) { | |
| 18 | .windows => @import("socket_windows.zig"), | |
| 19 | else => @import("socket_posix.zig"), | |
| 20 | }; | |
| 16 | /// A generic, cross-platform socket abstraction. | |
| 17 | pub const Socket = struct { | |
| 18 | /// A socket-address pair. | |
| 19 | pub const Connection = struct { | |
| 20 | socket: Socket, | |
| 21 | address: Socket.Address, | |
| 21 | 22 | |
| 22 | /// A common subset of shared structs across cross-platform abstractions over socket syscalls. | |
| 23 | pub fn Mixin(comptime Self: type) type { | |
| 24 | return struct { | |
| 25 | /// A socket-address pair. | |
| 26 | pub const Connection = struct { | |
| 27 | socket: Self, | |
| 28 | address: Self.Address, | |
| 23 | /// Enclose a socket and address into a socket-address pair. | |
| 24 | pub fn from(socket: Socket, address: Socket.Address) Socket.Connection { | |
| 25 | return .{ .socket = socket, .address = address }; | |
| 26 | } | |
| 27 | }; | |
| 29 | 28 | |
| 30 | /// Enclose a socket and address into a socket-address pair. | |
| 31 | pub fn from(socket: Self, address: Self.Address) Self.Connection { | |
| 32 | return .{ .socket = socket, .address = address }; | |
| 33 | } | |
| 34 | }; | |
| 29 | /// A generic socket address abstraction. It is safe to directly access and modify | |
| 30 | /// the fields of a `Socket.Address`. | |
| 31 | pub const Address = union(enum) { | |
| 32 | ipv4: net.IPv4.Address, | |
| 33 | ipv6: net.IPv6.Address, | |
| 35 | 34 | |
| 36 | /// A generic socket address abstraction. It is safe to directly access and modify | |
| 37 | /// the fields of a `Self.Address`. | |
| 38 | pub const Address = union(enum) { | |
| 39 | ipv4: net.IPv4.Address, | |
| 40 | ipv6: net.IPv6.Address, | |
| 35 | /// Instantiate a new address with a IPv4 host and port. | |
| 36 | pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address { | |
| 37 | return .{ .ipv4 = .{ .host = host, .port = port } }; | |
| 38 | } | |
| 41 | 39 | |
| 42 | /// Instantiate a new address with a IPv4 host and port. | |
| 43 | pub fn initIPv4(host: net.IPv4, port: u16) Self.Address { | |
| 44 | return .{ .ipv4 = .{ .host = host, .port = port } }; | |
| 45 | } | |
| 40 | /// Instantiate a new address with a IPv6 host and port. | |
| 41 | pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address { | |
| 42 | return .{ .ipv6 = .{ .host = host, .port = port } }; | |
| 43 | } | |
| 46 | 44 | |
| 47 | /// Instantiate a new address with a IPv6 host and port. | |
| 48 | pub fn initIPv6(host: net.IPv6, port: u16) Self.Address { | |
| 49 | return .{ .ipv6 = .{ .host = host, .port = port } }; | |
| 50 | } | |
| 51 | ||
| 52 | /// Parses a `sockaddr` into a generic socket address. | |
| 53 | pub fn fromNative(address: *align(4) const os.sockaddr) Self.Address { | |
| 54 | switch (address.family) { | |
| 55 | os.AF_INET => { | |
| 56 | const info = @ptrCast(*const os.sockaddr_in, address); | |
| 57 | const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) }; | |
| 58 | const port = mem.bigToNative(u16, info.port); | |
| 59 | return Self.Address.initIPv4(host, port); | |
| 60 | }, | |
| 61 | os.AF_INET6 => { | |
| 62 | const info = @ptrCast(*const os.sockaddr_in6, address); | |
| 63 | const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id }; | |
| 64 | const port = mem.bigToNative(u16, info.port); | |
| 65 | return Self.Address.initIPv6(host, port); | |
| 66 | }, | |
| 67 | else => unreachable, | |
| 68 | } | |
| 45 | /// Parses a `sockaddr` into a generic socket address. | |
| 46 | pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address { | |
| 47 | switch (address.family) { | |
| 48 | os.AF_INET => { | |
| 49 | const info = @ptrCast(*const os.sockaddr_in, address); | |
| 50 | const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) }; | |
| 51 | const port = mem.bigToNative(u16, info.port); | |
| 52 | return Socket.Address.initIPv4(host, port); | |
| 53 | }, | |
| 54 | os.AF_INET6 => { | |
| 55 | const info = @ptrCast(*const os.sockaddr_in6, address); | |
| 56 | const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id }; | |
| 57 | const port = mem.bigToNative(u16, info.port); | |
| 58 | return Socket.Address.initIPv6(host, port); | |
| 59 | }, | |
| 60 | else => unreachable, | |
| 69 | 61 | } |
| 62 | } | |
| 70 | 63 | |
| 71 | /// Encodes a generic socket address into an extern union that may be reliably | |
| 72 | /// casted into a `sockaddr` which may be passed into socket syscalls. | |
| 73 | pub fn toNative(self: Self.Address) extern union { | |
| 74 | ipv4: os.sockaddr_in, | |
| 75 | ipv6: os.sockaddr_in6, | |
| 76 | } { | |
| 77 | return switch (self) { | |
| 78 | .ipv4 => |address| .{ | |
| 79 | .ipv4 = .{ | |
| 80 | .addr = @bitCast(u32, address.host.octets), | |
| 81 | .port = mem.nativeToBig(u16, address.port), | |
| 82 | }, | |
| 64 | /// Encodes a generic socket address into an extern union that may be reliably | |
| 65 | /// casted into a `sockaddr` which may be passed into socket syscalls. | |
| 66 | pub fn toNative(self: Socket.Address) extern union { | |
| 67 | ipv4: os.sockaddr_in, | |
| 68 | ipv6: os.sockaddr_in6, | |
| 69 | } { | |
| 70 | return switch (self) { | |
| 71 | .ipv4 => |address| .{ | |
| 72 | .ipv4 = .{ | |
| 73 | .addr = @bitCast(u32, address.host.octets), | |
| 74 | .port = mem.nativeToBig(u16, address.port), | |
| 83 | 75 | }, |
| 84 | .ipv6 => |address| .{ | |
| 85 | .ipv6 = .{ | |
| 86 | .addr = address.host.octets, | |
| 87 | .port = mem.nativeToBig(u16, address.port), | |
| 88 | .scope_id = address.host.scope_id, | |
| 89 | .flowinfo = 0, | |
| 90 | }, | |
| 76 | }, | |
| 77 | .ipv6 => |address| .{ | |
| 78 | .ipv6 = .{ | |
| 79 | .addr = address.host.octets, | |
| 80 | .port = mem.nativeToBig(u16, address.port), | |
| 81 | .scope_id = address.host.scope_id, | |
| 82 | .flowinfo = 0, | |
| 91 | 83 | }, |
| 92 | }; | |
| 93 | } | |
| 84 | }, | |
| 85 | }; | |
| 86 | } | |
| 94 | 87 | |
| 95 | /// Returns the number of bytes that make up the `sockaddr` equivalent to the address. | |
| 96 | pub fn getNativeSize(self: Self.Address) u32 { | |
| 97 | return switch (self) { | |
| 98 | .ipv4 => @sizeOf(os.sockaddr_in), | |
| 99 | .ipv6 => @sizeOf(os.sockaddr_in6), | |
| 100 | }; | |
| 101 | } | |
| 88 | /// Returns the number of bytes that make up the `sockaddr` equivalent to the address. | |
| 89 | pub fn getNativeSize(self: Socket.Address) u32 { | |
| 90 | return switch (self) { | |
| 91 | .ipv4 => @sizeOf(os.sockaddr_in), | |
| 92 | .ipv6 => @sizeOf(os.sockaddr_in6), | |
| 93 | }; | |
| 94 | } | |
| 102 | 95 | |
| 103 | /// Implements the `std.fmt.format` API. | |
| 104 | pub fn format( | |
| 105 | self: Self.Address, | |
| 106 | comptime layout: []const u8, | |
| 107 | opts: fmt.FormatOptions, | |
| 108 | writer: anytype, | |
| 109 | ) !void { | |
| 110 | switch (self) { | |
| 111 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 112 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 113 | } | |
| 96 | /// Implements the `std.fmt.format` API. | |
| 97 | pub fn format( | |
| 98 | self: Socket.Address, | |
| 99 | comptime layout: []const u8, | |
| 100 | opts: fmt.FormatOptions, | |
| 101 | writer: anytype, | |
| 102 | ) !void { | |
| 103 | switch (self) { | |
| 104 | .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 105 | .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }), | |
| 114 | 106 | } |
| 115 | }; | |
| 107 | } | |
| 116 | 108 | }; |
| 117 | } | |
| 109 | ||
| 110 | /// The underlying handle of a socket. | |
| 111 | fd: os.socket_t, | |
| 112 | ||
| 113 | /// Enclose a socket abstraction over an existing socket file descriptor. | |
| 114 | pub fn from(fd: os.socket_t) Socket { | |
| 115 | return Socket{ .fd = fd }; | |
| 116 | } | |
| 117 | ||
| 118 | /// Mix in socket syscalls depending on the platform we are compiling against. | |
| 119 | pub usingnamespace switch (builtin.os.tag) { | |
| 120 | .windows => @import("socket_windows.zig"), | |
| 121 | else => @import("socket_posix.zig"), | |
| 122 | }.Mixin(Socket); | |
| 123 | }; |
lib/std/x/os/socket_posix.zig+237-227| ... | ... | @@ -10,232 +10,242 @@ const os = std.os; |
| 10 | 10 | const mem = std.mem; |
| 11 | 11 | const time = std.time; |
| 12 | 12 | |
| 13 | pub const Socket = struct { | |
| 14 | /// Import in `Socket.Address` and `Socket.Connection`. | |
| 15 | pub usingnamespace @import("socket.zig").Mixin(Socket); | |
| 16 | ||
| 17 | /// The underlying handle of a socket. | |
| 18 | fd: os.socket_t, | |
| 19 | ||
| 20 | /// Open a new socket. | |
| 21 | pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { | |
| 22 | return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; | |
| 23 | } | |
| 24 | ||
| 25 | /// Enclose a socket abstraction over an existing socket file descriptor. | |
| 26 | pub fn from(fd: os.socket_t) Socket { | |
| 27 | return Socket{ .fd = fd }; | |
| 28 | } | |
| 29 | ||
| 30 | /// Closes the socket. | |
| 31 | pub fn deinit(self: Socket) void { | |
| 32 | os.closeSocket(self.fd); | |
| 33 | } | |
| 34 | ||
| 35 | /// Shutdown either the read side, write side, or all side of the socket. | |
| 36 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | |
| 37 | return os.shutdown(self.fd, how); | |
| 38 | } | |
| 39 | ||
| 40 | /// Binds the socket to an address. | |
| 41 | pub fn bind(self: Socket, address: Socket.Address) !void { | |
| 42 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 43 | } | |
| 44 | ||
| 45 | /// Start listening for incoming connections on the socket. | |
| 46 | pub fn listen(self: Socket, max_backlog_size: u31) !void { | |
| 47 | return os.listen(self.fd, max_backlog_size); | |
| 48 | } | |
| 49 | ||
| 50 | /// Have the socket attempt to the connect to an address. | |
| 51 | pub fn connect(self: Socket, address: Socket.Address) !void { | |
| 52 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 53 | } | |
| 54 | ||
| 55 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 56 | /// of the socket. | |
| 57 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { | |
| 58 | var address: os.sockaddr_storage = undefined; | |
| 59 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 60 | ||
| 61 | const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) }; | |
| 62 | const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 63 | ||
| 64 | return Socket.Connection.from(socket, socket_address); | |
| 65 | } | |
| 66 | ||
| 67 | /// Read data from the socket into the buffer provided with a set of flags | |
| 68 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 69 | pub fn read(self: Socket, buf: []u8, flags: u32) !usize { | |
| 70 | return os.recv(self.fd, buf, flags); | |
| 71 | } | |
| 72 | ||
| 73 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 74 | /// It returns the number of bytes that are written to the socket. | |
| 75 | pub fn write(self: Socket, buf: []const u8, flags: u32) !usize { | |
| 76 | return os.send(self.fd, buf, flags); | |
| 77 | } | |
| 78 | ||
| 79 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 80 | /// with a set of flags specified. It returns the number of bytes that are | |
| 81 | /// written to the socket. | |
| 82 | pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize { | |
| 83 | return os.sendmsg(self.fd, msg, flags); | |
| 84 | } | |
| 85 | ||
| 86 | /// Read multiple I/O vectors with a prepended message header from the socket | |
| 87 | /// with a set of flags specified. It returns the number of bytes that were | |
| 88 | /// read into the buffer provided. | |
| 89 | pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize { | |
| 90 | return error.NotImplemented; | |
| 91 | } | |
| 92 | ||
| 93 | /// Query the address that the socket is locally bounded to. | |
| 94 | pub fn getLocalAddress(self: Socket) !Socket.Address { | |
| 95 | var address: os.sockaddr_storage = undefined; | |
| 96 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 97 | try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | |
| 98 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 99 | } | |
| 100 | ||
| 101 | /// Query the address that the socket is connected to. | |
| 102 | pub fn getRemoteAddress(self: Socket) !Socket.Address { | |
| 103 | var address: os.sockaddr_storage = undefined; | |
| 104 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 105 | try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | |
| 106 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 107 | } | |
| 108 | ||
| 109 | /// Query and return the latest cached error on the socket. | |
| 110 | pub fn getError(self: Socket) !void { | |
| 111 | return os.getsockoptError(self.fd); | |
| 112 | } | |
| 113 | ||
| 114 | /// Query the read buffer size of the socket. | |
| 115 | pub fn getReadBufferSize(self: Socket) !u32 { | |
| 116 | var value: u32 = undefined; | |
| 117 | var value_len: u32 = @sizeOf(u32); | |
| 118 | ||
| 119 | const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len); | |
| 120 | return switch (os.errno(rc)) { | |
| 121 | 0 => value, | |
| 122 | os.EBADF => error.BadFileDescriptor, | |
| 123 | os.EFAULT => error.InvalidAddressSpace, | |
| 124 | os.EINVAL => error.InvalidSocketOption, | |
| 125 | os.ENOPROTOOPT => error.UnknownSocketOption, | |
| 126 | os.ENOTSOCK => error.NotASocket, | |
| 127 | else => |err| os.unexpectedErrno(err), | |
| 128 | }; | |
| 129 | } | |
| 130 | ||
| 131 | /// Query the write buffer size of the socket. | |
| 132 | pub fn getWriteBufferSize(self: Socket) !u32 { | |
| 133 | var value: u32 = undefined; | |
| 134 | var value_len: u32 = @sizeOf(u32); | |
| 135 | ||
| 136 | const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len); | |
| 137 | return switch (os.errno(rc)) { | |
| 138 | 0 => value, | |
| 139 | os.EBADF => error.BadFileDescriptor, | |
| 140 | os.EFAULT => error.InvalidAddressSpace, | |
| 141 | os.EINVAL => error.InvalidSocketOption, | |
| 142 | os.ENOPROTOOPT => error.UnknownSocketOption, | |
| 143 | os.ENOTSOCK => error.NotASocket, | |
| 144 | else => |err| os.unexpectedErrno(err), | |
| 145 | }; | |
| 146 | } | |
| 147 | ||
| 148 | /// Set a socket option. | |
| 149 | pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void { | |
| 150 | return os.setsockopt(self.fd, level, code, value); | |
| 151 | } | |
| 152 | ||
| 153 | /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully | |
| 154 | /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption` | |
| 155 | /// if the host does not support the option for a socket to linger around up until a timeout specified in | |
| 156 | /// seconds. | |
| 157 | pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { | |
| 158 | if (comptime @hasDecl(os, "SO_LINGER")) { | |
| 159 | const settings = extern struct { | |
| 160 | l_onoff: c_int, | |
| 161 | l_linger: c_int, | |
| 162 | }{ | |
| 163 | .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)), | |
| 164 | .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0, | |
| 13 | pub fn Mixin(comptime Socket: type) type { | |
| 14 | return struct { | |
| 15 | /// Open a new socket. | |
| 16 | pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { | |
| 17 | return Socket{ .fd = try os.socket(domain, socket_type, protocol) }; | |
| 18 | } | |
| 19 | ||
| 20 | /// Closes the socket. | |
| 21 | pub fn deinit(self: Socket) void { | |
| 22 | os.closeSocket(self.fd); | |
| 23 | } | |
| 24 | ||
| 25 | /// Shutdown either the read side, write side, or all side of the socket. | |
| 26 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | |
| 27 | return os.shutdown(self.fd, how); | |
| 28 | } | |
| 29 | ||
| 30 | /// Binds the socket to an address. | |
| 31 | pub fn bind(self: Socket, address: Socket.Address) !void { | |
| 32 | return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 33 | } | |
| 34 | ||
| 35 | /// Start listening for incoming connections on the socket. | |
| 36 | pub fn listen(self: Socket, max_backlog_size: u31) !void { | |
| 37 | return os.listen(self.fd, max_backlog_size); | |
| 38 | } | |
| 39 | ||
| 40 | /// Have the socket attempt to the connect to an address. | |
| 41 | pub fn connect(self: Socket, address: Socket.Address) !void { | |
| 42 | return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize()); | |
| 43 | } | |
| 44 | ||
| 45 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 46 | /// of the socket. | |
| 47 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { | |
| 48 | var address: os.sockaddr_storage = undefined; | |
| 49 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 50 | ||
| 51 | const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) }; | |
| 52 | const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 53 | ||
| 54 | return Socket.Connection.from(socket, socket_address); | |
| 55 | } | |
| 56 | ||
| 57 | /// Read data from the socket into the buffer provided with a set of flags | |
| 58 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 59 | pub fn read(self: Socket, buf: []u8, flags: u32) !usize { | |
| 60 | return os.recv(self.fd, buf, flags); | |
| 61 | } | |
| 62 | ||
| 63 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 64 | /// It returns the number of bytes that are written to the socket. | |
| 65 | pub fn write(self: Socket, buf: []const u8, flags: u32) !usize { | |
| 66 | return os.send(self.fd, buf, flags); | |
| 67 | } | |
| 68 | ||
| 69 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 70 | /// with a set of flags specified. It returns the number of bytes that are | |
| 71 | /// written to the socket. | |
| 72 | pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize { | |
| 73 | return os.sendmsg(self.fd, msg, flags); | |
| 74 | } | |
| 75 | ||
| 76 | /// Read multiple I/O vectors with a prepended message header from the socket | |
| 77 | /// with a set of flags specified. It returns the number of bytes that were | |
| 78 | /// read into the buffer provided. | |
| 79 | pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize { | |
| 80 | if (comptime @hasDecl(os.system, "recvmsg")) { | |
| 81 | while (true) { | |
| 82 | const rc = os.system.recvmsg(self.fd, msg, flags); | |
| 83 | return switch (os.errno(rc)) { | |
| 84 | 0 => @intCast(usize, rc), | |
| 85 | os.EBADF => unreachable, // always a race condition | |
| 86 | os.EFAULT => unreachable, | |
| 87 | os.EINVAL => unreachable, | |
| 88 | os.ENOTCONN => unreachable, | |
| 89 | os.ENOTSOCK => unreachable, | |
| 90 | os.EINTR => continue, | |
| 91 | os.EAGAIN => error.WouldBlock, | |
| 92 | os.ENOMEM => error.SystemResources, | |
| 93 | os.ECONNREFUSED => error.ConnectionRefused, | |
| 94 | os.ECONNRESET => error.ConnectionResetByPeer, | |
| 95 | else => |err| os.unexpectedErrno(err), | |
| 96 | }; | |
| 97 | } | |
| 98 | } | |
| 99 | return error.NotSupported; | |
| 100 | } | |
| 101 | ||
| 102 | /// Query the address that the socket is locally bounded to. | |
| 103 | pub fn getLocalAddress(self: Socket) !Socket.Address { | |
| 104 | var address: os.sockaddr_storage = undefined; | |
| 105 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 106 | try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | |
| 107 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 108 | } | |
| 109 | ||
| 110 | /// Query the address that the socket is connected to. | |
| 111 | pub fn getRemoteAddress(self: Socket) !Socket.Address { | |
| 112 | var address: os.sockaddr_storage = undefined; | |
| 113 | var address_len: u32 = @sizeOf(os.sockaddr_storage); | |
| 114 | try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len); | |
| 115 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 116 | } | |
| 117 | ||
| 118 | /// Query and return the latest cached error on the socket. | |
| 119 | pub fn getError(self: Socket) !void { | |
| 120 | return os.getsockoptError(self.fd); | |
| 121 | } | |
| 122 | ||
| 123 | /// Query the read buffer size of the socket. | |
| 124 | pub fn getReadBufferSize(self: Socket) !u32 { | |
| 125 | var value: u32 = undefined; | |
| 126 | var value_len: u32 = @sizeOf(u32); | |
| 127 | ||
| 128 | const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len); | |
| 129 | return switch (os.errno(rc)) { | |
| 130 | 0 => value, | |
| 131 | os.EBADF => error.BadFileDescriptor, | |
| 132 | os.EFAULT => error.InvalidAddressSpace, | |
| 133 | os.EINVAL => error.InvalidSocketOption, | |
| 134 | os.ENOPROTOOPT => error.UnknownSocketOption, | |
| 135 | os.ENOTSOCK => error.NotASocket, | |
| 136 | else => |err| os.unexpectedErrno(err), | |
| 165 | 137 | }; |
| 138 | } | |
| 139 | ||
| 140 | /// Query the write buffer size of the socket. | |
| 141 | pub fn getWriteBufferSize(self: Socket) !u32 { | |
| 142 | var value: u32 = undefined; | |
| 143 | var value_len: u32 = @sizeOf(u32); | |
| 144 | ||
| 145 | const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len); | |
| 146 | return switch (os.errno(rc)) { | |
| 147 | 0 => value, | |
| 148 | os.EBADF => error.BadFileDescriptor, | |
| 149 | os.EFAULT => error.InvalidAddressSpace, | |
| 150 | os.EINVAL => error.InvalidSocketOption, | |
| 151 | os.ENOPROTOOPT => error.UnknownSocketOption, | |
| 152 | os.ENOTSOCK => error.NotASocket, | |
| 153 | else => |err| os.unexpectedErrno(err), | |
| 154 | }; | |
| 155 | } | |
| 156 | ||
| 157 | /// Set a socket option. | |
| 158 | pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void { | |
| 159 | return os.setsockopt(self.fd, level, code, value); | |
| 160 | } | |
| 161 | ||
| 162 | /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully | |
| 163 | /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption` | |
| 164 | /// if the host does not support the option for a socket to linger around up until a timeout specified in | |
| 165 | /// seconds. | |
| 166 | pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { | |
| 167 | if (comptime @hasDecl(os, "SO_LINGER")) { | |
| 168 | const settings = extern struct { | |
| 169 | l_onoff: c_int, | |
| 170 | l_linger: c_int, | |
| 171 | }{ | |
| 172 | .l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)), | |
| 173 | .l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0, | |
| 174 | }; | |
| 175 | ||
| 176 | return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings)); | |
| 177 | } | |
| 178 | ||
| 179 | return error.UnsupportedSocketOption; | |
| 180 | } | |
| 181 | ||
| 182 | /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive | |
| 183 | /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if | |
| 184 | /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. | |
| 185 | pub fn setKeepAlive(self: Socket, enabled: bool) !void { | |
| 186 | if (comptime @hasDecl(os, "SO_KEEPALIVE")) { | |
| 187 | return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 188 | } | |
| 189 | return error.UnsupportedSocketOption; | |
| 190 | } | |
| 191 | ||
| 192 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 193 | /// the host does not support sockets listening the same address. | |
| 194 | pub fn setReuseAddress(self: Socket, enabled: bool) !void { | |
| 195 | if (comptime @hasDecl(os, "SO_REUSEADDR")) { | |
| 196 | return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 197 | } | |
| 198 | return error.UnsupportedSocketOption; | |
| 199 | } | |
| 166 | 200 | |
| 167 | return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings)); | |
| 168 | } | |
| 169 | ||
| 170 | return error.UnsupportedSocketOption; | |
| 171 | } | |
| 172 | ||
| 173 | /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive | |
| 174 | /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if | |
| 175 | /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. | |
| 176 | pub fn setKeepAlive(self: Socket, enabled: bool) !void { | |
| 177 | if (comptime @hasDecl(os, "SO_KEEPALIVE")) { | |
| 178 | return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 179 | } | |
| 180 | return error.UnsupportedSocketOption; | |
| 181 | } | |
| 182 | ||
| 183 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 184 | /// the host does not support sockets listening the same address. | |
| 185 | pub fn setReuseAddress(self: Socket, enabled: bool) !void { | |
| 186 | if (comptime @hasDecl(os, "SO_REUSEADDR")) { | |
| 187 | return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 188 | } | |
| 189 | return error.UnsupportedSocketOption; | |
| 190 | } | |
| 191 | ||
| 192 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 193 | /// the host does not supports sockets listening on the same port. | |
| 194 | pub fn setReusePort(self: Socket, enabled: bool) !void { | |
| 195 | if (comptime @hasDecl(os, "SO_REUSEPORT")) { | |
| 196 | return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 197 | } | |
| 198 | return error.UnsupportedSocketOption; | |
| 199 | } | |
| 200 | ||
| 201 | /// Set the write buffer size of the socket. | |
| 202 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { | |
| 203 | return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size)); | |
| 204 | } | |
| 205 | ||
| 206 | /// Set the read buffer size of the socket. | |
| 207 | pub fn setReadBufferSize(self: Socket, size: u32) !void { | |
| 208 | return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size)); | |
| 209 | } | |
| 210 | ||
| 211 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 212 | /// set on a non-blocking socket. | |
| 213 | /// | |
| 214 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 215 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 216 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 217 | pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { | |
| 218 | const timeout = os.timeval{ | |
| 219 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), | |
| 220 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), | |
| 221 | }; | |
| 222 | ||
| 223 | return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout)); | |
| 224 | } | |
| 225 | ||
| 226 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 227 | /// set on a non-blocking socket. | |
| 228 | /// | |
| 229 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 230 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 231 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 232 | /// exceeded. | |
| 233 | pub fn setReadTimeout(self: Socket, milliseconds: usize) !void { | |
| 234 | const timeout = os.timeval{ | |
| 235 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), | |
| 236 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), | |
| 237 | }; | |
| 238 | ||
| 239 | return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout)); | |
| 240 | } | |
| 241 | }; | |
| 201 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 202 | /// the host does not supports sockets listening on the same port. | |
| 203 | pub fn setReusePort(self: Socket, enabled: bool) !void { | |
| 204 | if (comptime @hasDecl(os, "SO_REUSEPORT")) { | |
| 205 | return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 206 | } | |
| 207 | return error.UnsupportedSocketOption; | |
| 208 | } | |
| 209 | ||
| 210 | /// Set the write buffer size of the socket. | |
| 211 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { | |
| 212 | return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size)); | |
| 213 | } | |
| 214 | ||
| 215 | /// Set the read buffer size of the socket. | |
| 216 | pub fn setReadBufferSize(self: Socket, size: u32) !void { | |
| 217 | return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size)); | |
| 218 | } | |
| 219 | ||
| 220 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 221 | /// set on a non-blocking socket. | |
| 222 | /// | |
| 223 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 224 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 225 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 226 | pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void { | |
| 227 | const timeout = os.timeval{ | |
| 228 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), | |
| 229 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), | |
| 230 | }; | |
| 231 | ||
| 232 | return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout)); | |
| 233 | } | |
| 234 | ||
| 235 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 236 | /// set on a non-blocking socket. | |
| 237 | /// | |
| 238 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 239 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 240 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 241 | /// exceeded. | |
| 242 | pub fn setReadTimeout(self: Socket, milliseconds: usize) !void { | |
| 243 | const timeout = os.timeval{ | |
| 244 | .tv_sec = @intCast(i32, milliseconds / time.ms_per_s), | |
| 245 | .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms), | |
| 246 | }; | |
| 247 | ||
| 248 | return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout)); | |
| 249 | } | |
| 250 | }; | |
| 251 | } |
lib/std/x/os/socket_windows.zig+412-360| ... | ... | @@ -13,386 +13,438 @@ const mem = std.mem; |
| 13 | 13 | const windows = std.os.windows; |
| 14 | 14 | const ws2_32 = windows.ws2_32; |
| 15 | 15 | |
| 16 | pub const Socket = struct { | |
| 17 | /// Import in `Socket.Address` and `Socket.Connection`. | |
| 18 | pub usingnamespace @import("socket.zig").Mixin(Socket); | |
| 16 | pub fn Mixin(comptime Socket: type) type { | |
| 17 | return struct { | |
| 18 | /// Open a new socket. | |
| 19 | pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { | |
| 20 | var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC); | |
| 21 | ||
| 22 | var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED; | |
| 23 | if (socket_type & os.SOCK_CLOEXEC != 0) { | |
| 24 | filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; | |
| 25 | } | |
| 26 | ||
| 27 | const fd = ws2_32.WSASocketW( | |
| 28 | @intCast(i32, domain), | |
| 29 | @intCast(i32, filtered_socket_type), | |
| 30 | @intCast(i32, protocol), | |
| 31 | null, | |
| 32 | 0, | |
| 33 | filtered_flags, | |
| 34 | ); | |
| 35 | if (fd == ws2_32.INVALID_SOCKET) { | |
| 36 | return switch (ws2_32.WSAGetLastError()) { | |
| 37 | .WSANOTINITIALISED => { | |
| 38 | _ = try windows.WSAStartup(2, 2); | |
| 39 | return Socket.init(domain, socket_type, protocol); | |
| 40 | }, | |
| 41 | .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported, | |
| 42 | .WSAEMFILE => error.ProcessFdQuotaExceeded, | |
| 43 | .WSAENOBUFS => error.SystemResources, | |
| 44 | .WSAEPROTONOSUPPORT => error.ProtocolNotSupported, | |
| 45 | else => |err| windows.unexpectedWSAError(err), | |
| 46 | }; | |
| 47 | } | |
| 48 | ||
| 49 | return Socket{ .fd = fd }; | |
| 50 | } | |
| 19 | 51 | |
| 20 | /// The underlying handle of a socket. | |
| 21 | fd: os.socket_t, | |
| 52 | /// Closes the socket. | |
| 53 | pub fn deinit(self: Socket) void { | |
| 54 | _ = ws2_32.closesocket(self.fd); | |
| 55 | } | |
| 22 | 56 | |
| 23 | /// Open a new socket. | |
| 24 | pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket { | |
| 25 | var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC); | |
| 57 | /// Shutdown either the read side, write side, or all side of the socket. | |
| 58 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | |
| 59 | const rc = ws2_32.shutdown(self.fd, switch (how) { | |
| 60 | .recv => ws2_32.SD_RECEIVE, | |
| 61 | .send => ws2_32.SD_SEND, | |
| 62 | .both => ws2_32.SD_BOTH, | |
| 63 | }); | |
| 64 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 65 | return switch (ws2_32.WSAGetLastError()) { | |
| 66 | .WSAECONNABORTED => return error.ConnectionAborted, | |
| 67 | .WSAECONNRESET => return error.ConnectionResetByPeer, | |
| 68 | .WSAEINPROGRESS => return error.BlockingOperationInProgress, | |
| 69 | .WSAEINVAL => unreachable, | |
| 70 | .WSAENETDOWN => return error.NetworkSubsystemFailed, | |
| 71 | .WSAENOTCONN => return error.SocketNotConnected, | |
| 72 | .WSAENOTSOCK => unreachable, | |
| 73 | .WSANOTINITIALISED => unreachable, | |
| 74 | else => |err| return windows.unexpectedWSAError(err), | |
| 75 | }; | |
| 76 | } | |
| 77 | } | |
| 26 | 78 | |
| 27 | var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED; | |
| 28 | if (socket_type & os.SOCK_CLOEXEC != 0) { | |
| 29 | filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; | |
| 79 | /// Binds the socket to an address. | |
| 80 | pub fn bind(self: Socket, address: Socket.Address) !void { | |
| 81 | const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize())); | |
| 82 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 83 | return switch (ws2_32.WSAGetLastError()) { | |
| 84 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 85 | .WSAEACCES => error.AccessDenied, | |
| 86 | .WSAEADDRINUSE => error.AddressInUse, | |
| 87 | .WSAEADDRNOTAVAIL => error.AddressNotAvailable, | |
| 88 | .WSAEFAULT => error.BadAddress, | |
| 89 | .WSAEINPROGRESS => error.WouldBlock, | |
| 90 | .WSAEINVAL => error.AlreadyBound, | |
| 91 | .WSAENOBUFS => error.NoEphemeralPortsAvailable, | |
| 92 | .WSAENOTSOCK => error.NotASocket, | |
| 93 | else => |err| windows.unexpectedWSAError(err), | |
| 94 | }; | |
| 95 | } | |
| 30 | 96 | } |
| 31 | 97 | |
| 32 | const fd = ws2_32.WSASocketW( | |
| 33 | @intCast(i32, domain), | |
| 34 | @intCast(i32, filtered_socket_type), | |
| 35 | @intCast(i32, protocol), | |
| 36 | null, | |
| 37 | 0, | |
| 38 | filtered_flags, | |
| 39 | ); | |
| 40 | if (fd == ws2_32.INVALID_SOCKET) { | |
| 41 | return switch (ws2_32.WSAGetLastError()) { | |
| 42 | .WSANOTINITIALISED => { | |
| 43 | _ = try windows.WSAStartup(2, 2); | |
| 44 | return Socket.init(domain, socket_type, protocol); | |
| 45 | }, | |
| 46 | .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported, | |
| 47 | .WSAEMFILE => error.ProcessFdQuotaExceeded, | |
| 48 | .WSAENOBUFS => error.SystemResources, | |
| 49 | .WSAEPROTONOSUPPORT => error.ProtocolNotSupported, | |
| 50 | else => |err| windows.unexpectedWSAError(err), | |
| 51 | }; | |
| 98 | /// Start listening for incoming connections on the socket. | |
| 99 | pub fn listen(self: Socket, max_backlog_size: u31) !void { | |
| 100 | const rc = ws2_32.listen(self.fd, max_backlog_size); | |
| 101 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 102 | return switch (ws2_32.WSAGetLastError()) { | |
| 103 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 104 | .WSAEADDRINUSE => error.AddressInUse, | |
| 105 | .WSAEISCONN => error.AlreadyConnected, | |
| 106 | .WSAEINVAL => error.SocketNotBound, | |
| 107 | .WSAEMFILE, .WSAENOBUFS => error.SystemResources, | |
| 108 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 109 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 110 | .WSAEINPROGRESS => error.WouldBlock, | |
| 111 | else => |err| windows.unexpectedWSAError(err), | |
| 112 | }; | |
| 113 | } | |
| 52 | 114 | } |
| 53 | 115 | |
| 54 | return Socket{ .fd = fd }; | |
| 55 | } | |
| 56 | ||
| 57 | /// Enclose a socket abstraction over an existing socket file descriptor. | |
| 58 | pub fn from(fd: os.socket_t) Socket { | |
| 59 | return Socket{ .fd = fd }; | |
| 60 | } | |
| 61 | ||
| 62 | /// Closes the socket. | |
| 63 | pub fn deinit(self: Socket) void { | |
| 64 | _ = ws2_32.closesocket(self.fd); | |
| 65 | } | |
| 66 | ||
| 67 | /// Shutdown either the read side, write side, or all side of the socket. | |
| 68 | pub fn shutdown(self: Socket, how: os.ShutdownHow) !void { | |
| 69 | const rc = ws2_32.shutdown(self.fd, switch (how) { | |
| 70 | .recv => ws2_32.SD_RECEIVE, | |
| 71 | .send => ws2_32.SD_SEND, | |
| 72 | .both => ws2_32.SD_BOTH, | |
| 73 | }); | |
| 74 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 75 | return switch (ws2_32.WSAGetLastError()) { | |
| 76 | .WSAECONNABORTED => return error.ConnectionAborted, | |
| 77 | .WSAECONNRESET => return error.ConnectionResetByPeer, | |
| 78 | .WSAEINPROGRESS => return error.BlockingOperationInProgress, | |
| 79 | .WSAEINVAL => unreachable, | |
| 80 | .WSAENETDOWN => return error.NetworkSubsystemFailed, | |
| 81 | .WSAENOTCONN => return error.SocketNotConnected, | |
| 82 | .WSAENOTSOCK => unreachable, | |
| 83 | .WSANOTINITIALISED => unreachable, | |
| 84 | else => |err| return windows.unexpectedWSAError(err), | |
| 85 | }; | |
| 116 | /// Have the socket attempt to the connect to an address. | |
| 117 | pub fn connect(self: Socket, address: Socket.Address) !void { | |
| 118 | const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize())); | |
| 119 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 120 | return switch (ws2_32.WSAGetLastError()) { | |
| 121 | .WSAEADDRINUSE => error.AddressInUse, | |
| 122 | .WSAEADDRNOTAVAIL => error.AddressNotAvailable, | |
| 123 | .WSAECONNREFUSED => error.ConnectionRefused, | |
| 124 | .WSAETIMEDOUT => error.ConnectionTimedOut, | |
| 125 | .WSAEFAULT => error.BadAddress, | |
| 126 | .WSAEINVAL => error.ListeningSocket, | |
| 127 | .WSAEISCONN => error.AlreadyConnected, | |
| 128 | .WSAENOTSOCK => error.NotASocket, | |
| 129 | .WSAEACCES => error.BroadcastNotEnabled, | |
| 130 | .WSAENOBUFS => error.SystemResources, | |
| 131 | .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported, | |
| 132 | .WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock, | |
| 133 | .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable, | |
| 134 | else => |err| windows.unexpectedWSAError(err), | |
| 135 | }; | |
| 136 | } | |
| 86 | 137 | } |
| 87 | } | |
| 88 | ||
| 89 | /// Binds the socket to an address. | |
| 90 | pub fn bind(self: Socket, address: Socket.Address) !void { | |
| 91 | const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize())); | |
| 92 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 93 | return switch (ws2_32.WSAGetLastError()) { | |
| 94 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 95 | .WSAEACCES => error.AccessDenied, | |
| 96 | .WSAEADDRINUSE => error.AddressInUse, | |
| 97 | .WSAEADDRNOTAVAIL => error.AddressNotAvailable, | |
| 98 | .WSAEFAULT => error.BadAddress, | |
| 99 | .WSAEINPROGRESS => error.WouldBlock, | |
| 100 | .WSAEINVAL => error.AlreadyBound, | |
| 101 | .WSAENOBUFS => error.NoEphemeralPortsAvailable, | |
| 102 | .WSAENOTSOCK => error.NotASocket, | |
| 103 | else => |err| windows.unexpectedWSAError(err), | |
| 104 | }; | |
| 138 | ||
| 139 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 140 | /// of the socket. | |
| 141 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { | |
| 142 | var address: ws2_32.sockaddr_storage = undefined; | |
| 143 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 144 | ||
| 145 | const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 146 | if (rc == ws2_32.INVALID_SOCKET) { | |
| 147 | return switch (ws2_32.WSAGetLastError()) { | |
| 148 | .WSANOTINITIALISED => unreachable, | |
| 149 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 150 | .WSAEFAULT => unreachable, | |
| 151 | .WSAEINVAL => error.SocketNotListening, | |
| 152 | .WSAEMFILE => error.ProcessFdQuotaExceeded, | |
| 153 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 154 | .WSAENOBUFS => error.FileDescriptorNotASocket, | |
| 155 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 156 | .WSAEWOULDBLOCK => error.WouldBlock, | |
| 157 | else => |err| windows.unexpectedWSAError(err), | |
| 158 | }; | |
| 159 | } | |
| 160 | ||
| 161 | const socket = Socket.from(rc); | |
| 162 | const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address)); | |
| 163 | ||
| 164 | return Socket.Connection.from(socket, socket_address); | |
| 105 | 165 | } |
| 106 | } | |
| 107 | ||
| 108 | /// Start listening for incoming connections on the socket. | |
| 109 | pub fn listen(self: Socket, max_backlog_size: u31) !void { | |
| 110 | const rc = ws2_32.listen(self.fd, max_backlog_size); | |
| 111 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 112 | return switch (ws2_32.WSAGetLastError()) { | |
| 113 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 114 | .WSAEADDRINUSE => error.AddressInUse, | |
| 115 | .WSAEISCONN => error.AlreadyConnected, | |
| 116 | .WSAEINVAL => error.SocketNotBound, | |
| 117 | .WSAEMFILE, .WSAENOBUFS => error.SystemResources, | |
| 118 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 119 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 120 | .WSAEINPROGRESS => error.WouldBlock, | |
| 121 | else => |err| windows.unexpectedWSAError(err), | |
| 122 | }; | |
| 166 | ||
| 167 | /// Read data from the socket into the buffer provided with a set of flags | |
| 168 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 169 | pub fn read(self: Socket, buf: []u8, flags: u32) !usize { | |
| 170 | var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }}; | |
| 171 | var num_bytes: u32 = undefined; | |
| 172 | var flags_ = flags; | |
| 173 | ||
| 174 | const rc = ws2_32.WSARecv(self.fd, bufs, 1, &num_bytes, &flags_, null, null); | |
| 175 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 176 | return switch (ws2_32.WSAGetLastError()) { | |
| 177 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 178 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 179 | .WSAEDISCON => error.ConnectionClosedByPeer, | |
| 180 | .WSAEFAULT => error.BadBuffer, | |
| 181 | .WSAEINPROGRESS, | |
| 182 | .WSAEWOULDBLOCK, | |
| 183 | .WSA_IO_PENDING, | |
| 184 | .WSAETIMEDOUT, | |
| 185 | => error.WouldBlock, | |
| 186 | .WSAEINTR => error.Cancelled, | |
| 187 | .WSAEINVAL => error.SocketNotBound, | |
| 188 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 189 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 190 | .WSAENETRESET => error.NetworkReset, | |
| 191 | .WSAENOTCONN => error.SocketNotConnected, | |
| 192 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 193 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 194 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 195 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 196 | else => |err| windows.unexpectedWSAError(err), | |
| 197 | }; | |
| 198 | } | |
| 199 | ||
| 200 | return @intCast(usize, num_bytes); | |
| 123 | 201 | } |
| 124 | } | |
| 125 | ||
| 126 | /// Have the socket attempt to the connect to an address. | |
| 127 | pub fn connect(self: Socket, address: Socket.Address) !void { | |
| 128 | const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize())); | |
| 129 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 130 | return switch (ws2_32.WSAGetLastError()) { | |
| 131 | .WSAEADDRINUSE => error.AddressInUse, | |
| 132 | .WSAEADDRNOTAVAIL => error.AddressNotAvailable, | |
| 133 | .WSAECONNREFUSED => error.ConnectionRefused, | |
| 134 | .WSAETIMEDOUT => error.ConnectionTimedOut, | |
| 135 | .WSAEFAULT => error.BadAddress, | |
| 136 | .WSAEINVAL => error.ListeningSocket, | |
| 137 | .WSAEISCONN => error.AlreadyConnected, | |
| 138 | .WSAENOTSOCK => error.NotASocket, | |
| 139 | .WSAEACCES => error.BroadcastNotEnabled, | |
| 140 | .WSAENOBUFS => error.SystemResources, | |
| 141 | .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported, | |
| 142 | .WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock, | |
| 143 | .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable, | |
| 144 | else => |err| windows.unexpectedWSAError(err), | |
| 145 | }; | |
| 202 | ||
| 203 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 204 | /// It returns the number of bytes that are written to the socket. | |
| 205 | pub fn write(self: Socket, buf: []const u8, flags: u32) !usize { | |
| 206 | var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = @intToPtr([*]u8, @ptrToInt(buf.ptr)) }}; | |
| 207 | var num_bytes: u32 = undefined; | |
| 208 | ||
| 209 | const rc = ws2_32.WSASend(self.fd, bufs, 1, &num_bytes, flags, null, null); | |
| 210 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 211 | return switch (ws2_32.WSAGetLastError()) { | |
| 212 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 213 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 214 | .WSAEFAULT => error.BadBuffer, | |
| 215 | .WSAEINPROGRESS, | |
| 216 | .WSAEWOULDBLOCK, | |
| 217 | .WSA_IO_PENDING, | |
| 218 | .WSAETIMEDOUT, | |
| 219 | => error.WouldBlock, | |
| 220 | .WSAEINTR => error.Cancelled, | |
| 221 | .WSAEINVAL => error.SocketNotBound, | |
| 222 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 223 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 224 | .WSAENETRESET => error.NetworkReset, | |
| 225 | .WSAENOBUFS => error.BufferDeadlock, | |
| 226 | .WSAENOTCONN => error.SocketNotConnected, | |
| 227 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 228 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 229 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 230 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 231 | else => |err| windows.unexpectedWSAError(err), | |
| 232 | }; | |
| 233 | } | |
| 234 | ||
| 235 | return @intCast(usize, num_bytes); | |
| 146 | 236 | } |
| 147 | } | |
| 148 | ||
| 149 | /// Accept a pending incoming connection queued to the kernel backlog | |
| 150 | /// of the socket. | |
| 151 | pub fn accept(self: Socket, flags: u32) !Socket.Connection { | |
| 152 | var address: ws2_32.sockaddr_storage = undefined; | |
| 153 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 154 | ||
| 155 | const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 156 | if (rc == ws2_32.INVALID_SOCKET) { | |
| 157 | return switch (ws2_32.WSAGetLastError()) { | |
| 158 | .WSANOTINITIALISED => unreachable, | |
| 159 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 160 | .WSAEFAULT => unreachable, | |
| 161 | .WSAEINVAL => error.SocketNotListening, | |
| 162 | .WSAEMFILE => error.ProcessFdQuotaExceeded, | |
| 163 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 164 | .WSAENOBUFS => error.FileDescriptorNotASocket, | |
| 165 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 166 | .WSAEWOULDBLOCK => error.WouldBlock, | |
| 167 | else => |err| windows.unexpectedWSAError(err), | |
| 168 | }; | |
| 237 | ||
| 238 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 239 | /// with a set of flags specified. It returns the number of bytes that are | |
| 240 | /// written to the socket. | |
| 241 | pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize { | |
| 242 | const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG); | |
| 243 | ||
| 244 | var num_bytes: u32 = undefined; | |
| 245 | ||
| 246 | const rc = call(self.fd, &msg, flags, &num_bytes, null, null); | |
| 247 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 248 | return switch (ws2_32.WSAGetLastError()) { | |
| 249 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 250 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 251 | .WSAEFAULT => error.BadBuffer, | |
| 252 | .WSAEINPROGRESS, | |
| 253 | .WSAEWOULDBLOCK, | |
| 254 | .WSA_IO_PENDING, | |
| 255 | .WSAETIMEDOUT, | |
| 256 | => error.WouldBlock, | |
| 257 | .WSAEINTR => error.Cancelled, | |
| 258 | .WSAEINVAL => error.SocketNotBound, | |
| 259 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 260 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 261 | .WSAENETRESET => error.NetworkReset, | |
| 262 | .WSAENOBUFS => error.BufferDeadlock, | |
| 263 | .WSAENOTCONN => error.SocketNotConnected, | |
| 264 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 265 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 266 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 267 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 268 | else => |err| windows.unexpectedWSAError(err), | |
| 269 | }; | |
| 270 | } | |
| 271 | ||
| 272 | return @intCast(usize, num_bytes); | |
| 169 | 273 | } |
| 170 | 274 | |
| 171 | const socket = Socket.from(rc); | |
| 172 | const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address)); | |
| 173 | ||
| 174 | return Socket.Connection.from(socket, socket_address); | |
| 175 | } | |
| 176 | ||
| 177 | /// Read data from the socket into the buffer provided with a set of flags | |
| 178 | /// specified. It returns the number of bytes read into the buffer provided. | |
| 179 | pub fn read(self: Socket, buf: []u8, flags: u32) !usize { | |
| 180 | var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }}; | |
| 181 | var flags_ = flags; | |
| 182 | ||
| 183 | const rc = ws2_32.WSARecv(self.fd, bufs, 1, null, &flags_, null, null); | |
| 184 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 185 | return switch (ws2_32.WSAGetLastError()) { | |
| 186 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 187 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 188 | .WSAEDISCON => error.ConnectionClosedByPeer, | |
| 189 | .WSAEFAULT => error.BadBuffer, | |
| 190 | .WSAEINPROGRESS, | |
| 191 | .WSAEWOULDBLOCK, | |
| 192 | .WSA_IO_PENDING, | |
| 193 | .WSAETIMEDOUT, | |
| 194 | => error.WouldBlock, | |
| 195 | .WSAEINTR => error.Cancelled, | |
| 196 | .WSAEINVAL => error.SocketNotBound, | |
| 197 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 198 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 199 | .WSAENETRESET => error.NetworkReset, | |
| 200 | .WSAENOTCONN => error.SocketNotConnected, | |
| 201 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 202 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 203 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 204 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 205 | else => |err| windows.unexpectedWSAError(err), | |
| 206 | }; | |
| 275 | /// Read multiple I/O vectors with a prepended message header from the socket | |
| 276 | /// with a set of flags specified. It returns the number of bytes that were | |
| 277 | /// read into the buffer provided. | |
| 278 | pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize { | |
| 279 | const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG); | |
| 280 | ||
| 281 | var num_bytes: u32 = undefined; | |
| 282 | ||
| 283 | const rc = call(self.fd, msg, &num_bytes, null, null); | |
| 284 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 285 | return switch (ws2_32.WSAGetLastError()) { | |
| 286 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 287 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 288 | .WSAEDISCON => error.ConnectionClosedByPeer, | |
| 289 | .WSAEFAULT => error.BadBuffer, | |
| 290 | .WSAEINPROGRESS, | |
| 291 | .WSAEWOULDBLOCK, | |
| 292 | .WSA_IO_PENDING, | |
| 293 | .WSAETIMEDOUT, | |
| 294 | => error.WouldBlock, | |
| 295 | .WSAEINTR => error.Cancelled, | |
| 296 | .WSAEINVAL => error.SocketNotBound, | |
| 297 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 298 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 299 | .WSAENETRESET => error.NetworkReset, | |
| 300 | .WSAENOTCONN => error.SocketNotConnected, | |
| 301 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 302 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 303 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 304 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 305 | else => |err| windows.unexpectedWSAError(err), | |
| 306 | }; | |
| 307 | } | |
| 308 | ||
| 309 | return @intCast(usize, num_bytes); | |
| 207 | 310 | } |
| 208 | 311 | |
| 209 | return @intCast(usize, rc); | |
| 210 | } | |
| 211 | ||
| 212 | /// Write a buffer of data provided to the socket with a set of flags specified. | |
| 213 | /// It returns the number of bytes that are written to the socket. | |
| 214 | pub fn write(self: Socket, buf: []const u8, flags: u32) !usize { | |
| 215 | var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }}; | |
| 216 | var flags_ = flags; | |
| 217 | ||
| 218 | const rc = ws2_32.WSASend(self.fd, bufs, 1, null, &flags_, null, null); | |
| 219 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 220 | return switch (ws2_32.WSAGetLastError()) { | |
| 221 | .WSAECONNABORTED => error.ConnectionAborted, | |
| 222 | .WSAECONNRESET => error.ConnectionResetByPeer, | |
| 223 | .WSAEFAULT => error.BadBuffer, | |
| 224 | .WSAEINPROGRESS, | |
| 225 | .WSAEWOULDBLOCK, | |
| 226 | .WSA_IO_PENDING, | |
| 227 | .WSAETIMEDOUT, | |
| 228 | => error.WouldBlock, | |
| 229 | .WSAEINTR => error.Cancelled, | |
| 230 | .WSAEINVAL => error.SocketNotBound, | |
| 231 | .WSAEMSGSIZE => error.MessageTooLarge, | |
| 232 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 233 | .WSAENETRESET => error.NetworkReset, | |
| 234 | .WSAENOBUFS => error.BufferDeadlock, | |
| 235 | .WSAENOTCONN => error.SocketNotConnected, | |
| 236 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 237 | .WSAEOPNOTSUPP => error.OperationNotSupported, | |
| 238 | .WSAESHUTDOWN => error.AlreadyShutdown, | |
| 239 | .WSA_OPERATION_ABORTED => error.OperationAborted, | |
| 240 | else => |err| windows.unexpectedWSAError(err), | |
| 241 | }; | |
| 312 | /// Query the address that the socket is locally bounded to. | |
| 313 | pub fn getLocalAddress(self: Socket) !Socket.Address { | |
| 314 | var address: ws2_32.sockaddr_storage = undefined; | |
| 315 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 316 | ||
| 317 | const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 318 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 319 | return switch (ws2_32.WSAGetLastError()) { | |
| 320 | .WSANOTINITIALISED => unreachable, | |
| 321 | .WSAEFAULT => unreachable, | |
| 322 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 323 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 324 | .WSAEINVAL => error.SocketNotBound, | |
| 325 | else => |err| windows.unexpectedWSAError(err), | |
| 326 | }; | |
| 327 | } | |
| 328 | ||
| 329 | return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address)); | |
| 242 | 330 | } |
| 243 | 331 | |
| 244 | return @intCast(usize, rc); | |
| 245 | } | |
| 246 | ||
| 247 | /// Writes multiple I/O vectors with a prepended message header to the socket | |
| 248 | /// with a set of flags specified. It returns the number of bytes that are | |
| 249 | /// written to the socket. | |
| 250 | pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize { | |
| 251 | return error.NotImplemented; | |
| 252 | } | |
| 253 | ||
| 254 | /// Read multiple I/O vectors with a prepended message header from the socket | |
| 255 | /// with a set of flags specified. It returns the number of bytes that were | |
| 256 | /// read into the buffer provided. | |
| 257 | pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize { | |
| 258 | return error.NotImplemented; | |
| 259 | } | |
| 260 | ||
| 261 | /// Query the address that the socket is locally bounded to. | |
| 262 | pub fn getLocalAddress(self: Socket) !Socket.Address { | |
| 263 | var address: ws2_32.sockaddr_storage = undefined; | |
| 264 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 265 | ||
| 266 | const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 267 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 268 | return switch (ws2_32.WSAGetLastError()) { | |
| 269 | .WSANOTINITIALISED => unreachable, | |
| 270 | .WSAEFAULT => unreachable, | |
| 271 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 272 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 273 | .WSAEINVAL => error.SocketNotBound, | |
| 274 | else => |err| windows.unexpectedWSAError(err), | |
| 275 | }; | |
| 332 | /// Query the address that the socket is connected to. | |
| 333 | pub fn getRemoteAddress(self: Socket) !Socket.Address { | |
| 334 | var address: ws2_32.sockaddr_storage = undefined; | |
| 335 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 336 | ||
| 337 | const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 338 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 339 | return switch (ws2_32.WSAGetLastError()) { | |
| 340 | .WSANOTINITIALISED => unreachable, | |
| 341 | .WSAEFAULT => unreachable, | |
| 342 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 343 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 344 | .WSAEINVAL => error.SocketNotBound, | |
| 345 | else => |err| windows.unexpectedWSAError(err), | |
| 346 | }; | |
| 347 | } | |
| 348 | ||
| 349 | return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address)); | |
| 276 | 350 | } |
| 277 | 351 | |
| 278 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 279 | } | |
| 280 | ||
| 281 | /// Query the address that the socket is connected to. | |
| 282 | pub fn getRemoteAddress(self: Socket) !Socket.Address { | |
| 283 | var address: ws2_32.sockaddr_storage = undefined; | |
| 284 | var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage); | |
| 285 | ||
| 286 | const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len); | |
| 287 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 288 | return switch (ws2_32.WSAGetLastError()) { | |
| 289 | .WSANOTINITIALISED => unreachable, | |
| 290 | .WSAEFAULT => unreachable, | |
| 291 | .WSAENETDOWN => error.NetworkSubsystemFailed, | |
| 292 | .WSAENOTSOCK => error.FileDescriptorNotASocket, | |
| 293 | .WSAEINVAL => error.SocketNotBound, | |
| 294 | else => |err| windows.unexpectedWSAError(err), | |
| 295 | }; | |
| 352 | /// Query and return the latest cached error on the socket. | |
| 353 | pub fn getError(self: Socket) !void { | |
| 354 | return {}; | |
| 355 | } | |
| 356 | ||
| 357 | /// Query the read buffer size of the socket. | |
| 358 | pub fn getReadBufferSize(self: Socket) !u32 { | |
| 359 | return 0; | |
| 296 | 360 | } |
| 297 | 361 | |
| 298 | return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address)); | |
| 299 | } | |
| 300 | ||
| 301 | /// Query and return the latest cached error on the socket. | |
| 302 | pub fn getError(self: Socket) !void { | |
| 303 | return {}; | |
| 304 | } | |
| 305 | ||
| 306 | /// Query the read buffer size of the socket. | |
| 307 | pub fn getReadBufferSize(self: Socket) !u32 { | |
| 308 | return 0; | |
| 309 | } | |
| 310 | ||
| 311 | /// Query the write buffer size of the socket. | |
| 312 | pub fn getWriteBufferSize(self: Socket) !u32 { | |
| 313 | return 0; | |
| 314 | } | |
| 315 | ||
| 316 | /// Set a socket option. | |
| 317 | pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void { | |
| 318 | const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len)); | |
| 319 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 320 | return switch (ws2_32.WSAGetLastError()) { | |
| 321 | .WSANOTINITIALISED => unreachable, | |
| 322 | .WSAENETDOWN => return error.NetworkSubsystemFailed, | |
| 323 | .WSAEFAULT => unreachable, | |
| 324 | .WSAENOTSOCK => return error.FileDescriptorNotASocket, | |
| 325 | .WSAEINVAL => return error.SocketNotBound, | |
| 326 | .WSAENOTCONN => return error.SocketNotConnected, | |
| 327 | .WSAESHUTDOWN => return error.AlreadyShutdown, | |
| 328 | else => |err| windows.unexpectedWSAError(err), | |
| 362 | /// Query the write buffer size of the socket. | |
| 363 | pub fn getWriteBufferSize(self: Socket) !u32 { | |
| 364 | return 0; | |
| 365 | } | |
| 366 | ||
| 367 | /// Set a socket option. | |
| 368 | pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void { | |
| 369 | const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len)); | |
| 370 | if (rc == ws2_32.SOCKET_ERROR) { | |
| 371 | return switch (ws2_32.WSAGetLastError()) { | |
| 372 | .WSANOTINITIALISED => unreachable, | |
| 373 | .WSAENETDOWN => return error.NetworkSubsystemFailed, | |
| 374 | .WSAEFAULT => unreachable, | |
| 375 | .WSAENOTSOCK => return error.FileDescriptorNotASocket, | |
| 376 | .WSAEINVAL => return error.SocketNotBound, | |
| 377 | .WSAENOTCONN => return error.SocketNotConnected, | |
| 378 | .WSAESHUTDOWN => return error.AlreadyShutdown, | |
| 379 | else => |err| windows.unexpectedWSAError(err), | |
| 380 | }; | |
| 381 | } | |
| 382 | } | |
| 383 | ||
| 384 | /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully | |
| 385 | /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption` | |
| 386 | /// if the host does not support the option for a socket to linger around up until a timeout specified in | |
| 387 | /// seconds. | |
| 388 | pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { | |
| 389 | const settings = ws2_32.linger{ | |
| 390 | .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)), | |
| 391 | .l_linger = if (timeout_seconds) |seconds| seconds else 0, | |
| 329 | 392 | }; |
| 393 | ||
| 394 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings)); | |
| 395 | } | |
| 396 | ||
| 397 | /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive | |
| 398 | /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if | |
| 399 | /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. | |
| 400 | pub fn setKeepAlive(self: Socket, enabled: bool) !void { | |
| 401 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 402 | } | |
| 403 | ||
| 404 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 405 | /// the host does not support sockets listening the same address. | |
| 406 | pub fn setReuseAddress(self: Socket, enabled: bool) !void { | |
| 407 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 408 | } | |
| 409 | ||
| 410 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 411 | /// the host does not supports sockets listening on the same port. | |
| 412 | /// | |
| 413 | /// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior | |
| 414 | pub fn setReusePort(self: Socket, enabled: bool) !void { | |
| 415 | try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 416 | try self.setReuseAddress(enabled); | |
| 417 | } | |
| 418 | ||
| 419 | /// Set the write buffer size of the socket. | |
| 420 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { | |
| 421 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size)); | |
| 422 | } | |
| 423 | ||
| 424 | /// Set the read buffer size of the socket. | |
| 425 | pub fn setReadBufferSize(self: Socket, size: u32) !void { | |
| 426 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size)); | |
| 427 | } | |
| 428 | ||
| 429 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 430 | /// set on a non-blocking socket. | |
| 431 | /// | |
| 432 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 433 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 434 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 435 | pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void { | |
| 436 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds)); | |
| 437 | } | |
| 438 | ||
| 439 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 440 | /// set on a non-blocking socket. | |
| 441 | /// | |
| 442 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 443 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 444 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 445 | /// exceeded. | |
| 446 | pub fn setReadTimeout(self: Socket, milliseconds: u32) !void { | |
| 447 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds)); | |
| 330 | 448 | } |
| 331 | } | |
| 332 | ||
| 333 | /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully | |
| 334 | /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption` | |
| 335 | /// if the host does not support the option for a socket to linger around up until a timeout specified in | |
| 336 | /// seconds. | |
| 337 | pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void { | |
| 338 | const settings = ws2_32.linger{ | |
| 339 | .l_onoff = @as(u16, @boolToInt(timeout_seconds != null)), | |
| 340 | .l_linger = if (timeout_seconds) |seconds| seconds else 0, | |
| 341 | }; | |
| 342 | ||
| 343 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings)); | |
| 344 | } | |
| 345 | ||
| 346 | /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive | |
| 347 | /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if | |
| 348 | /// the host does not support periodically sending keep-alive messages on connection-oriented sockets. | |
| 349 | pub fn setKeepAlive(self: Socket, enabled: bool) !void { | |
| 350 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 351 | } | |
| 352 | ||
| 353 | /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if | |
| 354 | /// the host does not support sockets listening the same address. | |
| 355 | pub fn setReuseAddress(self: Socket, enabled: bool) !void { | |
| 356 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 357 | } | |
| 358 | ||
| 359 | /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if | |
| 360 | /// the host does not supports sockets listening on the same port. | |
| 361 | /// | |
| 362 | /// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior | |
| 363 | pub fn setReusePort(self: Socket, enabled: bool) !void { | |
| 364 | try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled)))); | |
| 365 | try self.setReuseAddress(enabled); | |
| 366 | } | |
| 367 | ||
| 368 | /// Set the write buffer size of the socket. | |
| 369 | pub fn setWriteBufferSize(self: Socket, size: u32) !void { | |
| 370 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size)); | |
| 371 | } | |
| 372 | ||
| 373 | /// Set the read buffer size of the socket. | |
| 374 | pub fn setReadBufferSize(self: Socket, size: u32) !void { | |
| 375 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size)); | |
| 376 | } | |
| 377 | ||
| 378 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 379 | /// set on a non-blocking socket. | |
| 380 | /// | |
| 381 | /// Set a timeout on the socket that is to occur if no messages are successfully written | |
| 382 | /// to its bound destination after a specified number of milliseconds. A subsequent write | |
| 383 | /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded. | |
| 384 | pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void { | |
| 385 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds)); | |
| 386 | } | |
| 387 | ||
| 388 | /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is | |
| 389 | /// set on a non-blocking socket. | |
| 390 | /// | |
| 391 | /// Set a timeout on the socket that is to occur if no messages are successfully read | |
| 392 | /// from its bound destination after a specified number of milliseconds. A subsequent | |
| 393 | /// read from the socket will thereafter return `error.WouldBlock` should the timeout be | |
| 394 | /// exceeded. | |
| 395 | pub fn setReadTimeout(self: Socket, milliseconds: u32) !void { | |
| 396 | return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds)); | |
| 397 | } | |
| 398 | }; | |
| 449 | }; | |
| 450 | } |