An HTTP/1.1 server for zig
0

Configure Feed

Select the types of activity you want to include in your feed.

http.zig / src / posix.zig
34 kB 833 lines
1const std = @import("std"); 2const builtin = @import("builtin"); 3const windows = @import("windows.zig"); 4 5const posix = std.posix; 6pub const system = posix.system; 7 8pub const O = system.O; 9pub const F = system.F; 10pub const AF = posix.AF; 11pub const SO = posix.SO; 12pub const SOL = posix.SOL; 13pub const SOCK = posix.SOCK; 14pub const fd_t = posix.fd_t; 15pub const socket_t = posix.socket_t; 16pub const timeval = posix.timeval; 17pub const IPPROTO = posix.IPPROTO; 18pub const sockaddr = posix.sockaddr; 19pub const timespec = posix.timespec; 20pub const socklen_t = posix.socklen_t; 21pub const Kevent = system.Kevent; 22 23const native_os = builtin.os.tag; 24 25/// WARNING: this flag is not supported by windows socket functions directly, 26/// it is only supported by `socket` below. Be sure that this value does 27/// not share any bits with any of the `SOCK` values. 28pub const CLOEXEC = if (native_os == .windows) 0x10000 else SOCK.CLOEXEC; 29pub const NONBLOCK = if (native_os == .windows) 0x20000 else SOCK.NONBLOCK; 30 31pub fn socket(domain: u32, socket_type: u32, protocol: u32) !socket_t { 32 if (native_os == .windows) { 33 // These flags are not actually part of the Windows API, instead they are converted here for compatibility 34 const filtered_sock_type = socket_type & ~@as(u32, NONBLOCK | CLOEXEC); 35 var flags: u32 = windows.ws2_32.WSA_FLAG_OVERLAPPED; 36 if ((socket_type & CLOEXEC) != 0) flags |= windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; 37 38 const rc = try windows.WSASocketW( 39 @bitCast(domain), 40 @bitCast(filtered_sock_type), 41 @bitCast(protocol), 42 null, 43 0, 44 flags, 45 ); 46 errdefer windows.closesocket(rc) catch unreachable; 47 if ((socket_type & NONBLOCK) != 0) { 48 var mode: c_ulong = 1; // nonblocking 49 if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) { 50 switch (windows.ws2_32.WSAGetLastError()) { 51 // have not identified any error codes that should be handled yet 52 else => unreachable, 53 } 54 } 55 } 56 return rc; 57 } 58 59 const have_sock_flags = !builtin.target.os.tag.isDarwin() and native_os != .haiku; 60 const filtered_sock_type = if (!have_sock_flags) 61 socket_type & ~@as(u32, NONBLOCK | CLOEXEC) 62 else 63 socket_type; 64 const rc = posix.system.socket(domain, filtered_sock_type, protocol); 65 switch (posix.errno(rc)) { 66 .SUCCESS => { 67 const fd: fd_t = @intCast(rc); 68 errdefer close(fd); 69 if (!have_sock_flags) { 70 try setSockFlags(fd, socket_type); 71 } 72 return fd; 73 }, 74 .ACCES => return error.AccessDenied, 75 .AFNOSUPPORT => return error.AddressFamilyNotSupported, 76 .INVAL => return error.ProtocolFamilyNotAvailable, 77 .MFILE => return error.ProcessFdQuotaExceeded, 78 .NFILE => return error.SystemFdQuotaExceeded, 79 .NOBUFS => return error.SystemResources, 80 .NOMEM => return error.SystemResources, 81 .PROTONOSUPPORT => return error.ProtocolNotSupported, 82 .PROTOTYPE => return error.SocketTypeNotSupported, 83 else => return error.Unexpected, 84 } 85} 86 87fn setSockFlags(sock: socket_t, flags: u32) !void { 88 if ((flags & CLOEXEC) != 0) { 89 if (native_os == .windows) { 90 // Windows: disable handle inheritance. This prevents child 91 // processes from inheriting the socket, equivalent to FD_CLOEXEC. 92 // Socket handles are HANDLE objects in Windows NT, so 93 // SetHandleInformation works on them. 94 try windows.setNoInherit(@ptrCast(sock)); 95 } else { 96 var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) { 97 error.FileBusy => unreachable, 98 error.Locked => unreachable, 99 error.PermissionDenied => unreachable, 100 error.DeadLock => unreachable, 101 error.LockedRegionLimitExceeded => unreachable, 102 else => |e| return e, 103 }; 104 fd_flags |= system.FD_CLOEXEC; 105 _ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) { 106 error.FileBusy => unreachable, 107 error.Locked => unreachable, 108 error.PermissionDenied => unreachable, 109 error.DeadLock => unreachable, 110 error.LockedRegionLimitExceeded => unreachable, 111 else => |e| return e, 112 }; 113 } 114 } 115 if ((flags & NONBLOCK) != 0) { 116 if (native_os == .windows) { 117 var mode: c_ulong = 1; 118 if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) { 119 switch (windows.ws2_32.WSAGetLastError()) { 120 .WSANOTINITIALISED => unreachable, 121 .WSAENETDOWN => return error.NetworkSubsystemFailed, 122 .WSAENOTSOCK => return error.FileDescriptorNotASocket, 123 .WSAEINVAL => return error.Unexpected, 124 .WSAEFAULT, 125 .WSAEOPNOTSUPP, 126 .WSAEINPROGRESS, 127 => unreachable, 128 else => |err| return windows.unexpectedWSAError(err), 129 } 130 } 131 } else { 132 var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) { 133 error.FileBusy => unreachable, 134 error.Locked => unreachable, 135 error.PermissionDenied => unreachable, 136 error.DeadLock => unreachable, 137 error.LockedRegionLimitExceeded => unreachable, 138 else => |e| return e, 139 }; 140 fl_flags |= 1 << @bitOffsetOf(O, "NONBLOCK"); 141 _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) { 142 error.FileBusy => unreachable, 143 error.Locked => unreachable, 144 error.PermissionDenied => unreachable, 145 error.DeadLock => unreachable, 146 error.LockedRegionLimitExceeded => unreachable, 147 else => |e| return e, 148 }; 149 } 150 } 151} 152 153pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) !usize { 154 while (true) { 155 const rc = posix.system.fcntl(fd, cmd, arg); 156 switch (posix.errno(rc)) { 157 .SUCCESS => return @intCast(rc), 158 .INTR => continue, 159 .AGAIN, .ACCES => return error.Locked, 160 .BADF => unreachable, 161 .BUSY => return error.FileBusy, 162 .INVAL => unreachable, // invalid parameters 163 .PERM => return error.PermissionDenied, 164 .MFILE => return error.ProcessFdQuotaExceeded, 165 .NOTDIR => unreachable, // invalid parameter 166 .DEADLK => return error.DeadLock, 167 .NOLCK => return error.LockedRegionLimitExceeded, 168 else => return error.Unexpected, 169 } 170 } 171} 172 173pub fn close(fd: fd_t) void { 174 if (native_os == .windows) { 175 // httpz only ever passes socket handles to close; `closesocket` is the 176 // proper API on Windows (CloseHandle skips Winsock's refcount/cleanup). 177 windows.closesocket(fd) catch {}; 178 return; 179 } 180 switch (posix.errno(system.close(fd))) { 181 .BADF => unreachable, // Always a race condition. 182 .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 183 else => return, 184 } 185} 186 187pub fn setsockopt(fd: socket_t, level: i32, optname: u32, opt: []const u8) !void { 188 if (native_os == .windows) { 189 // Winsock takes SO_RCVTIMEO/SO_SNDTIMEO as a DWORD of milliseconds, 190 // not a struct timeval. Translate so callers can stay portable. 191 var ms_buf: u32 = 0; 192 var opt_ptr: [*]const u8 = opt.ptr; 193 var opt_len: i32 = @intCast(opt.len); 194 if (level == SOL.SOCKET and (optname == SO.RCVTIMEO or optname == SO.SNDTIMEO) and opt.len == @sizeOf(timeval)) { 195 const tv: *const timeval = @ptrCast(@alignCast(opt.ptr)); 196 const total_ms = @as(i64, tv.sec) * 1000 + @divTrunc(@as(i64, tv.usec), 1000); 197 ms_buf = if (total_ms < 0) 0 else @intCast(@min(total_ms, std.math.maxInt(u32))); 198 opt_ptr = @ptrCast(&ms_buf); 199 opt_len = @sizeOf(u32); 200 } 201 const rc = windows.ws2_32.setsockopt(fd, level, @intCast(optname), opt_ptr, opt_len); 202 if (rc == windows.ws2_32.SOCKET_ERROR) { 203 switch (windows.ws2_32.WSAGetLastError()) { 204 .WSANOTINITIALISED => unreachable, 205 .WSAENETDOWN => return error.NetworkSubsystemFailed, 206 .WSAEFAULT => unreachable, 207 .WSAENOTSOCK => return error.FileDescriptorNotASocket, 208 .WSAEINVAL => return error.SocketNotBound, 209 else => |err| return windows.unexpectedWSAError(err), 210 } 211 } 212 return; 213 } else { 214 switch (posix.errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(opt.len)))) { 215 .SUCCESS => {}, 216 .BADF => unreachable, // always a race condition 217 .NOTSOCK => unreachable, // always a race condition 218 .INVAL => unreachable, 219 .FAULT => unreachable, 220 .DOM => return error.TimeoutTooBig, 221 .ISCONN => return error.AlreadyConnected, 222 .NOPROTOOPT => return error.InvalidProtocolOption, 223 .NOMEM => return error.SystemResources, 224 .NOBUFS => return error.SystemResources, 225 .PERM => return error.PermissionDenied, 226 .NODEV => return error.NoDevice, 227 .OPNOTSUPP => return error.OperationNotSupported, 228 else => return error.Unexpected, 229 } 230 } 231} 232 233pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) !void { 234 if (native_os == .windows) { 235 const rc = windows.bind(sock, addr, len); 236 if (rc == windows.ws2_32.SOCKET_ERROR) { 237 switch (windows.ws2_32.WSAGetLastError()) { 238 .WSANOTINITIALISED => unreachable, // not initialized WSA 239 .WSAEACCES => return error.AccessDenied, 240 .WSAEADDRINUSE => return error.AddressInUse, 241 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, 242 .WSAENOTSOCK => return error.FileDescriptorNotASocket, 243 .WSAEFAULT => unreachable, // invalid pointers 244 .WSAEINVAL => return error.AlreadyBound, 245 .WSAENOBUFS => return error.SystemResources, 246 .WSAENETDOWN => return error.NetworkSubsystemFailed, 247 else => |err| return windows.unexpectedWSAError(err), 248 } 249 unreachable; 250 } 251 return; 252 } else { 253 const rc = system.bind(sock, addr, len); 254 switch (posix.errno(rc)) { 255 .SUCCESS => return, 256 .ACCES, .PERM => return error.AccessDenied, 257 .ADDRINUSE => return error.AddressInUse, 258 .BADF => unreachable, // always a race condition if this error is returned 259 .INVAL => unreachable, // invalid parameters 260 .NOTSOCK => unreachable, // invalid `sockfd` 261 .AFNOSUPPORT => return error.AddressFamilyNotSupported, 262 .ADDRNOTAVAIL => return error.AddressNotAvailable, 263 .FAULT => unreachable, // invalid `addr` pointer 264 .LOOP => return error.SymLinkLoop, 265 .NAMETOOLONG => return error.NameTooLong, 266 .NOENT => return error.FileNotFound, 267 .NOMEM => return error.SystemResources, 268 .NOTDIR => return error.NotDir, 269 .ROFS => return error.ReadOnlyFileSystem, 270 else => return error.Unexpected, 271 } 272 } 273 unreachable; 274} 275 276pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) !void { 277 if (native_os == .windows) { 278 const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(len)); 279 if (rc == 0) return; 280 switch (windows.ws2_32.WSAGetLastError()) { 281 .WSAEADDRINUSE => return error.AddressInUse, 282 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, 283 .WSAECONNREFUSED => return error.ConnectionRefused, 284 .WSAECONNRESET => return error.ConnectionResetByPeer, 285 .WSAETIMEDOUT => return error.ConnectionTimedOut, 286 .WSAEHOSTUNREACH, 287 .WSAENETUNREACH, 288 => return error.NetworkUnreachable, 289 .WSAEFAULT => unreachable, 290 .WSAEINVAL => unreachable, 291 .WSAEISCONN => unreachable, 292 .WSAENOTSOCK => unreachable, 293 .WSAEWOULDBLOCK => return error.WouldBlock, 294 .WSAEACCES => unreachable, 295 .WSAENOBUFS => return error.SystemResources, 296 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, 297 else => |err| return windows.unexpectedWSAError(err), 298 } 299 return; 300 } 301 302 while (true) { 303 switch (posix.errno(system.connect(sock, sock_addr, len))) { 304 .SUCCESS => return, 305 .ACCES => return error.AccessDenied, 306 .PERM => return error.PermissionDenied, 307 .ADDRINUSE => return error.AddressInUse, 308 .ADDRNOTAVAIL => return error.AddressNotAvailable, 309 .AFNOSUPPORT => return error.AddressFamilyNotSupported, 310 .AGAIN, .INPROGRESS => return error.WouldBlock, 311 .ALREADY => return error.ConnectionPending, 312 .BADF => unreachable, 313 .CONNREFUSED => return error.ConnectionRefused, 314 .CONNRESET => return error.ConnectionResetByPeer, 315 .FAULT => unreachable, 316 .INTR => continue, 317 .ISCONN => unreachable, 318 .HOSTUNREACH => return error.NetworkUnreachable, 319 .NETUNREACH => return error.NetworkUnreachable, 320 .NOTSOCK => unreachable, 321 .PROTOTYPE => unreachable, 322 .TIMEDOUT => return error.ConnectionTimedOut, 323 .NOENT => return error.FileNotFound, 324 .CONNABORTED => unreachable, 325 else => return error.Unexpected, 326 } 327 } 328} 329 330pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) !void { 331 if (native_os == .windows) { 332 const rc = windows.getsockname(sock, addr, addrlen); 333 if (rc == windows.ws2_32.SOCKET_ERROR) { 334 switch (windows.ws2_32.WSAGetLastError()) { 335 .WSANOTINITIALISED => unreachable, 336 .WSAENETDOWN => return error.NetworkSubsystemFailed, 337 .WSAEFAULT => unreachable, 338 .WSAENOTSOCK => return error.FileDescriptorNotASocket, 339 .WSAEINVAL => return error.SocketNotBound, 340 else => |err| return windows.unexpectedWSAError(err), 341 } 342 } 343 return; 344 } else { 345 const rc = system.getsockname(sock, addr, addrlen); 346 switch (posix.errno(rc)) { 347 .SUCCESS => return, 348 .BADF => unreachable, 349 .FAULT => unreachable, 350 .INVAL => unreachable, 351 .NOTSOCK => return error.FileDescriptorNotASocket, 352 .NOBUFS => return error.SystemResources, 353 else => return error.Unexpected, 354 } 355 } 356} 357 358pub const Address = extern union { 359 any: posix.sockaddr, 360 in: posix.sockaddr.in, 361 in6: posix.sockaddr.in6, 362 un: if (@hasDecl(posix.sockaddr, "un")) posix.sockaddr.un else posix.sockaddr, 363 364 pub fn initUnix(path: []const u8) !Address { 365 var sock_addr = posix.sockaddr.un{ 366 .family = AF.UNIX, 367 .path = undefined, 368 }; 369 370 // Add 1 to ensure a terminating 0 is present in the path array for maximum portability. 371 if (path.len + 1 > sock_addr.path.len) { 372 return error.NameTooLong; 373 } 374 375 @memset(&sock_addr.path, 0); 376 @memcpy(sock_addr.path[0..path.len], path); 377 378 return .{ .un = sock_addr }; 379 } 380 381 pub fn initIp4(addr: [4]u8, port: u16) !Address { 382 return .{ .in = .{ 383 .port = std.mem.nativeToBig(u16, port), 384 .addr = @as(*align(1) const u32, @ptrCast(&addr)).*, 385 } }; 386 } 387 388 pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) !Address { 389 return .{ .in6 = .{ 390 .addr = addr, 391 .port = std.mem.nativeToBig(u16, port), 392 .flowinfo = flowinfo, 393 .scope_id = scope_id, 394 } }; 395 } 396 397 pub fn getOsSockLen(self: Address) posix.socklen_t { 398 return switch (self.any.family) { 399 posix.AF.INET => @sizeOf(posix.sockaddr.in), 400 posix.AF.INET6 => @sizeOf(posix.sockaddr.in6), 401 posix.AF.UNIX => if (@hasDecl(posix.sockaddr, "un")) 402 @intCast(@offsetOf(posix.sockaddr.un, "path") + std.mem.indexOfScalar(u8, &self.un.path, 0).? + 1) 403 else 404 @sizeOf(posix.sockaddr), 405 else => @sizeOf(posix.sockaddr), 406 }; 407 } 408 409 pub fn toIOAddress(self: Address) std.Io.net.IpAddress { 410 return switch (self.any.family) { 411 posix.AF.INET => { 412 const bytes: *const [4]u8 = @ptrCast(&self.in.addr); 413 return .{ .ip4 = .{ .bytes = bytes.*, .port = std.mem.bigToNative(u16, self.in.port) } }; 414 }, 415 posix.AF.INET6 => { 416 // @ZIG016 I don't think this is correct 417 const bytes: *const [16]u8 = @ptrCast(&self.in.addr); 418 return .{ .ip6 = .{ .bytes = bytes.*, .port = std.mem.bigToNative(u16, self.in.port) } }; 419 }, 420 else => .{ .ip4 = .unspecified(0) }, 421 }; 422 } 423 424 pub fn format(self: Address, w: *std.Io.Writer) std.Io.Writer.Error!void { 425 return self.toIOAddress().format(w); 426 } 427}; 428 429pub fn listen(sock: socket_t, backlog: u31) !void { 430 if (native_os == .windows) { 431 const rc = windows.listen(sock, backlog); 432 if (rc == windows.ws2_32.SOCKET_ERROR) { 433 switch (windows.ws2_32.WSAGetLastError()) { 434 .WSANOTINITIALISED => unreachable, // not initialized WSA 435 .WSAENETDOWN => return error.NetworkSubsystemFailed, 436 .WSAEADDRINUSE => return error.AddressInUse, 437 .WSAEISCONN => return error.AlreadyConnected, 438 .WSAEINVAL => return error.SocketNotBound, 439 .WSAEMFILE, .WSAENOBUFS => return error.SystemResources, 440 .WSAENOTSOCK => return error.FileDescriptorNotASocket, 441 .WSAEOPNOTSUPP => return error.OperationNotSupported, 442 .WSAEINPROGRESS => unreachable, 443 else => |err| return windows.unexpectedWSAError(err), 444 } 445 } 446 return; 447 } else { 448 const rc = system.listen(sock, backlog); 449 switch (posix.errno(rc)) { 450 .SUCCESS => return, 451 .ADDRINUSE => return error.AddressInUse, 452 .BADF => unreachable, 453 .NOTSOCK => return error.FileDescriptorNotASocket, 454 .OPNOTSUPP => return error.OperationNotSupported, 455 else => return error.Unexpected, 456 } 457 } 458} 459 460pub fn accept( 461 /// This argument is a socket that has been created with `socket`, bound to a local address 462 /// with `bind`, and is listening for connections after a `listen`. 463 sock: socket_t, 464 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the 465 /// address of the peer socket, as known to the communications layer. The exact format of the 466 /// address returned addr is determined by the socket's address family (see `socket` and the 467 /// respective protocol man pages). 468 addr: ?*sockaddr, 469 /// This argument is a value-result argument: the caller must initialize it to contain the 470 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size 471 /// of the peer address. 472 /// 473 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` 474 /// will return a value greater than was supplied to the call. 475 addr_size: ?*socklen_t, 476 /// The following values can be bitwise ORed in flags to obtain different behavior: 477 /// * `SOCK.NONBLOCK` - Set the `NONBLOCK` file status flag on the open file description (see `open`) 478 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve 479 /// the same result. 480 /// * `SOCK.CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the 481 /// description of the `CLOEXEC` flag in `open` for reasons why this may be useful. 482 flags: u32, 483) !socket_t { 484 const have_accept4 = !(builtin.target.os.tag.isDarwin() or native_os == .windows or native_os == .haiku); 485 std.debug.assert(0 == (flags & ~@as(u32, NONBLOCK | CLOEXEC))); // Unsupported flag(s) 486 487 const accepted_sock: socket_t = while (true) { 488 const rc = if (have_accept4) 489 system.accept4(sock, addr, addr_size, flags) 490 else if (native_os == .windows) 491 windows.accept(sock, addr, addr_size) 492 else 493 system.accept(sock, addr, addr_size); 494 495 if (native_os == .windows) { 496 if (rc == windows.ws2_32.INVALID_SOCKET) { 497 switch (windows.ws2_32.WSAGetLastError()) { 498 .WSANOTINITIALISED => unreachable, // not initialized WSA 499 .WSAECONNRESET => return error.ConnectionResetByPeer, 500 .WSAEFAULT => unreachable, 501 .WSAEINTR, .WSAENOTSOCK => return error.SocketNotListening, 502 .WSAEINVAL => return error.SocketNotListening, 503 .WSAEMFILE => return error.ProcessFdQuotaExceeded, 504 .WSAENETDOWN => return error.NetworkSubsystemFailed, 505 .WSAENOBUFS => return error.FileDescriptorNotASocket, 506 .WSAEOPNOTSUPP => return error.OperationNotSupported, 507 .WSAEWOULDBLOCK => return error.WouldBlock, 508 else => |err| return windows.unexpectedWSAError(err), 509 } 510 } else { 511 break rc; 512 } 513 } else { 514 switch (posix.errno(rc)) { 515 .SUCCESS => break @intCast(rc), 516 .INTR => continue, 517 .AGAIN => return error.WouldBlock, 518 .BADF => { 519 // ZIG016 This is not right. If we hit this, it's always certainly 520 // an error - we're trying to read from a socket after it's been closed 521 // which is not safe. But, the code around this worked in 0.15 and I 522 // consider this entire 0.16 transition experimental. 523 return error.SocketNotListening; 524 }, 525 .CONNABORTED => return error.ConnectionAborted, 526 .FAULT => unreachable, 527 .INVAL => return error.SocketNotListening, 528 .NOTSOCK => unreachable, 529 .MFILE => return error.ProcessFdQuotaExceeded, 530 .NFILE => return error.SystemFdQuotaExceeded, 531 .NOBUFS => return error.SystemResources, 532 .NOMEM => return error.SystemResources, 533 .OPNOTSUPP => unreachable, 534 .PROTO => return error.ProtocolFailure, 535 .PERM => return error.BlockedByFirewall, 536 else => return error.Unexpected, 537 } 538 } 539 }; 540 541 errdefer switch (native_os) { 542 .windows => windows.closesocket(accepted_sock) catch unreachable, 543 else => close(accepted_sock), 544 }; 545 if (!have_accept4) { 546 try setSockFlags(accepted_sock, flags); 547 } 548 return accepted_sock; 549} 550 551pub const iovec_const = posix.iovec_const; 552 553/// Union of read errors across platforms. Declared explicitly so callers can 554/// switch on `error.WouldBlock` portably regardless of OS. 555pub const ReadError = error{ 556 WouldBlock, 557 ProcessNotFound, 558 Canceled, 559 NotOpenForReading, 560 InputOutput, 561 IsDir, 562 SystemResources, 563 SocketNotConnected, 564 ConnectionResetByPeer, 565 ConnectionTimedOut, 566 NetworkSubsystemFailed, 567 Unexpected, 568}; 569 570pub const WriteError = error{ 571 WouldBlock, 572 InvalidArgument, 573 ProcessNotFound, 574 NotOpenForWriting, 575 DiskQuota, 576 FileTooBig, 577 InputOutput, 578 NoSpaceLeft, 579 AccessDenied, 580 PermissionDenied, 581 BrokenPipe, 582 ConnectionResetByPeer, 583 DeviceBusy, 584 NoDevice, 585 MessageTooBig, 586 SystemResources, 587 SocketNotConnected, 588 NetworkSubsystemFailed, 589 Unexpected, 590}; 591 592pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { 593 if (native_os == .windows) { 594 // TODO improve this to use WSASend with the full iovec 595 if (iov.len == 0) return 0; 596 const first = iov[0]; 597 return write(fd, first.base[0..first.len]); 598 } 599 600 while (true) { 601 const rc = system.writev(fd, iov.ptr, @min(iov.len, posix.IOV_MAX)); 602 switch (posix.errno(rc)) { 603 .SUCCESS => return @intCast(rc), 604 .INTR => continue, 605 .INVAL => return error.InvalidArgument, 606 .FAULT => unreachable, 607 .SRCH => return error.ProcessNotFound, 608 .AGAIN => return error.WouldBlock, 609 .BADF => return error.NotOpenForWriting, // Can be a race condition. 610 .DESTADDRREQ => unreachable, // `connect` was never called. 611 .DQUOT => return error.DiskQuota, 612 .FBIG => return error.FileTooBig, 613 .IO => return error.InputOutput, 614 .NOSPC => return error.NoSpaceLeft, 615 .PERM => return error.PermissionDenied, 616 .PIPE => return error.BrokenPipe, 617 .CONNRESET => return error.ConnectionResetByPeer, 618 .BUSY => return error.DeviceBusy, 619 else => return error.Unexpected, 620 } 621 } 622} 623 624pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { 625 if (bytes.len == 0) return 0; 626 if (native_os == .windows) { 627 return windows.send(fd, bytes); 628 } 629 630 const max_count = switch (native_os) { 631 .linux => 0x7ffff000, 632 .macos, .ios, .watchos, .tvos, .visionos => std.math.maxInt(i32), 633 else => std.math.maxInt(isize), 634 }; 635 while (true) { 636 const rc = system.write(fd, bytes.ptr, @min(bytes.len, max_count)); 637 switch (posix.errno(rc)) { 638 .SUCCESS => return @intCast(rc), 639 .INTR => continue, 640 .INVAL => return error.InvalidArgument, 641 .FAULT => unreachable, 642 .SRCH => return error.ProcessNotFound, 643 .AGAIN => return error.WouldBlock, 644 .BADF => return error.NotOpenForWriting, // can be a race condition. 645 .DESTADDRREQ => unreachable, // `connect` was never called. 646 .DQUOT => return error.DiskQuota, 647 .FBIG => return error.FileTooBig, 648 .IO => return error.InputOutput, 649 .NOSPC => return error.NoSpaceLeft, 650 .ACCES => return error.AccessDenied, 651 .PERM => return error.PermissionDenied, 652 .PIPE => return error.BrokenPipe, 653 .CONNRESET => return error.ConnectionResetByPeer, 654 .BUSY => return error.DeviceBusy, 655 .NXIO => return error.NoDevice, 656 .MSGSIZE => return error.MessageTooBig, 657 else => return error.Unexpected, 658 } 659 } 660} 661 662pub fn read(fd: fd_t, buf: []u8) ReadError!usize { 663 if (buf.len == 0) return 0; 664 if (native_os == .windows) { 665 return windows.recv(fd, buf); 666 } 667 668 // Prevents EINVAL. 669 const max_count = switch (native_os) { 670 .linux => 0x7ffff000, 671 .macos, .ios, .watchos, .tvos, .visionos => std.math.maxInt(i32), 672 else => std.math.maxInt(isize), 673 }; 674 while (true) { 675 const rc = system.read(fd, buf.ptr, @min(buf.len, max_count)); 676 switch (posix.errno(rc)) { 677 .SUCCESS => return @intCast(rc), 678 .INTR => continue, 679 .INVAL => unreachable, 680 .FAULT => unreachable, 681 .SRCH => return error.ProcessNotFound, 682 .AGAIN => return error.WouldBlock, 683 .CANCELED => return error.Canceled, 684 .BADF => return error.NotOpenForReading, // Can be a race condition. 685 .IO => return error.InputOutput, 686 .ISDIR => return error.IsDir, 687 .NOBUFS => return error.SystemResources, 688 .NOMEM => return error.SystemResources, 689 .NOTCONN => return error.SocketNotConnected, 690 .CONNRESET => return error.ConnectionResetByPeer, 691 .TIMEDOUT => return error.ConnectionTimedOut, 692 else => return error.Unexpected, 693 } 694 } 695} 696 697pub const ShutdownHow = enum { recv, send, both }; 698 699/// Shutdown socket send/receive operations 700pub fn shutdown(sock: socket_t, how: ShutdownHow) !void { 701 if (native_os == .windows) { 702 const result = windows.ws2_32.shutdown(sock, switch (how) { 703 .recv => windows.ws2_32.SD_RECEIVE, 704 .send => windows.ws2_32.SD_SEND, 705 .both => windows.ws2_32.SD_BOTH, 706 }); 707 if (0 != result) switch (windows.ws2_32.WSAGetLastError()) { 708 .WSAECONNABORTED => return error.ConnectionAborted, 709 .WSAECONNRESET => return error.ConnectionResetByPeer, 710 .WSAEINPROGRESS => return error.BlockingOperationInProgress, 711 .WSAEINVAL => unreachable, 712 .WSAENETDOWN => return error.NetworkSubsystemFailed, 713 .WSAENOTCONN => return error.SocketNotConnected, 714 .WSAENOTSOCK => unreachable, 715 .WSANOTINITIALISED => unreachable, 716 else => |err| return windows.unexpectedWSAError(err), 717 }; 718 } else { 719 const rc = system.shutdown(sock, switch (how) { 720 .recv => posix.SHUT.RD, 721 .send => posix.SHUT.WR, 722 .both => posix.SHUT.RDWR, 723 }); 724 switch (posix.errno(rc)) { 725 .SUCCESS => return, 726 .BADF => unreachable, 727 .INVAL => unreachable, 728 .NOTCONN => return error.SocketNotConnected, 729 .NOTSOCK => unreachable, 730 .NOBUFS => return error.SystemResources, 731 else => return error.Unexpected, 732 } 733 } 734} 735 736pub fn kevent( 737 kq: i32, 738 changelist: []const Kevent, 739 eventlist: []Kevent, 740 timeout: ?*const timespec, 741) !usize { 742 while (true) { 743 const rc = system.kevent( 744 kq, 745 changelist.ptr, 746 std.math.cast(c_int, changelist.len) orelse return error.Overflow, 747 eventlist.ptr, 748 std.math.cast(c_int, eventlist.len) orelse return error.Overflow, 749 timeout, 750 ); 751 switch (posix.errno(rc)) { 752 .SUCCESS => return @intCast(rc), 753 .ACCES => return error.AccessDenied, 754 .FAULT => unreachable, 755 .BADF => unreachable, // Always a race condition. 756 .INTR => continue, 757 .INVAL => unreachable, 758 .NOENT => return error.EventNotFound, 759 .NOMEM => return error.SystemResources, 760 .SRCH => return error.ProcessNotFound, 761 else => unreachable, 762 } 763 } 764} 765 766pub fn kqueue() !i32 { 767 const rc = system.kqueue(); 768 switch (posix.errno(rc)) { 769 .SUCCESS => return @intCast(rc), 770 .MFILE => return error.ProcessFdQuotaExceeded, 771 .NFILE => return error.SystemFdQuotaExceeded, 772 else => return error.Unexpected, 773 } 774} 775 776pub fn eventfd(initval: u32, flags: u32) !i32 { 777 const rc = system.eventfd(initval, flags); 778 switch (posix.errno(rc)) { 779 .SUCCESS => return @intCast(rc), 780 .INVAL => unreachable, // invalid parameters 781 .MFILE => return error.ProcessFdQuotaExceeded, 782 .NFILE => return error.SystemFdQuotaExceeded, 783 .NODEV => return error.SystemResources, 784 .NOMEM => return error.SystemResources, 785 else => return error.Unexpected, 786 } 787} 788 789pub fn epoll_create1(flags: u32) !i32 { 790 const rc = system.epoll_create1(flags); 791 switch (posix.errno(rc)) { 792 .SUCCESS => return @intCast(rc), 793 .INVAL => unreachable, 794 .MFILE => return error.ProcessFdQuotaExceeded, 795 .NFILE => return error.SystemFdQuotaExceeded, 796 .NOMEM => return error.SystemResources, 797 else => return error.Unexpected, 798 } 799} 800 801pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*system.epoll_event) !void { 802 const rc = system.epoll_ctl(epfd, op, fd, event); 803 switch (posix.errno(rc)) { 804 .SUCCESS => return, 805 .BADF => unreachable, // always a race condition if this happens 806 .EXIST => return error.FileDescriptorAlreadyPresentInSet, 807 .INVAL => unreachable, 808 .LOOP => return error.OperationCausesCircularLoop, 809 .NOENT => return error.FileDescriptorNotRegistered, 810 .NOMEM => return error.SystemResources, 811 .NOSPC => return error.UserResourceLimitReached, 812 .PERM => return error.FileDescriptorIncompatibleWithEpoll, 813 else => return error.Unexpected, 814 } 815} 816 817/// Waits for an I/O event on an epoll file descriptor. 818/// Returns the number of file descriptors ready for the requested I/O, 819/// or zero if no file descriptor became ready during the requested timeout milliseconds. 820pub fn epoll_wait(epfd: i32, events: []system.epoll_event, timeout: i32) usize { 821 while (true) { 822 // TODO get rid of the @intCast 823 const rc = system.epoll_wait(epfd, events.ptr, @intCast(events.len), timeout); 824 switch (posix.errno(rc)) { 825 .SUCCESS => return @intCast(rc), 826 .INTR => continue, 827 .BADF => unreachable, 828 .FAULT => unreachable, 829 .INVAL => unreachable, 830 else => unreachable, 831 } 832 } 833}