Made some changes and additions to the networking to make it work on windows.

This commit is contained in:
Bas van den Berg 2020-09-02 23:53:29 +02:00
parent 12ce6eb8f6
commit d80554cedf
4 changed files with 201 additions and 110 deletions

View File

@ -2657,14 +2657,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
@ -2741,24 +2734,34 @@ pub const BindError = error{
} || 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()) {
// TODO: handle errors
else => |err| return windows.unexpectedWSAError(err),
}
}
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),
}
}
}
@ -2777,15 +2780,25 @@ const ListenError = error{
OperationNotSupported,
} || 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()) {
// TODO: handle errors
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),
}
}
}
@ -2822,19 +2835,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
@ -2842,46 +2855,58 @@ 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()) {
// TODO: handle errors
else => |err| return windows.unexpectedWSAError(err),
}
return fd;
},
EINTR => continue,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(sockfd);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // always a race condition
ECONNABORTED => return error.ConnectionAborted,
EFAULT => unreachable,
EINVAL => unreachable,
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),
break rc;
}
} else {
switch (errno(rc)) {
0 => {
break @intCast(socket_t, rc);
},
EINTR => continue,
EAGAIN => if (std.event.Loop.instance) |loop| {
loop.waitUntilFdReadable(sock);
continue;
} else {
return error.WouldBlock;
},
EBADF => unreachable, // always a race condition
ECONNABORTED => return error.ConnectionAborted,
EFAULT => unreachable,
EINVAL => unreachable,
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{
@ -2997,16 +3022,27 @@ pub const GetSockNameError = error{
SystemResources,
} || 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()) {
// TODO: handle errors
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 => unreachable,
ENOBUFS => return error.SystemResources,
}
}
}
@ -3052,9 +3088,9 @@ pub const ConnectError = error{
} || UnexpectedError;
/// Initiate a connection on a socket.
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, len);
if (rc == 0) return;
switch (windows.ws2_32.WSAGetLastError()) {
.WSAEADDRINUSE => return error.AddressInUse,
@ -3077,7 +3113,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,
@ -3086,8 +3122,8 @@ pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) Con
EAFNOSUPPORT => return error.AddressFamilyNotSupported,
EAGAIN, EINPROGRESS => {
const loop = std.event.Loop.instance orelse return error.WouldBlock;
loop.waitUntilFdWritable(sockfd);
return getsockoptError(sockfd);
loop.waitUntilFdWritable(sock);
return getsockoptError(sock);
},
EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
EBADF => unreachable, // sockfd is not a valid open file descriptor.
@ -3928,32 +3964,46 @@ 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()) {
// TODO: handle 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,
};
}
}
}

View File

@ -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;

View File

@ -1153,6 +1153,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, 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 => {},
@ -1163,6 +1171,25 @@ 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));
if (namelen) |name_length| {
var os_namelen: c_int = name_length.*;
const sock = ws2_32.accept(s, name, &os_namelen);
name_length.* = @intCast(ws2_32.socklen_t, os_namelen);
return sock;
} else {
return ws2_32.accept(s, null, null);
}
}
pub fn getsockname(s: ws2_32.SOCKET, name: *ws2_32.sockaddr, namelen: *ws2_32.socklen_t) i32 {
var os_namelen: c_int = namelen.*;
const rc = ws2_32.getsockname(s, name, &os_namelen);
namelen.* = @intCast(ws2_32.socklen_t, os_namelen);
return rc;
}
pub fn WSAIoctl(
s: ws2_32.SOCKET,
dwIoControlCode: DWORD,

View File

@ -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 = u31;
pub const AF_UNSPEC = 0;
pub const AF_UNIX = 1;
@ -734,12 +734,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,
@ -795,3 +804,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;