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