mirror of
https://github.com/ziglang/zig.git
synced 2026-01-20 14:25:16 +00:00
x/os/socket, std/os/windows: implement loading winsock extensions
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.
This commit is contained in:
parent
3d946ef5eb
commit
77f8a9ae22
@ -1749,6 +1749,38 @@ fn MAKELANGID(p: c_ushort, s: c_ushort) callconv(.Inline) LANGID {
|
||||
return (s << 10) | p;
|
||||
}
|
||||
|
||||
/// Loads a Winsock extension function in runtime specified by a GUID.
|
||||
pub fn loadWinsockExtensionFunction(comptime T: type, sock: ws2_32.SOCKET, guid: GUID) !T {
|
||||
var function: T = undefined;
|
||||
var num_bytes: DWORD = undefined;
|
||||
|
||||
const rc = ws2_32.WSAIoctl(
|
||||
sock,
|
||||
ws2_32.SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||
@ptrCast(*const c_void, &guid),
|
||||
@sizeOf(GUID),
|
||||
&function,
|
||||
@sizeOf(T),
|
||||
&num_bytes,
|
||||
null,
|
||||
null,
|
||||
);
|
||||
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
else => |err| unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
if (num_bytes != @sizeOf(T)) {
|
||||
return error.ShortRead;
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
/// Call this when you made a windows DLL call or something that does SetLastError
|
||||
/// and you get an unexpected error.
|
||||
pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
|
||||
|
||||
@ -266,10 +266,54 @@ pub const SENDER_DEFAULT_LATE_JOINER_PERCENTAGE = 0;
|
||||
pub const SENDER_MAX_LATE_JOINER_PERCENTAGE = 75;
|
||||
pub const BITS_PER_BYTE = 8;
|
||||
pub const LOG2_BITS_PER_BYTE = 3;
|
||||
|
||||
pub const SOCKET_DEFAULT2_QM_POLICY = GUID.parse("{aec2ef9c-3a4d-4d3e-8842-239942e39a47}");
|
||||
pub const REAL_TIME_NOTIFICATION_CAPABILITY = GUID.parse("{6b59819a-5cae-492d-a901-2a3c2c50164f}");
|
||||
pub const REAL_TIME_NOTIFICATION_CAPABILITY_EX = GUID.parse("{6843da03-154a-4616-a508-44371295f96b}");
|
||||
pub const ASSOCIATE_NAMERES_CONTEXT = GUID.parse("{59a38b67-d4fe-46e1-ba3c-87ea74ca3049}");
|
||||
|
||||
pub const WSAID_CONNECTEX = GUID{
|
||||
.Data1 = 0x25a207b9,
|
||||
.Data2 = 0xddf3,
|
||||
.Data3 = 0x4660,
|
||||
.Data4 = [8]u8{ 0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e },
|
||||
};
|
||||
|
||||
pub const WSAID_ACCEPTEX = GUID{
|
||||
.Data1 = 0xb5367df1,
|
||||
.Data2 = 0xcbac,
|
||||
.Data3 = 0x11cf,
|
||||
.Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 },
|
||||
};
|
||||
|
||||
pub const WSAID_GETACCEPTEXSOCKADDRS = GUID{
|
||||
.Data1 = 0xb5367df2,
|
||||
.Data2 = 0xcbac,
|
||||
.Data3 = 0x11cf,
|
||||
.Data4 = [8]u8{ 0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92 },
|
||||
};
|
||||
|
||||
pub const WSAID_WSARECVMSG = GUID{
|
||||
.Data1 = 0xf689d7c8,
|
||||
.Data2 = 0x6f1f,
|
||||
.Data3 = 0x436b,
|
||||
.Data4 = [8]u8{ 0x8a, 0x53, 0xe5, 0x4f, 0xe3, 0x51, 0xc3, 0x22 },
|
||||
};
|
||||
|
||||
pub const WSAID_WSAPOLL = GUID{
|
||||
.Data1 = 0x18C76F85,
|
||||
.Data2 = 0xDC66,
|
||||
.Data3 = 0x4964,
|
||||
.Data4 = [8]u8{ 0x97, 0x2E, 0x23, 0xC2, 0x72, 0x38, 0x31, 0x2B },
|
||||
};
|
||||
|
||||
pub const WSAID_WSASENDMSG = GUID{
|
||||
.Data1 = 0xa441e712,
|
||||
.Data2 = 0x754f,
|
||||
.Data3 = 0x43ca,
|
||||
.Data4 = [8]u8{ 0x84, 0xa7, 0x0d, 0xee, 0x44, 0xcf, 0x60, 0x6d },
|
||||
};
|
||||
|
||||
pub const TCP_INITIAL_RTO_DEFAULT_RTT = 0;
|
||||
pub const TCP_INITIAL_RTO_DEFAULT_MAX_SYN_RETRANSMISSIONS = 0;
|
||||
pub const SOCKET_SETTINGS_GUARANTEE_ENCRYPTION = 1;
|
||||
@ -485,6 +529,7 @@ pub const IOC_UNIX = 0;
|
||||
pub const IOC_WS2 = 134217728;
|
||||
pub const IOC_PROTOCOL = 268435456;
|
||||
pub const IOC_VENDOR = 402653184;
|
||||
pub const SIO_GET_EXTENSION_FUNCTION_POINTER = IOC_OUT | IOC_IN | IOC_WS2 | 6;
|
||||
pub const SIO_BSP_HANDLE = IOC_OUT | IOC_WS2 | 27;
|
||||
pub const SIO_BSP_HANDLE_SELECT = IOC_OUT | IOC_WS2 | 28;
|
||||
pub const SIO_BSP_HANDLE_POLL = IOC_OUT | IOC_WS2 | 29;
|
||||
@ -1115,9 +1160,9 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = fn (
|
||||
RemoteSockaddrLength: *i32,
|
||||
) callconv(WINAPI) void;
|
||||
|
||||
pub const LFN_WSASENDMSG = fn (
|
||||
pub const LPFN_WSASENDMSG = fn (
|
||||
s: SOCKET,
|
||||
lpMsg: *WSAMSG_const,
|
||||
lpMsg: *const WSAMSG_const,
|
||||
dwFlags: u32,
|
||||
lpNumberOfBytesSent: ?*u32,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
@ -1927,7 +1972,7 @@ pub extern "ws2_32" fn WSAHtons(
|
||||
pub extern "ws2_32" fn WSAIoctl(
|
||||
s: SOCKET,
|
||||
dwIoControlCode: u32,
|
||||
lpvInBuffer: ?*c_void,
|
||||
lpvInBuffer: ?*const c_void,
|
||||
cbInBuffer: u32,
|
||||
lpvOutbuffer: ?*c_void,
|
||||
cbOutbuffer: u32,
|
||||
@ -1992,7 +2037,7 @@ pub extern "ws2_32" fn WSASend(
|
||||
s: SOCKET,
|
||||
lpBuffers: [*]WSABUF,
|
||||
dwBufferCount: u32,
|
||||
lpNumberOfBytesSent: ?*U32,
|
||||
lpNumberOfBytesSent: ?*u32,
|
||||
dwFlags: u32,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
|
||||
@ -2000,7 +2045,7 @@ pub extern "ws2_32" fn WSASend(
|
||||
|
||||
pub extern "ws2_32" fn WSASendMsg(
|
||||
s: SOCKET,
|
||||
lpMsg: *WSAMSG_const,
|
||||
lpMsg: *const WSAMSG_const,
|
||||
dwFlags: u32,
|
||||
lpNumberOfBytesSent: ?*u32,
|
||||
lpOverlapped: ?*OVERLAPPED,
|
||||
|
||||
@ -13,105 +13,111 @@ const mem = std.mem;
|
||||
const time = std.time;
|
||||
const builtin = std.builtin;
|
||||
|
||||
/// Import in a `Socket` abstraction depending on the platform we are compiling against.
|
||||
pub usingnamespace switch (builtin.os.tag) {
|
||||
.windows => @import("socket_windows.zig"),
|
||||
else => @import("socket_posix.zig"),
|
||||
};
|
||||
/// A generic, cross-platform socket abstraction.
|
||||
pub const Socket = struct {
|
||||
/// A socket-address pair.
|
||||
pub const Connection = struct {
|
||||
socket: Socket,
|
||||
address: Socket.Address,
|
||||
|
||||
/// A common subset of shared structs across cross-platform abstractions over socket syscalls.
|
||||
pub fn Mixin(comptime Self: type) type {
|
||||
return struct {
|
||||
/// A socket-address pair.
|
||||
pub const Connection = struct {
|
||||
socket: Self,
|
||||
address: Self.Address,
|
||||
|
||||
/// Enclose a socket and address into a socket-address pair.
|
||||
pub fn from(socket: Self, address: Self.Address) Self.Connection {
|
||||
return .{ .socket = socket, .address = address };
|
||||
}
|
||||
};
|
||||
|
||||
/// A generic socket address abstraction. It is safe to directly access and modify
|
||||
/// the fields of a `Self.Address`.
|
||||
pub const Address = union(enum) {
|
||||
ipv4: net.IPv4.Address,
|
||||
ipv6: net.IPv6.Address,
|
||||
|
||||
/// Instantiate a new address with a IPv4 host and port.
|
||||
pub fn initIPv4(host: net.IPv4, port: u16) Self.Address {
|
||||
return .{ .ipv4 = .{ .host = host, .port = port } };
|
||||
}
|
||||
|
||||
/// Instantiate a new address with a IPv6 host and port.
|
||||
pub fn initIPv6(host: net.IPv6, port: u16) Self.Address {
|
||||
return .{ .ipv6 = .{ .host = host, .port = port } };
|
||||
}
|
||||
|
||||
/// Parses a `sockaddr` into a generic socket address.
|
||||
pub fn fromNative(address: *align(4) const os.sockaddr) Self.Address {
|
||||
switch (address.family) {
|
||||
os.AF_INET => {
|
||||
const info = @ptrCast(*const os.sockaddr_in, address);
|
||||
const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
|
||||
const port = mem.bigToNative(u16, info.port);
|
||||
return Self.Address.initIPv4(host, port);
|
||||
},
|
||||
os.AF_INET6 => {
|
||||
const info = @ptrCast(*const os.sockaddr_in6, address);
|
||||
const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
|
||||
const port = mem.bigToNative(u16, info.port);
|
||||
return Self.Address.initIPv6(host, port);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes a generic socket address into an extern union that may be reliably
|
||||
/// casted into a `sockaddr` which may be passed into socket syscalls.
|
||||
pub fn toNative(self: Self.Address) extern union {
|
||||
ipv4: os.sockaddr_in,
|
||||
ipv6: os.sockaddr_in6,
|
||||
} {
|
||||
return switch (self) {
|
||||
.ipv4 => |address| .{
|
||||
.ipv4 = .{
|
||||
.addr = @bitCast(u32, address.host.octets),
|
||||
.port = mem.nativeToBig(u16, address.port),
|
||||
},
|
||||
},
|
||||
.ipv6 => |address| .{
|
||||
.ipv6 = .{
|
||||
.addr = address.host.octets,
|
||||
.port = mem.nativeToBig(u16, address.port),
|
||||
.scope_id = address.host.scope_id,
|
||||
.flowinfo = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
|
||||
pub fn getNativeSize(self: Self.Address) u32 {
|
||||
return switch (self) {
|
||||
.ipv4 => @sizeOf(os.sockaddr_in),
|
||||
.ipv6 => @sizeOf(os.sockaddr_in6),
|
||||
};
|
||||
}
|
||||
|
||||
/// Implements the `std.fmt.format` API.
|
||||
pub fn format(
|
||||
self: Self.Address,
|
||||
comptime layout: []const u8,
|
||||
opts: fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||
}
|
||||
}
|
||||
};
|
||||
/// Enclose a socket and address into a socket-address pair.
|
||||
pub fn from(socket: Socket, address: Socket.Address) Socket.Connection {
|
||||
return .{ .socket = socket, .address = address };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A generic socket address abstraction. It is safe to directly access and modify
|
||||
/// the fields of a `Socket.Address`.
|
||||
pub const Address = union(enum) {
|
||||
ipv4: net.IPv4.Address,
|
||||
ipv6: net.IPv6.Address,
|
||||
|
||||
/// Instantiate a new address with a IPv4 host and port.
|
||||
pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address {
|
||||
return .{ .ipv4 = .{ .host = host, .port = port } };
|
||||
}
|
||||
|
||||
/// Instantiate a new address with a IPv6 host and port.
|
||||
pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address {
|
||||
return .{ .ipv6 = .{ .host = host, .port = port } };
|
||||
}
|
||||
|
||||
/// Parses a `sockaddr` into a generic socket address.
|
||||
pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address {
|
||||
switch (address.family) {
|
||||
os.AF_INET => {
|
||||
const info = @ptrCast(*const os.sockaddr_in, address);
|
||||
const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
|
||||
const port = mem.bigToNative(u16, info.port);
|
||||
return Socket.Address.initIPv4(host, port);
|
||||
},
|
||||
os.AF_INET6 => {
|
||||
const info = @ptrCast(*const os.sockaddr_in6, address);
|
||||
const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
|
||||
const port = mem.bigToNative(u16, info.port);
|
||||
return Socket.Address.initIPv6(host, port);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes a generic socket address into an extern union that may be reliably
|
||||
/// casted into a `sockaddr` which may be passed into socket syscalls.
|
||||
pub fn toNative(self: Socket.Address) extern union {
|
||||
ipv4: os.sockaddr_in,
|
||||
ipv6: os.sockaddr_in6,
|
||||
} {
|
||||
return switch (self) {
|
||||
.ipv4 => |address| .{
|
||||
.ipv4 = .{
|
||||
.addr = @bitCast(u32, address.host.octets),
|
||||
.port = mem.nativeToBig(u16, address.port),
|
||||
},
|
||||
},
|
||||
.ipv6 => |address| .{
|
||||
.ipv6 = .{
|
||||
.addr = address.host.octets,
|
||||
.port = mem.nativeToBig(u16, address.port),
|
||||
.scope_id = address.host.scope_id,
|
||||
.flowinfo = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
|
||||
pub fn getNativeSize(self: Socket.Address) u32 {
|
||||
return switch (self) {
|
||||
.ipv4 => @sizeOf(os.sockaddr_in),
|
||||
.ipv6 => @sizeOf(os.sockaddr_in6),
|
||||
};
|
||||
}
|
||||
|
||||
/// Implements the `std.fmt.format` API.
|
||||
pub fn format(
|
||||
self: Socket.Address,
|
||||
comptime layout: []const u8,
|
||||
opts: fmt.FormatOptions,
|
||||
writer: anytype,
|
||||
) !void {
|
||||
switch (self) {
|
||||
.ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||
.ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// The underlying handle of a socket.
|
||||
fd: os.socket_t,
|
||||
|
||||
/// Enclose a socket abstraction over an existing socket file descriptor.
|
||||
pub fn from(fd: os.socket_t) Socket {
|
||||
return Socket{ .fd = fd };
|
||||
}
|
||||
|
||||
/// Mix in socket syscalls depending on the platform we are compiling against.
|
||||
pub usingnamespace switch (builtin.os.tag) {
|
||||
.windows => @import("socket_windows.zig"),
|
||||
else => @import("socket_posix.zig"),
|
||||
}.Mixin(Socket);
|
||||
};
|
||||
|
||||
@ -10,232 +10,242 @@ const os = std.os;
|
||||
const mem = std.mem;
|
||||
const time = std.time;
|
||||
|
||||
pub const Socket = struct {
|
||||
/// Import in `Socket.Address` and `Socket.Connection`.
|
||||
pub usingnamespace @import("socket.zig").Mixin(Socket);
|
||||
pub fn Mixin(comptime Socket: type) type {
|
||||
return struct {
|
||||
/// Open a new socket.
|
||||
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
|
||||
return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
|
||||
}
|
||||
|
||||
/// The underlying handle of a socket.
|
||||
fd: os.socket_t,
|
||||
/// Closes the socket.
|
||||
pub fn deinit(self: Socket) void {
|
||||
os.closeSocket(self.fd);
|
||||
}
|
||||
|
||||
/// Open a new socket.
|
||||
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
|
||||
return Socket{ .fd = try os.socket(domain, socket_type, protocol) };
|
||||
}
|
||||
/// Shutdown either the read side, write side, or all side of the socket.
|
||||
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
||||
return os.shutdown(self.fd, how);
|
||||
}
|
||||
|
||||
/// Enclose a socket abstraction over an existing socket file descriptor.
|
||||
pub fn from(fd: os.socket_t) Socket {
|
||||
return Socket{ .fd = fd };
|
||||
}
|
||||
/// Binds the socket to an address.
|
||||
pub fn bind(self: Socket, address: Socket.Address) !void {
|
||||
return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||
}
|
||||
|
||||
/// Closes the socket.
|
||||
pub fn deinit(self: Socket) void {
|
||||
os.closeSocket(self.fd);
|
||||
}
|
||||
/// Start listening for incoming connections on the socket.
|
||||
pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
||||
return os.listen(self.fd, max_backlog_size);
|
||||
}
|
||||
|
||||
/// Shutdown either the read side, write side, or all side of the socket.
|
||||
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
||||
return os.shutdown(self.fd, how);
|
||||
}
|
||||
/// Have the socket attempt to the connect to an address.
|
||||
pub fn connect(self: Socket, address: Socket.Address) !void {
|
||||
return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||
}
|
||||
|
||||
/// Binds the socket to an address.
|
||||
pub fn bind(self: Socket, address: Socket.Address) !void {
|
||||
return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||
}
|
||||
/// Accept a pending incoming connection queued to the kernel backlog
|
||||
/// of the socket.
|
||||
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
|
||||
/// Start listening for incoming connections on the socket.
|
||||
pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
||||
return os.listen(self.fd, max_backlog_size);
|
||||
}
|
||||
const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) };
|
||||
const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
|
||||
/// Have the socket attempt to the connect to an address.
|
||||
pub fn connect(self: Socket, address: Socket.Address) !void {
|
||||
return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
|
||||
}
|
||||
return Socket.Connection.from(socket, socket_address);
|
||||
}
|
||||
|
||||
/// Accept a pending incoming connection queued to the kernel backlog
|
||||
/// of the socket.
|
||||
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
/// Read data from the socket into the buffer provided with a set of flags
|
||||
/// specified. It returns the number of bytes read into the buffer provided.
|
||||
pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
|
||||
return os.recv(self.fd, buf, flags);
|
||||
}
|
||||
|
||||
const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, flags) };
|
||||
const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
/// Write a buffer of data provided to the socket with a set of flags specified.
|
||||
/// It returns the number of bytes that are written to the socket.
|
||||
pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
|
||||
return os.send(self.fd, buf, flags);
|
||||
}
|
||||
|
||||
return Socket.Connection.from(socket, socket_address);
|
||||
}
|
||||
/// Writes multiple I/O vectors with a prepended message header to the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that are
|
||||
/// written to the socket.
|
||||
pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
|
||||
return os.sendmsg(self.fd, msg, flags);
|
||||
}
|
||||
|
||||
/// Read data from the socket into the buffer provided with a set of flags
|
||||
/// specified. It returns the number of bytes read into the buffer provided.
|
||||
pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
|
||||
return os.recv(self.fd, buf, flags);
|
||||
}
|
||||
/// Read multiple I/O vectors with a prepended message header from the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that were
|
||||
/// read into the buffer provided.
|
||||
pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
|
||||
if (comptime @hasDecl(os.system, "recvmsg")) {
|
||||
while (true) {
|
||||
const rc = os.system.recvmsg(self.fd, msg, flags);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => @intCast(usize, rc),
|
||||
os.EBADF => unreachable, // always a race condition
|
||||
os.EFAULT => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
os.ENOTCONN => unreachable,
|
||||
os.ENOTSOCK => unreachable,
|
||||
os.EINTR => continue,
|
||||
os.EAGAIN => error.WouldBlock,
|
||||
os.ENOMEM => error.SystemResources,
|
||||
os.ECONNREFUSED => error.ConnectionRefused,
|
||||
os.ECONNRESET => error.ConnectionResetByPeer,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
return error.NotSupported;
|
||||
}
|
||||
|
||||
/// Write a buffer of data provided to the socket with a set of flags specified.
|
||||
/// It returns the number of bytes that are written to the socket.
|
||||
pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
|
||||
return os.send(self.fd, buf, flags);
|
||||
}
|
||||
/// Query the address that the socket is locally bounded to.
|
||||
pub fn getLocalAddress(self: Socket) !Socket.Address {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Writes multiple I/O vectors with a prepended message header to the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that are
|
||||
/// written to the socket.
|
||||
pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
|
||||
return os.sendmsg(self.fd, msg, flags);
|
||||
}
|
||||
/// Query the address that the socket is connected to.
|
||||
pub fn getRemoteAddress(self: Socket) !Socket.Address {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Read multiple I/O vectors with a prepended message header from the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that were
|
||||
/// read into the buffer provided.
|
||||
pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
|
||||
return error.NotImplemented;
|
||||
}
|
||||
/// Query and return the latest cached error on the socket.
|
||||
pub fn getError(self: Socket) !void {
|
||||
return os.getsockoptError(self.fd);
|
||||
}
|
||||
|
||||
/// Query the address that the socket is locally bounded to.
|
||||
pub fn getLocalAddress(self: Socket) !Socket.Address {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
/// Query the read buffer size of the socket.
|
||||
pub fn getReadBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
|
||||
/// Query the address that the socket is connected to.
|
||||
pub fn getRemoteAddress(self: Socket) !Socket.Address {
|
||||
var address: os.sockaddr_storage = undefined;
|
||||
var address_len: u32 = @sizeOf(os.sockaddr_storage);
|
||||
try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
/// Query and return the latest cached error on the socket.
|
||||
pub fn getError(self: Socket) !void {
|
||||
return os.getsockoptError(self.fd);
|
||||
}
|
||||
/// Query the write buffer size of the socket.
|
||||
pub fn getWriteBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
|
||||
/// Query the read buffer size of the socket.
|
||||
pub fn getReadBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
/// Set a socket option.
|
||||
pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
|
||||
return os.setsockopt(self.fd, level, code, value);
|
||||
}
|
||||
|
||||
/// Query the write buffer size of the socket.
|
||||
pub fn getWriteBufferSize(self: Socket) !u32 {
|
||||
var value: u32 = undefined;
|
||||
var value_len: u32 = @sizeOf(u32);
|
||||
/// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
|
||||
/// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
|
||||
/// if the host does not support the option for a socket to linger around up until a timeout specified in
|
||||
/// seconds.
|
||||
pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
|
||||
if (comptime @hasDecl(os, "SO_LINGER")) {
|
||||
const settings = extern struct {
|
||||
l_onoff: c_int,
|
||||
l_linger: c_int,
|
||||
}{
|
||||
.l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)),
|
||||
.l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0,
|
||||
};
|
||||
|
||||
const rc = os.system.getsockopt(self.fd, os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&value), &value_len);
|
||||
return switch (os.errno(rc)) {
|
||||
0 => value,
|
||||
os.EBADF => error.BadFileDescriptor,
|
||||
os.EFAULT => error.InvalidAddressSpace,
|
||||
os.EINVAL => error.InvalidSocketOption,
|
||||
os.ENOPROTOOPT => error.UnknownSocketOption,
|
||||
os.ENOTSOCK => error.NotASocket,
|
||||
else => |err| os.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
|
||||
}
|
||||
|
||||
/// Set a socket option.
|
||||
pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
|
||||
return os.setsockopt(self.fd, level, code, value);
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
|
||||
/// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
|
||||
/// if the host does not support the option for a socket to linger around up until a timeout specified in
|
||||
/// seconds.
|
||||
pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
|
||||
if (comptime @hasDecl(os, "SO_LINGER")) {
|
||||
const settings = extern struct {
|
||||
l_onoff: c_int,
|
||||
l_linger: c_int,
|
||||
}{
|
||||
.l_onoff = @intCast(c_int, @boolToInt(timeout_seconds != null)),
|
||||
.l_linger = if (timeout_seconds) |seconds| @intCast(c_int, seconds) else 0,
|
||||
/// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
|
||||
/// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
|
||||
pub fn setKeepAlive(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_KEEPALIVE")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support sockets listening the same address.
|
||||
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEADDR")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not supports sockets listening on the same port.
|
||||
pub fn setReusePort(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEPORT")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Set the write buffer size of the socket.
|
||||
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// Set the read buffer size of the socket.
|
||||
pub fn setReadBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully written
|
||||
/// to its bound destination after a specified number of milliseconds. A subsequent write
|
||||
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
|
||||
pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_LINGER, mem.asBytes(&settings));
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully read
|
||||
/// from its bound destination after a specified number of milliseconds. A subsequent
|
||||
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
|
||||
/// exceeded.
|
||||
pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
/// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
|
||||
/// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
|
||||
pub fn setKeepAlive(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_KEEPALIVE")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support sockets listening the same address.
|
||||
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEADDR")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not supports sockets listening on the same port.
|
||||
pub fn setReusePort(self: Socket, enabled: bool) !void {
|
||||
if (comptime @hasDecl(os, "SO_REUSEPORT")) {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
return error.UnsupportedSocketOption;
|
||||
}
|
||||
|
||||
/// Set the write buffer size of the socket.
|
||||
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_SNDBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// Set the read buffer size of the socket.
|
||||
pub fn setReadBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_RCVBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully written
|
||||
/// to its bound destination after a specified number of milliseconds. A subsequent write
|
||||
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
|
||||
pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_SNDTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully read
|
||||
/// from its bound destination after a specified number of milliseconds. A subsequent
|
||||
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
|
||||
/// exceeded.
|
||||
pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
|
||||
const timeout = os.timeval{
|
||||
.tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
|
||||
.tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
|
||||
};
|
||||
|
||||
return self.setOption(os.SOL_SOCKET, os.SO_RCVTIMEO, mem.asBytes(&timeout));
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -13,386 +13,438 @@ const mem = std.mem;
|
||||
const windows = std.os.windows;
|
||||
const ws2_32 = windows.ws2_32;
|
||||
|
||||
pub const Socket = struct {
|
||||
/// Import in `Socket.Address` and `Socket.Connection`.
|
||||
pub usingnamespace @import("socket.zig").Mixin(Socket);
|
||||
pub fn Mixin(comptime Socket: type) type {
|
||||
return struct {
|
||||
/// Open a new socket.
|
||||
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
|
||||
var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);
|
||||
|
||||
/// The underlying handle of a socket.
|
||||
fd: os.socket_t,
|
||||
var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
|
||||
if (socket_type & os.SOCK_CLOEXEC != 0) {
|
||||
filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
|
||||
}
|
||||
|
||||
/// Open a new socket.
|
||||
pub fn init(domain: u32, socket_type: u32, protocol: u32) !Socket {
|
||||
var filtered_socket_type = socket_type & ~@as(u32, os.SOCK_CLOEXEC);
|
||||
const fd = ws2_32.WSASocketW(
|
||||
@intCast(i32, domain),
|
||||
@intCast(i32, filtered_socket_type),
|
||||
@intCast(i32, protocol),
|
||||
null,
|
||||
0,
|
||||
filtered_flags,
|
||||
);
|
||||
if (fd == ws2_32.INVALID_SOCKET) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => {
|
||||
_ = try windows.WSAStartup(2, 2);
|
||||
return Socket.init(domain, socket_type, protocol);
|
||||
},
|
||||
.WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
|
||||
.WSAEMFILE => error.ProcessFdQuotaExceeded,
|
||||
.WSAENOBUFS => error.SystemResources,
|
||||
.WSAEPROTONOSUPPORT => error.ProtocolNotSupported,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
var filtered_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
|
||||
if (socket_type & os.SOCK_CLOEXEC != 0) {
|
||||
filtered_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
|
||||
return Socket{ .fd = fd };
|
||||
}
|
||||
|
||||
const fd = ws2_32.WSASocketW(
|
||||
@intCast(i32, domain),
|
||||
@intCast(i32, filtered_socket_type),
|
||||
@intCast(i32, protocol),
|
||||
null,
|
||||
0,
|
||||
filtered_flags,
|
||||
);
|
||||
if (fd == ws2_32.INVALID_SOCKET) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => {
|
||||
_ = try windows.WSAStartup(2, 2);
|
||||
return Socket.init(domain, socket_type, protocol);
|
||||
},
|
||||
.WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
|
||||
.WSAEMFILE => error.ProcessFdQuotaExceeded,
|
||||
.WSAENOBUFS => error.SystemResources,
|
||||
.WSAEPROTONOSUPPORT => error.ProtocolNotSupported,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
/// Closes the socket.
|
||||
pub fn deinit(self: Socket) void {
|
||||
_ = ws2_32.closesocket(self.fd);
|
||||
}
|
||||
|
||||
/// Shutdown either the read side, write side, or all side of the socket.
|
||||
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
||||
const rc = ws2_32.shutdown(self.fd, switch (how) {
|
||||
.recv => ws2_32.SD_RECEIVE,
|
||||
.send => ws2_32.SD_SEND,
|
||||
.both => ws2_32.SD_BOTH,
|
||||
});
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => return error.ConnectionAborted,
|
||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
||||
.WSAEINVAL => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOTCONN => return error.SocketNotConnected,
|
||||
.WSAENOTSOCK => unreachable,
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds the socket to an address.
|
||||
pub fn bind(self: Socket, address: Socket.Address) !void {
|
||||
const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAEACCES => error.AccessDenied,
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEADDRNOTAVAIL => error.AddressNotAvailable,
|
||||
.WSAEFAULT => error.BadAddress,
|
||||
.WSAEINPROGRESS => error.WouldBlock,
|
||||
.WSAEINVAL => error.AlreadyBound,
|
||||
.WSAENOBUFS => error.NoEphemeralPortsAvailable,
|
||||
.WSAENOTSOCK => error.NotASocket,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Start listening for incoming connections on the socket.
|
||||
pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
||||
const rc = ws2_32.listen(self.fd, max_backlog_size);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEISCONN => error.AlreadyConnected,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMFILE, .WSAENOBUFS => error.SystemResources,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAEINPROGRESS => error.WouldBlock,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Have the socket attempt to the connect to an address.
|
||||
pub fn connect(self: Socket, address: Socket.Address) !void {
|
||||
const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEADDRNOTAVAIL => error.AddressNotAvailable,
|
||||
.WSAECONNREFUSED => error.ConnectionRefused,
|
||||
.WSAETIMEDOUT => error.ConnectionTimedOut,
|
||||
.WSAEFAULT => error.BadAddress,
|
||||
.WSAEINVAL => error.ListeningSocket,
|
||||
.WSAEISCONN => error.AlreadyConnected,
|
||||
.WSAENOTSOCK => error.NotASocket,
|
||||
.WSAEACCES => error.BroadcastNotEnabled,
|
||||
.WSAENOBUFS => error.SystemResources,
|
||||
.WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
|
||||
.WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock,
|
||||
.WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept a pending incoming connection queued to the kernel backlog
|
||||
/// of the socket.
|
||||
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.INVALID_SOCKET) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAEINVAL => error.SocketNotListening,
|
||||
.WSAEMFILE => error.ProcessFdQuotaExceeded,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOBUFS => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAEWOULDBLOCK => error.WouldBlock,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
const socket = Socket.from(rc);
|
||||
const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
|
||||
|
||||
return Socket.Connection.from(socket, socket_address);
|
||||
}
|
||||
|
||||
/// Read data from the socket into the buffer provided with a set of flags
|
||||
/// specified. It returns the number of bytes read into the buffer provided.
|
||||
pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
|
||||
var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
|
||||
var num_bytes: u32 = undefined;
|
||||
var flags_ = flags;
|
||||
|
||||
const rc = ws2_32.WSARecv(self.fd, bufs, 1, &num_bytes, &flags_, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEDISCON => error.ConnectionClosedByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return @intCast(usize, num_bytes);
|
||||
}
|
||||
|
||||
/// Write a buffer of data provided to the socket with a set of flags specified.
|
||||
/// It returns the number of bytes that are written to the socket.
|
||||
pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
|
||||
var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = @intToPtr([*]u8, @ptrToInt(buf.ptr)) }};
|
||||
var num_bytes: u32 = undefined;
|
||||
|
||||
const rc = ws2_32.WSASend(self.fd, bufs, 1, &num_bytes, flags, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOBUFS => error.BufferDeadlock,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return @intCast(usize, num_bytes);
|
||||
}
|
||||
|
||||
/// Writes multiple I/O vectors with a prepended message header to the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that are
|
||||
/// written to the socket.
|
||||
pub fn writeVectorized(self: Socket, msg: ws2_32.msghdr_const, flags: u32) !usize {
|
||||
const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);
|
||||
|
||||
var num_bytes: u32 = undefined;
|
||||
|
||||
const rc = call(self.fd, &msg, flags, &num_bytes, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOBUFS => error.BufferDeadlock,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return @intCast(usize, num_bytes);
|
||||
}
|
||||
|
||||
/// Read multiple I/O vectors with a prepended message header from the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that were
|
||||
/// read into the buffer provided.
|
||||
pub fn readVectorized(self: Socket, msg: *ws2_32.msghdr, flags: u32) !usize {
|
||||
const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);
|
||||
|
||||
var num_bytes: u32 = undefined;
|
||||
|
||||
const rc = call(self.fd, msg, &num_bytes, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEDISCON => error.ConnectionClosedByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return @intCast(usize, num_bytes);
|
||||
}
|
||||
|
||||
/// Query the address that the socket is locally bounded to.
|
||||
pub fn getLocalAddress(self: Socket) !Socket.Address {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Query the address that the socket is connected to.
|
||||
pub fn getRemoteAddress(self: Socket) !Socket.Address {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
|
||||
return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Query and return the latest cached error on the socket.
|
||||
pub fn getError(self: Socket) !void {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Query the read buffer size of the socket.
|
||||
pub fn getReadBufferSize(self: Socket) !u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Query the write buffer size of the socket.
|
||||
pub fn getWriteBufferSize(self: Socket) !u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Set a socket option.
|
||||
pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
|
||||
const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => return error.SocketNotBound,
|
||||
.WSAENOTCONN => return error.SocketNotConnected,
|
||||
.WSAESHUTDOWN => return error.AlreadyShutdown,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
|
||||
/// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
|
||||
/// if the host does not support the option for a socket to linger around up until a timeout specified in
|
||||
/// seconds.
|
||||
pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
|
||||
const settings = ws2_32.linger{
|
||||
.l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
|
||||
.l_linger = if (timeout_seconds) |seconds| seconds else 0,
|
||||
};
|
||||
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
|
||||
}
|
||||
|
||||
return Socket{ .fd = fd };
|
||||
}
|
||||
|
||||
/// Enclose a socket abstraction over an existing socket file descriptor.
|
||||
pub fn from(fd: os.socket_t) Socket {
|
||||
return Socket{ .fd = fd };
|
||||
}
|
||||
|
||||
/// Closes the socket.
|
||||
pub fn deinit(self: Socket) void {
|
||||
_ = ws2_32.closesocket(self.fd);
|
||||
}
|
||||
|
||||
/// Shutdown either the read side, write side, or all side of the socket.
|
||||
pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
|
||||
const rc = ws2_32.shutdown(self.fd, switch (how) {
|
||||
.recv => ws2_32.SD_RECEIVE,
|
||||
.send => ws2_32.SD_SEND,
|
||||
.both => ws2_32.SD_BOTH,
|
||||
});
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => return error.ConnectionAborted,
|
||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
|
||||
.WSAEINVAL => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOTCONN => return error.SocketNotConnected,
|
||||
.WSAENOTSOCK => unreachable,
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Binds the socket to an address.
|
||||
pub fn bind(self: Socket, address: Socket.Address) !void {
|
||||
const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAEACCES => error.AccessDenied,
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEADDRNOTAVAIL => error.AddressNotAvailable,
|
||||
.WSAEFAULT => error.BadAddress,
|
||||
.WSAEINPROGRESS => error.WouldBlock,
|
||||
.WSAEINVAL => error.AlreadyBound,
|
||||
.WSAENOBUFS => error.NoEphemeralPortsAvailable,
|
||||
.WSAENOTSOCK => error.NotASocket,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Start listening for incoming connections on the socket.
|
||||
pub fn listen(self: Socket, max_backlog_size: u31) !void {
|
||||
const rc = ws2_32.listen(self.fd, max_backlog_size);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEISCONN => error.AlreadyConnected,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMFILE, .WSAENOBUFS => error.SystemResources,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAEINPROGRESS => error.WouldBlock,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Have the socket attempt to the connect to an address.
|
||||
pub fn connect(self: Socket, address: Socket.Address) !void {
|
||||
const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAEADDRINUSE => error.AddressInUse,
|
||||
.WSAEADDRNOTAVAIL => error.AddressNotAvailable,
|
||||
.WSAECONNREFUSED => error.ConnectionRefused,
|
||||
.WSAETIMEDOUT => error.ConnectionTimedOut,
|
||||
.WSAEFAULT => error.BadAddress,
|
||||
.WSAEINVAL => error.ListeningSocket,
|
||||
.WSAEISCONN => error.AlreadyConnected,
|
||||
.WSAENOTSOCK => error.NotASocket,
|
||||
.WSAEACCES => error.BroadcastNotEnabled,
|
||||
.WSAENOBUFS => error.SystemResources,
|
||||
.WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
|
||||
.WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock,
|
||||
.WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept a pending incoming connection queued to the kernel backlog
|
||||
/// of the socket.
|
||||
pub fn accept(self: Socket, flags: u32) !Socket.Connection {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.INVALID_SOCKET) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAEINVAL => error.SocketNotListening,
|
||||
.WSAEMFILE => error.ProcessFdQuotaExceeded,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOBUFS => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAEWOULDBLOCK => error.WouldBlock,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
|
||||
/// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
|
||||
pub fn setKeepAlive(self: Socket, enabled: bool) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
|
||||
const socket = Socket.from(rc);
|
||||
const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
|
||||
|
||||
return Socket.Connection.from(socket, socket_address);
|
||||
}
|
||||
|
||||
/// Read data from the socket into the buffer provided with a set of flags
|
||||
/// specified. It returns the number of bytes read into the buffer provided.
|
||||
pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
|
||||
var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
|
||||
var flags_ = flags;
|
||||
|
||||
const rc = ws2_32.WSARecv(self.fd, bufs, 1, null, &flags_, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEDISCON => error.ConnectionClosedByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support sockets listening the same address.
|
||||
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
|
||||
return @intCast(usize, rc);
|
||||
}
|
||||
|
||||
/// Write a buffer of data provided to the socket with a set of flags specified.
|
||||
/// It returns the number of bytes that are written to the socket.
|
||||
pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
|
||||
var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
|
||||
var flags_ = flags;
|
||||
|
||||
const rc = ws2_32.WSASend(self.fd, bufs, 1, null, &flags_, null, null);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSAECONNABORTED => error.ConnectionAborted,
|
||||
.WSAECONNRESET => error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => error.BadBuffer,
|
||||
.WSAEINPROGRESS,
|
||||
.WSAEWOULDBLOCK,
|
||||
.WSA_IO_PENDING,
|
||||
.WSAETIMEDOUT,
|
||||
=> error.WouldBlock,
|
||||
.WSAEINTR => error.Cancelled,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
.WSAEMSGSIZE => error.MessageTooLarge,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENETRESET => error.NetworkReset,
|
||||
.WSAENOBUFS => error.BufferDeadlock,
|
||||
.WSAENOTCONN => error.SocketNotConnected,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => error.OperationNotSupported,
|
||||
.WSAESHUTDOWN => error.AlreadyShutdown,
|
||||
.WSA_OPERATION_ABORTED => error.OperationAborted,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not supports sockets listening on the same port.
|
||||
///
|
||||
/// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior
|
||||
pub fn setReusePort(self: Socket, enabled: bool) !void {
|
||||
try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
try self.setReuseAddress(enabled);
|
||||
}
|
||||
|
||||
return @intCast(usize, rc);
|
||||
}
|
||||
|
||||
/// Writes multiple I/O vectors with a prepended message header to the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that are
|
||||
/// written to the socket.
|
||||
pub fn writeVectorized(self: Socket, msg: os.msghdr_const, flags: u32) !usize {
|
||||
return error.NotImplemented;
|
||||
}
|
||||
|
||||
/// Read multiple I/O vectors with a prepended message header from the socket
|
||||
/// with a set of flags specified. It returns the number of bytes that were
|
||||
/// read into the buffer provided.
|
||||
pub fn readVectorized(self: Socket, msg: *os.msghdr, flags: u32) !usize {
|
||||
return error.NotImplemented;
|
||||
}
|
||||
|
||||
/// Query the address that the socket is locally bounded to.
|
||||
pub fn getLocalAddress(self: Socket) !Socket.Address {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// Set the write buffer size of the socket.
|
||||
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Query the address that the socket is connected to.
|
||||
pub fn getRemoteAddress(self: Socket) !Socket.Address {
|
||||
var address: ws2_32.sockaddr_storage = undefined;
|
||||
var address_len: c_int = @sizeOf(ws2_32.sockaddr_storage);
|
||||
|
||||
const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENETDOWN => error.NetworkSubsystemFailed,
|
||||
.WSAENOTSOCK => error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => error.SocketNotBound,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// Set the read buffer size of the socket.
|
||||
pub fn setReadBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
|
||||
}
|
||||
|
||||
/// Query and return the latest cached error on the socket.
|
||||
pub fn getError(self: Socket) !void {
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Query the read buffer size of the socket.
|
||||
pub fn getReadBufferSize(self: Socket) !u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Query the write buffer size of the socket.
|
||||
pub fn getWriteBufferSize(self: Socket) !u32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Set a socket option.
|
||||
pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
|
||||
const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len));
|
||||
if (rc == ws2_32.SOCKET_ERROR) {
|
||||
return switch (ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => return error.SocketNotBound,
|
||||
.WSAENOTCONN => return error.SocketNotConnected,
|
||||
.WSAESHUTDOWN => return error.AlreadyShutdown,
|
||||
else => |err| windows.unexpectedWSAError(err),
|
||||
};
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully written
|
||||
/// to its bound destination after a specified number of milliseconds. A subsequent write
|
||||
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
|
||||
pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds));
|
||||
}
|
||||
}
|
||||
|
||||
/// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
|
||||
/// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
|
||||
/// if the host does not support the option for a socket to linger around up until a timeout specified in
|
||||
/// seconds.
|
||||
pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
|
||||
const settings = ws2_32.linger{
|
||||
.l_onoff = @as(u16, @boolToInt(timeout_seconds != null)),
|
||||
.l_linger = if (timeout_seconds) |seconds| seconds else 0,
|
||||
};
|
||||
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_LINGER, mem.asBytes(&settings));
|
||||
}
|
||||
|
||||
/// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
|
||||
/// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
|
||||
pub fn setKeepAlive(self: Socket, enabled: bool) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not support sockets listening the same address.
|
||||
pub fn setReuseAddress(self: Socket, enabled: bool) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
}
|
||||
|
||||
/// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
|
||||
/// the host does not supports sockets listening on the same port.
|
||||
///
|
||||
/// TODO: verify if this truly mimicks SO_REUSEPORT behavior, or if SO_REUSE_UNICASTPORT provides the correct behavior
|
||||
pub fn setReusePort(self: Socket, enabled: bool) !void {
|
||||
try self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled))));
|
||||
try self.setReuseAddress(enabled);
|
||||
}
|
||||
|
||||
/// Set the write buffer size of the socket.
|
||||
pub fn setWriteBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// Set the read buffer size of the socket.
|
||||
pub fn setReadBufferSize(self: Socket, size: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVBUF, mem.asBytes(&size));
|
||||
}
|
||||
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully written
|
||||
/// to its bound destination after a specified number of milliseconds. A subsequent write
|
||||
/// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
|
||||
pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_SNDTIMEO, mem.asBytes(&milliseconds));
|
||||
}
|
||||
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully read
|
||||
/// from its bound destination after a specified number of milliseconds. A subsequent
|
||||
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
|
||||
/// exceeded.
|
||||
pub fn setReadTimeout(self: Socket, milliseconds: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds));
|
||||
}
|
||||
};
|
||||
/// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
|
||||
/// set on a non-blocking socket.
|
||||
///
|
||||
/// Set a timeout on the socket that is to occur if no messages are successfully read
|
||||
/// from its bound destination after a specified number of milliseconds. A subsequent
|
||||
/// read from the socket will thereafter return `error.WouldBlock` should the timeout be
|
||||
/// exceeded.
|
||||
pub fn setReadTimeout(self: Socket, milliseconds: u32) !void {
|
||||
return self.setOption(ws2_32.SOL_SOCKET, ws2_32.SO_RCVTIMEO, mem.asBytes(&milliseconds));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user