Merge branch 'BarabasGitHub-improve-windows-networking'
commit
59c26c79e8
481
lib/std/os.zig
481
lib/std/os.zig
|
@ -2646,14 +2646,7 @@ pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t
|
|||
// NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations
|
||||
const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0;
|
||||
const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type), @intCast(c_int, protocol), null, 0, flags);
|
||||
if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
|
||||
.WSAENOBUFS => return error.SystemResources,
|
||||
.WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
|
||||
.WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
};
|
||||
const rc = try windows.WSASocketW(@bitCast(i32, domain), @bitCast(i32, filtered_sock_type), @bitCast(i32, protocol), null, 0, flags);
|
||||
errdefer windows.closesocket(rc) catch unreachable;
|
||||
if ((socket_type & SOCK_NONBLOCK) != 0) {
|
||||
var mode: c_ulong = 1; // nonblocking
|
||||
|
@ -2727,28 +2720,55 @@ pub const BindError = error{
|
|||
|
||||
/// The socket inode would reside on a read-only filesystem.
|
||||
ReadOnlyFileSystem,
|
||||
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
FileDescriptorNotASocket,
|
||||
|
||||
AlreadyBound,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// addr is `*const T` where T is one of the sockaddr
|
||||
pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
|
||||
const rc = system.bind(sockfd, addr, len);
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EADDRINUSE => return error.AddressInUse,
|
||||
EBADF => unreachable, // always a race condition if this error is returned
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
ENOTSOCK => unreachable, // invalid `sockfd`
|
||||
EADDRNOTAVAIL => return error.AddressNotAvailable,
|
||||
EFAULT => unreachable, // invalid `addr` pointer
|
||||
ELOOP => return error.SymLinkLoop,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOTDIR => return error.NotDir,
|
||||
EROFS => return error.ReadOnlyFileSystem,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
|
||||
const rc = system.bind(sock, addr, len);
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable, // not initialized WSA
|
||||
.WSAEACCES => return error.AccessDenied,
|
||||
.WSAEADDRINUSE => return error.AddressInUse,
|
||||
.WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
.WSAEFAULT => unreachable, // invalid pointers
|
||||
.WSAEINVAL => return error.AlreadyBound,
|
||||
.WSAENOBUFS => return error.SystemResources,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EADDRINUSE => return error.AddressInUse,
|
||||
EBADF => unreachable, // always a race condition if this error is returned
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
ENOTSOCK => unreachable, // invalid `sockfd`
|
||||
EADDRNOTAVAIL => return error.AddressNotAvailable,
|
||||
EFAULT => unreachable, // invalid `addr` pointer
|
||||
ELOOP => return error.SymLinkLoop,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOTDIR => return error.NotDir,
|
||||
EROFS => return error.ReadOnlyFileSystem,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const ListenError = error{
|
||||
|
@ -2764,23 +2784,57 @@ const ListenError = error{
|
|||
|
||||
/// The socket is not of a type that supports the listen() operation.
|
||||
OperationNotSupported,
|
||||
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
/// Ran out of system resources
|
||||
/// On Windows it can either run out of socket descriptors or buffer space
|
||||
SystemResources,
|
||||
|
||||
/// Already connected
|
||||
AlreadyConnected,
|
||||
|
||||
/// Socket has not been bound yet
|
||||
SocketNotBound,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void {
|
||||
const rc = system.listen(sockfd, backlog);
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
EADDRINUSE => return error.AddressInUse,
|
||||
EBADF => unreachable,
|
||||
ENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
EOPNOTSUPP => return error.OperationNotSupported,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
|
||||
const rc = system.listen(sock, backlog);
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable, // not initialized WSA
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAEADDRINUSE => return error.AddressInUse,
|
||||
.WSAEISCONN => return error.AlreadyConnected,
|
||||
.WSAEINVAL => return error.SocketNotBound,
|
||||
.WSAEMFILE, .WSAENOBUFS => return error.SystemResources,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => return error.OperationNotSupported,
|
||||
.WSAEINPROGRESS => unreachable,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
EADDRINUSE => return error.AddressInUse,
|
||||
EBADF => unreachable,
|
||||
ENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
EOPNOTSUPP => return error.OperationNotSupported,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const AcceptError = error{
|
||||
ConnectionAborted,
|
||||
|
||||
/// The file descriptor sockfd does not refer to a socket.
|
||||
FileDescriptorNotASocket,
|
||||
|
||||
/// The per-process limit on the number of open file descriptors has been reached.
|
||||
ProcessFdQuotaExceeded,
|
||||
|
||||
|
@ -2806,6 +2860,16 @@ pub const AcceptError = error{
|
|||
/// Permission to create a socket of the specified type and/or
|
||||
/// protocol is denied.
|
||||
PermissionDenied,
|
||||
|
||||
/// An incoming connection was indicated, but was subsequently terminated by the
|
||||
/// remote peer prior to accepting the call.
|
||||
ConnectionResetByPeer,
|
||||
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
/// The referenced socket is not a type that supports connection-oriented service.
|
||||
OperationNotSupported,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Accept a connection on a socket.
|
||||
|
@ -2814,19 +2878,19 @@ pub const AcceptError = error{
|
|||
pub fn accept(
|
||||
/// This argument is a socket that has been created with `socket`, bound to a local address
|
||||
/// with `bind`, and is listening for connections after a `listen`.
|
||||
sockfd: fd_t,
|
||||
sock: socket_t,
|
||||
/// This argument is a pointer to a sockaddr structure. This structure is filled in with the
|
||||
/// address of the peer socket, as known to the communications layer. The exact format of the
|
||||
/// address returned addr is determined by the socket's address family (see `socket` and the
|
||||
/// respective protocol man pages).
|
||||
addr: *sockaddr,
|
||||
addr: ?*sockaddr,
|
||||
/// This argument is a value-result argument: the caller must initialize it to contain the
|
||||
/// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
|
||||
/// of the peer address.
|
||||
///
|
||||
/// The returned address is truncated if the buffer provided is too small; in this case, `addr_size`
|
||||
/// will return a value greater than was supplied to the call.
|
||||
addr_size: *socklen_t,
|
||||
addr_size: ?*socklen_t,
|
||||
/// The following values can be bitwise ORed in flags to obtain different behavior:
|
||||
/// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
|
||||
/// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve
|
||||
|
@ -2834,41 +2898,61 @@ pub fn accept(
|
|||
/// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the
|
||||
/// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
|
||||
flags: u32,
|
||||
) AcceptError!fd_t {
|
||||
const have_accept4 = comptime !std.Target.current.isDarwin();
|
||||
) AcceptError!socket_t {
|
||||
const have_accept4 = comptime !(std.Target.current.isDarwin() or builtin.os.tag == .windows);
|
||||
assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s)
|
||||
|
||||
while (true) {
|
||||
const accepted_sock = while (true) {
|
||||
const rc = if (have_accept4)
|
||||
system.accept4(sockfd, addr, addr_size, flags)
|
||||
system.accept4(sock, addr, addr_size, flags)
|
||||
else
|
||||
system.accept(sockfd, addr, addr_size);
|
||||
system.accept(sock, addr, addr_size);
|
||||
|
||||
switch (errno(rc)) {
|
||||
0 => {
|
||||
const fd = @intCast(fd_t, rc);
|
||||
if (!have_accept4) {
|
||||
try setSockFlags(fd, flags);
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.INVALID_SOCKET) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable, // not initialized WSA
|
||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||
.WSAEFAULT => unreachable,
|
||||
.WSAEINVAL => return error.SocketNotListening,
|
||||
.WSAEMFILE => return error.ProcessFdQuotaExceeded,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOBUFS => return error.FileDescriptorNotASocket,
|
||||
.WSAEOPNOTSUPP => return error.OperationNotSupported,
|
||||
.WSAEWOULDBLOCK => return error.WouldBlock,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
return fd;
|
||||
},
|
||||
EINTR => continue,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
EBADF => unreachable, // always a race condition
|
||||
ECONNABORTED => return error.ConnectionAborted,
|
||||
EFAULT => unreachable,
|
||||
EINVAL => return error.SocketNotListening,
|
||||
ENOTSOCK => unreachable,
|
||||
EMFILE => return error.ProcessFdQuotaExceeded,
|
||||
ENFILE => return error.SystemFdQuotaExceeded,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
EOPNOTSUPP => unreachable,
|
||||
EPROTO => return error.ProtocolFailure,
|
||||
EPERM => return error.BlockedByFirewall,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
} else {
|
||||
break rc;
|
||||
}
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => {
|
||||
break @intCast(socket_t, rc);
|
||||
},
|
||||
EINTR => continue,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
EBADF => unreachable, // always a race condition
|
||||
ECONNABORTED => return error.ConnectionAborted,
|
||||
EFAULT => unreachable,
|
||||
EINVAL => return error.SocketNotListening,
|
||||
ENOTSOCK => unreachable,
|
||||
EMFILE => return error.ProcessFdQuotaExceeded,
|
||||
ENFILE => return error.SystemFdQuotaExceeded,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
EOPNOTSUPP => unreachable,
|
||||
EPROTO => return error.ProtocolFailure,
|
||||
EPERM => return error.BlockedByFirewall,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
} else unreachable;
|
||||
|
||||
if (!have_accept4) {
|
||||
try setSockFlags(accepted_sock, flags);
|
||||
}
|
||||
return accepted_sock;
|
||||
}
|
||||
|
||||
pub const EpollCreateError = error{
|
||||
|
@ -2982,18 +3066,41 @@ pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
|
|||
pub const GetSockNameError = error{
|
||||
/// Insufficient resources were available in the system to perform the operation.
|
||||
SystemResources,
|
||||
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
/// Socket hasn't been bound yet
|
||||
SocketNotBound,
|
||||
|
||||
FileDescriptorNotASocket,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
|
||||
switch (errno(system.getsockname(sockfd, addr, addrlen))) {
|
||||
0 => return,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
|
||||
const rc = system.getsockname(sock, addr, addrlen);
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
.WSAEINVAL => return error.SocketNotBound,
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
|
||||
EBADF => unreachable, // always a race condition
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
ENOTSOCK => unreachable,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
EBADF => unreachable, // always a race condition
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
ENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3041,9 +3148,9 @@ pub const ConnectError = error{
|
|||
/// Initiate a connection on a socket.
|
||||
/// If `sockfd` is opened in non blocking mode, the function will
|
||||
/// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
|
||||
pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
|
||||
pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
|
||||
const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(i32, len));
|
||||
if (rc == 0) return;
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSAEADDRINUSE => return error.AddressInUse,
|
||||
|
@ -3066,7 +3173,7 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
|
|||
}
|
||||
|
||||
while (true) {
|
||||
switch (errno(system.connect(sockfd, sock_addr, len))) {
|
||||
switch (errno(system.connect(sock, sock_addr, len))) {
|
||||
0 => return,
|
||||
EACCES => return error.PermissionDenied,
|
||||
EPERM => return error.PermissionDenied,
|
||||
|
@ -3921,32 +4028,49 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
|
|||
}
|
||||
}
|
||||
|
||||
fn setSockFlags(fd: fd_t, flags: u32) !void {
|
||||
fn setSockFlags(sock: socket_t, flags: u32) !void {
|
||||
if ((flags & SOCK_CLOEXEC) != 0) {
|
||||
var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fd_flags |= FD_CLOEXEC;
|
||||
_ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (builtin.os.tag == .windows) {
|
||||
// TODO: Find out if this is supported for sockets
|
||||
} else {
|
||||
var fd_flags = fcntl(sock, F_GETFD, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fd_flags |= FD_CLOEXEC;
|
||||
_ = fcntl(sock, F_SETFD, fd_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
if ((flags & SOCK_NONBLOCK) != 0) {
|
||||
var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fl_flags |= O_NONBLOCK;
|
||||
_ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
if (builtin.os.tag == .windows) {
|
||||
var mode: c_ulong = 1;
|
||||
if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
// TODO: handle more errors
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var fl_flags = fcntl(sock, F_GETFL, 0) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
fl_flags |= O_NONBLOCK;
|
||||
_ = fcntl(sock, F_SETFL, fl_flags) catch |err| switch (err) {
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4544,6 +4668,8 @@ pub const SendError = error{
|
|||
/// The local end has been shut down on a connection oriented socket. In this case, the
|
||||
/// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
|
||||
BrokenPipe,
|
||||
|
||||
FileDescriptorNotASocket,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Transmit a message to another socket.
|
||||
|
@ -4573,7 +4699,7 @@ pub const SendError = error{
|
|||
/// possible to send more data.
|
||||
pub fn sendto(
|
||||
/// The file descriptor of the sending socket.
|
||||
sockfd: fd_t,
|
||||
sockfd: socket_t,
|
||||
/// Message to send.
|
||||
buf: []const u8,
|
||||
flags: u32,
|
||||
|
@ -4582,26 +4708,43 @@ pub fn sendto(
|
|||
) SendError!usize {
|
||||
while (true) {
|
||||
const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EACCES => return error.AccessDenied,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
EALREADY => return error.FastOpenAlreadyInProgress,
|
||||
EBADF => unreachable, // always a race condition
|
||||
ECONNRESET => return error.ConnectionResetByPeer,
|
||||
EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
|
||||
EFAULT => unreachable, // An invalid user space address was specified for an argument.
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable, // Invalid argument passed.
|
||||
EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
|
||||
EMSGSIZE => return error.MessageTooBig,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
|
||||
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
|
||||
EPIPE => return error.BrokenPipe,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSAEACCES => return error.AccessDenied,
|
||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||
.WSAEMSGSIZE => return error.MessageTooBig,
|
||||
.WSAENOBUFS => return error.SystemResources,
|
||||
.WSAENOTSOCK => return error.FileDescriptorNotASocket,
|
||||
// TODO: handle more errors
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
} else {
|
||||
return @intCast(usize, rc);
|
||||
}
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
|
||||
EACCES => return error.AccessDenied,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
EALREADY => return error.FastOpenAlreadyInProgress,
|
||||
EBADF => unreachable, // always a race condition
|
||||
ECONNRESET => return error.ConnectionResetByPeer,
|
||||
EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
|
||||
EFAULT => unreachable, // An invalid user space address was specified for an argument.
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable, // Invalid argument passed.
|
||||
EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
|
||||
EMSGSIZE => return error.MessageTooBig,
|
||||
ENOBUFS => return error.SystemResources,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
|
||||
ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
|
||||
EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
|
||||
EPIPE => return error.BrokenPipe,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4627,7 +4770,7 @@ pub fn sendto(
|
|||
/// possible to send more data.
|
||||
pub fn send(
|
||||
/// The file descriptor of the sending socket.
|
||||
sockfd: fd_t,
|
||||
sockfd: socket_t,
|
||||
buf: []const u8,
|
||||
flags: u32,
|
||||
) SendError!usize {
|
||||
|
@ -5042,6 +5185,9 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len
|
|||
}
|
||||
|
||||
pub const PollError = error{
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
/// The kernel had no space to allocate file descriptor tables.
|
||||
SystemResources,
|
||||
} || UnexpectedError;
|
||||
|
@ -5049,14 +5195,29 @@ pub const PollError = error{
|
|||
pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
|
||||
while (true) {
|
||||
const rc = system.poll(fds.ptr, fds.len, timeout);
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EFAULT => unreachable,
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable,
|
||||
ENOMEM => return error.SystemResources,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOBUFS => return error.SystemResources,
|
||||
// TODO: handle more errors
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
} else {
|
||||
return @intCast(usize, rc);
|
||||
}
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EFAULT => unreachable,
|
||||
EINTR => continue,
|
||||
EINVAL => unreachable,
|
||||
ENOMEM => return error.SystemResources,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5071,12 +5232,30 @@ pub const RecvFromError = error{
|
|||
|
||||
/// Could not allocate kernel memory.
|
||||
SystemResources,
|
||||
|
||||
ConnectionResetByPeer,
|
||||
|
||||
/// The socket has not been bound.
|
||||
SocketNotBound,
|
||||
|
||||
/// The UDP message was too big for the buffer and part of it has been discarded
|
||||
MessageTooBig,
|
||||
|
||||
/// The network subsystem has failed.
|
||||
NetworkSubsystemFailed,
|
||||
|
||||
/// The socket is not connected (connection-oriented sockets only).
|
||||
SocketNotConnected,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
|
||||
return recvfrom(sock, buf, flags, null, null);
|
||||
}
|
||||
|
||||
/// If `sockfd` is opened in non blocking mode, the function will
|
||||
/// return error.WouldBlock when EAGAIN is received.
|
||||
pub fn recvfrom(
|
||||
sockfd: fd_t,
|
||||
sockfd: socket_t,
|
||||
buf: []u8,
|
||||
flags: u32,
|
||||
src_addr: ?*sockaddr,
|
||||
|
@ -5084,18 +5263,36 @@ pub fn recvfrom(
|
|||
) RecvFromError!usize {
|
||||
while (true) {
|
||||
const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EBADF => unreachable, // always a race condition
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable,
|
||||
ENOTCONN => unreachable,
|
||||
ENOTSOCK => unreachable,
|
||||
EINTR => continue,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ECONNREFUSED => return error.ConnectionRefused,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
if (builtin.os.tag == .windows) {
|
||||
if (rc == windows.ws2_32.SOCKET_ERROR) {
|
||||
switch (windows.ws2_32.WSAGetLastError()) {
|
||||
.WSANOTINITIALISED => unreachable,
|
||||
.WSAECONNRESET => return error.ConnectionResetByPeer,
|
||||
.WSAEINVAL => return error.SocketNotBound,
|
||||
.WSAEMSGSIZE => return error.MessageTooBig,
|
||||
.WSAENETDOWN => return error.NetworkSubsystemFailed,
|
||||
.WSAENOTCONN => return error.SocketNotConnected,
|
||||
.WSAEWOULDBLOCK => return error.WouldBlock,
|
||||
// TODO: handle more errors
|
||||
else => |err| return windows.unexpectedWSAError(err),
|
||||
}
|
||||
} else {
|
||||
return @intCast(usize, rc);
|
||||
}
|
||||
} else {
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EBADF => unreachable, // always a race condition
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable,
|
||||
ENOTCONN => unreachable,
|
||||
ENOTSOCK => unreachable,
|
||||
EINTR => continue,
|
||||
EAGAIN => return error.WouldBlock,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ECONNREFUSED => return error.ConnectionRefused,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ pub const AT_REMOVEDIR = 0x200;
|
|||
|
||||
pub const in_port_t = u16;
|
||||
pub const sa_family_t = ws2_32.ADDRESS_FAMILY;
|
||||
pub const socklen_t = u32;
|
||||
pub const socklen_t = ws2_32.socklen_t;
|
||||
|
||||
pub const sockaddr = ws2_32.sockaddr;
|
||||
pub const sockaddr_in = ws2_32.sockaddr_in;
|
||||
|
@ -243,6 +243,19 @@ pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
|
|||
pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
|
||||
pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
|
||||
|
||||
pub const pollfd = ws2_32.pollfd;
|
||||
|
||||
pub const POLLRDNORM = ws2_32.POLLRDNORM;
|
||||
pub const POLLRDBAND = ws2_32.POLLRDBAND;
|
||||
pub const POLLIN = ws2_32.POLLIN;
|
||||
pub const POLLPRI = ws2_32.POLLPRI;
|
||||
pub const POLLWRNORM = ws2_32.POLLWRNORM;
|
||||
pub const POLLOUT = ws2_32.POLLOUT;
|
||||
pub const POLLWRBAND = ws2_32.POLLWRBAND;
|
||||
pub const POLLERR = ws2_32.POLLERR;
|
||||
pub const POLLHUP = ws2_32.POLLHUP;
|
||||
pub const POLLNVAL = ws2_32.POLLNVAL;
|
||||
|
||||
pub const O_RDONLY = 0o0;
|
||||
pub const O_WRONLY = 0o1;
|
||||
pub const O_RDWR = 0o2;
|
||||
|
|
|
@ -1157,6 +1157,14 @@ pub fn WSASocketW(
|
|||
return rc;
|
||||
}
|
||||
|
||||
pub fn bind(s: ws2_32.SOCKET, name: *const ws2_32.sockaddr, namelen: ws2_32.socklen_t) i32 {
|
||||
return ws2_32.bind(s, name, @intCast(i32, namelen));
|
||||
}
|
||||
|
||||
pub fn listen(s: ws2_32.SOCKET, backlog: u31) i32 {
|
||||
return ws2_32.listen(s, backlog);
|
||||
}
|
||||
|
||||
pub fn closesocket(s: ws2_32.SOCKET) !void {
|
||||
switch (ws2_32.closesocket(s)) {
|
||||
0 => {},
|
||||
|
@ -1167,6 +1175,40 @@ pub fn closesocket(s: ws2_32.SOCKET) !void {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn accept(s: ws2_32.SOCKET, name: ?*ws2_32.sockaddr, namelen: ?*ws2_32.socklen_t) ws2_32.SOCKET {
|
||||
assert((name == null) == (namelen == null));
|
||||
return ws2_32.accept(s, name, @ptrCast(?*i32, namelen));
|
||||
}
|
||||
|
||||
pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
|
||||
return ws2_32.getsockname(s, name, @ptrCast(*i32, namelen));
|
||||
}
|
||||
|
||||
pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
|
||||
var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) };
|
||||
var bytes_send: DWORD = undefined;
|
||||
if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) {
|
||||
return ws2_32.SOCKET_ERROR;
|
||||
} else {
|
||||
return @as(i32, @intCast(u31, bytes_send));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recvfrom(s: ws2_32.SOCKET, buf: [*]u8, len: usize, flags: u32, from: ?*ws2_32.sockaddr, from_len: ?*ws2_32.socklen_t) i32 {
|
||||
var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = buf };
|
||||
var bytes_received: DWORD = undefined;
|
||||
var flags_inout = flags;
|
||||
if (ws2_32.WSARecvFrom(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_received, &flags_inout, from, from_len, null, null) == ws2_32.SOCKET_ERROR) {
|
||||
return ws2_32.SOCKET_ERROR;
|
||||
} else {
|
||||
return @as(i32, @intCast(u31, bytes_received));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(fds: [*]ws2_32.pollfd, n: usize, timeout: i32) i32 {
|
||||
return ws2_32.WSAPoll(fds, @intCast(u32, n), timeout);
|
||||
}
|
||||
|
||||
pub fn WSAIoctl(
|
||||
s: ws2_32.SOCKET,
|
||||
dwIoControlCode: DWORD,
|
||||
|
|
|
@ -116,7 +116,7 @@ pub const WSAOVERLAPPED_COMPLETION_ROUTINE = fn (dwError: DWORD, cbTransferred:
|
|||
pub const ADDRESS_FAMILY = u16;
|
||||
|
||||
// Microsoft use the signed c_int for this, but it should never be negative
|
||||
const socklen_t = u32;
|
||||
pub const socklen_t = u32;
|
||||
|
||||
pub const AF_UNSPEC = 0;
|
||||
pub const AF_UNIX = 1;
|
||||
|
@ -234,6 +234,27 @@ pub const WSAMSG = extern struct {
|
|||
dwFlags: DWORD,
|
||||
};
|
||||
|
||||
pub const pollfd = extern struct {
|
||||
fd: SOCKET,
|
||||
events: SHORT,
|
||||
revents: SHORT,
|
||||
};
|
||||
|
||||
// Event flag definitions for WSAPoll().
|
||||
|
||||
pub const POLLRDNORM = 0x0100;
|
||||
pub const POLLRDBAND = 0x0200;
|
||||
pub const POLLIN = (POLLRDNORM | POLLRDBAND);
|
||||
pub const POLLPRI = 0x0400;
|
||||
|
||||
pub const POLLWRNORM = 0x0010;
|
||||
pub const POLLOUT = (POLLWRNORM);
|
||||
pub const POLLWRBAND = 0x0020;
|
||||
|
||||
pub const POLLERR = 0x0001;
|
||||
pub const POLLHUP = 0x0002;
|
||||
pub const POLLNVAL = 0x0004;
|
||||
|
||||
// https://docs.microsoft.com/en-au/windows/win32/winsock/windows-sockets-error-codes-2
|
||||
pub const WinsockError = extern enum(u16) {
|
||||
/// Specified event object handle is invalid.
|
||||
|
@ -734,12 +755,21 @@ pub extern "ws2_32" fn WSAIoctl(
|
|||
pub extern "ws2_32" fn accept(
|
||||
s: SOCKET,
|
||||
addr: ?*sockaddr,
|
||||
addrlen: socklen_t,
|
||||
addrlen: ?*c_int,
|
||||
) callconv(.Stdcall) SOCKET;
|
||||
pub extern "ws2_32" fn bind(
|
||||
s: SOCKET,
|
||||
addr: ?*const sockaddr,
|
||||
addrlen: c_int,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn connect(
|
||||
s: SOCKET,
|
||||
name: *const sockaddr,
|
||||
namelen: socklen_t,
|
||||
namelen: c_int,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn listen(
|
||||
s: SOCKET,
|
||||
backlog: c_int,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn WSARecv(
|
||||
s: SOCKET,
|
||||
|
@ -777,10 +807,15 @@ pub extern "ws2_32" fn WSASendTo(
|
|||
lpNumberOfBytesSent: ?*DWORD,
|
||||
dwFlags: DWORD,
|
||||
lpTo: ?*const sockaddr,
|
||||
iTolen: socklen_t,
|
||||
iTolen: c_int,
|
||||
lpOverlapped: ?*WSAOVERLAPPED,
|
||||
lpCompletionRoutine: ?WSAOVERLAPPED_COMPLETION_ROUTINE,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn WSAPoll(
|
||||
fdArray: [*]pollfd,
|
||||
fds: c_ulong,
|
||||
timeout: c_int,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn getaddrinfo(
|
||||
pNodeName: [*:0]const u8,
|
||||
pServiceName: [*:0]const u8,
|
||||
|
@ -795,3 +830,8 @@ pub extern "ws2_32" fn ioctlsocket(
|
|||
cmd: c_long,
|
||||
argp: *c_ulong,
|
||||
) callconv(.Stdcall) c_int;
|
||||
pub extern "ws2_32" fn getsockname(
|
||||
s: SOCKET,
|
||||
name: *sockaddr,
|
||||
namelen: *c_int,
|
||||
) callconv(.Stdcall) c_int;
|
||||
|
|
Loading…
Reference in New Issue