| author | |
| committer | |
| log | 61d5a0bf48d034208aea37d72dac5b3531334be7 |
| tree | 57e545a972ae44c3bc6bab98396f9cb22203edd1 |
| parent | 6a15e8a7a771bcbf2534cceecd77231344aafbf8 |
| parent | 7b7ba51642c832c77ec2668491843be3b0114124 |
| signature |
30 files changed, 2840 insertions(+), 856 deletions(-)
lib/std/buffer.zig+8-8| ... | @@ -72,11 +72,11 @@ pub const Buffer = struct { | ... | @@ -72,11 +72,11 @@ pub const Buffer = struct { |
| 72 | self.list.deinit(); | 72 | self.list.deinit(); |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | pub fn toSlice(self: *const Buffer) []u8 { | 75 | pub fn toSlice(self: Buffer) []u8 { |
| 76 | return self.list.toSlice()[0..self.len()]; | 76 | return self.list.toSlice()[0..self.len()]; |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | pub fn toSliceConst(self: *const Buffer) []const u8 { | 79 | pub fn toSliceConst(self: Buffer) []const u8 { |
| 80 | return self.list.toSliceConst()[0..self.len()]; | 80 | return self.list.toSliceConst()[0..self.len()]; |
| 81 | } | 81 | } |
| 82 | 82 | ||
| ... | @@ -91,11 +91,11 @@ pub const Buffer = struct { | ... | @@ -91,11 +91,11 @@ pub const Buffer = struct { |
| 91 | self.list.items[self.len()] = 0; | 91 | self.list.items[self.len()] = 0; |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | pub fn isNull(self: *const Buffer) bool { | 94 | pub fn isNull(self: Buffer) bool { |
| 95 | return self.list.len == 0; | 95 | return self.list.len == 0; |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | pub fn len(self: *const Buffer) usize { | 98 | pub fn len(self: Buffer) usize { |
| 99 | return self.list.len - 1; | 99 | return self.list.len - 1; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| ... | @@ -111,16 +111,16 @@ pub const Buffer = struct { | ... | @@ -111,16 +111,16 @@ pub const Buffer = struct { |
| 111 | self.list.toSlice()[old_len] = byte; | 111 | self.list.toSlice()[old_len] = byte; |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | pub fn eql(self: *const Buffer, m: []const u8) bool { | 114 | pub fn eql(self: Buffer, m: []const u8) bool { |
| 115 | return mem.eql(u8, self.toSliceConst(), m); | 115 | return mem.eql(u8, self.toSliceConst(), m); |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | pub fn startsWith(self: *const Buffer, m: []const u8) bool { | 118 | pub fn startsWith(self: Buffer, m: []const u8) bool { |
| 119 | if (self.len() < m.len) return false; | 119 | if (self.len() < m.len) return false; |
| 120 | return mem.eql(u8, self.list.items[0..m.len], m); | 120 | return mem.eql(u8, self.list.items[0..m.len], m); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | pub fn endsWith(self: *const Buffer, m: []const u8) bool { | 123 | pub fn endsWith(self: Buffer, m: []const u8) bool { |
| 124 | const l = self.len(); | 124 | const l = self.len(); |
| 125 | if (l < m.len) return false; | 125 | if (l < m.len) return false; |
| 126 | const start = l - m.len; | 126 | const start = l - m.len; |
| ... | @@ -133,7 +133,7 @@ pub const Buffer = struct { | ... | @@ -133,7 +133,7 @@ pub const Buffer = struct { |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | /// For passing to C functions. | 135 | /// For passing to C functions. |
| 136 | pub fn ptr(self: *const Buffer) [*]u8 { | 136 | pub fn ptr(self: Buffer) [*]u8 { |
| 137 | return self.list.items.ptr; | 137 | return self.list.items.ptr; |
| 138 | } | 138 | } |
| 139 | }; | 139 | }; |
lib/std/c.zig+51| ... | @@ -117,6 +117,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add | ... | @@ -117,6 +117,26 @@ pub extern "c" fn getsockname(sockfd: fd_t, noalias addr: *sockaddr, noalias add |
| 117 | pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; | 117 | pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int; |
| 118 | pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; | 118 | pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int; |
| 119 | pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; | 119 | pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; |
| 120 | pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize; | ||
| 121 | pub extern "c" fn sendto( | ||
| 122 | sockfd: fd_t, | ||
| 123 | buf: *const c_void, | ||
| 124 | len: usize, | ||
| 125 | flags: u32, | ||
| 126 | dest_addr: *const sockaddr, | ||
| 127 | addrlen: socklen_t, | ||
| 128 | ) isize; | ||
| 129 | |||
| 130 | pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize; | ||
| 131 | pub extern fn recvfrom( | ||
| 132 | sockfd: fd_t, | ||
| 133 | noalias buf: *c_void, | ||
| 134 | len: usize, | ||
| 135 | flags: u32, | ||
| 136 | noalias src_addr: ?*sockaddr, | ||
| 137 | noalias addrlen: ?*socklen_t, | ||
| 138 | ) isize; | ||
| 139 | |||
| 120 | pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; | 140 | pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; |
| 121 | pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; | 141 | pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; |
| 122 | pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; | 142 | pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; |
| ... | @@ -149,3 +169,34 @@ pub extern "c" fn kevent( | ... | @@ -149,3 +169,34 @@ pub extern "c" fn kevent( |
| 149 | nevents: c_int, | 169 | nevents: c_int, |
| 150 | timeout: ?*const timespec, | 170 | timeout: ?*const timespec, |
| 151 | ) c_int; | 171 | ) c_int; |
| 172 | |||
| 173 | pub extern "c" fn getaddrinfo( | ||
| 174 | noalias node: [*]const u8, | ||
| 175 | noalias service: [*]const u8, | ||
| 176 | noalias hints: *const addrinfo, | ||
| 177 | noalias res: **addrinfo, | ||
| 178 | ) c_int; | ||
| 179 | |||
| 180 | pub extern "c" fn freeaddrinfo(res: *addrinfo) void; | ||
| 181 | |||
| 182 | pub extern "c" fn getnameinfo( | ||
| 183 | noalias addr: *const sockaddr, | ||
| 184 | addrlen: socklen_t, | ||
| 185 | noalias host: [*]u8, | ||
| 186 | hostlen: socklen_t, | ||
| 187 | noalias serv: [*]u8, | ||
| 188 | servlen: socklen_t, | ||
| 189 | flags: u32, | ||
| 190 | ) c_int; | ||
| 191 | |||
| 192 | pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8; | ||
| 193 | |||
| 194 | pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int; | ||
| 195 | |||
| 196 | pub extern "c" fn dn_expand( | ||
| 197 | msg: [*]const u8, | ||
| 198 | eomorig: [*]const u8, | ||
| 199 | comp_dn: [*]const u8, | ||
| 200 | exp_dn: [*]u8, | ||
| 201 | length: c_int, | ||
| 202 | ) c_int; |
lib/std/c/darwin.zig+55| ... | @@ -56,3 +56,58 @@ pub fn sigaddset(set: *sigset_t, signo: u5) void { | ... | @@ -56,3 +56,58 @@ pub fn sigaddset(set: *sigset_t, signo: u5) void { |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; | 58 | pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; |
| 59 | |||
| 60 | /// get address to use bind() | ||
| 61 | pub const AI_PASSIVE = 0x00000001; | ||
| 62 | |||
| 63 | /// fill ai_canonname | ||
| 64 | pub const AI_CANONNAME = 0x00000002; | ||
| 65 | |||
| 66 | /// prevent host name resolution | ||
| 67 | pub const AI_NUMERICHOST = 0x00000004; | ||
| 68 | |||
| 69 | /// prevent service name resolution | ||
| 70 | pub const AI_NUMERICSERV = 0x00001000; | ||
| 71 | |||
| 72 | /// address family for hostname not supported | ||
| 73 | pub const EAI_ADDRFAMILY = 1; | ||
| 74 | |||
| 75 | /// temporary failure in name resolution | ||
| 76 | pub const EAI_AGAIN = 2; | ||
| 77 | |||
| 78 | /// invalid value for ai_flags | ||
| 79 | pub const EAI_BADFLAGS = 3; | ||
| 80 | |||
| 81 | /// non-recoverable failure in name resolution | ||
| 82 | pub const EAI_FAIL = 4; | ||
| 83 | |||
| 84 | /// ai_family not supported | ||
| 85 | pub const EAI_FAMILY = 5; | ||
| 86 | |||
| 87 | /// memory allocation failure | ||
| 88 | pub const EAI_MEMORY = 6; | ||
| 89 | |||
| 90 | /// no address associated with hostname | ||
| 91 | pub const EAI_NODATA = 7; | ||
| 92 | |||
| 93 | /// hostname nor servname provided, or not known | ||
| 94 | pub const EAI_NONAME = 8; | ||
| 95 | |||
| 96 | /// servname not supported for ai_socktype | ||
| 97 | pub const EAI_SERVICE = 9; | ||
| 98 | |||
| 99 | /// ai_socktype not supported | ||
| 100 | pub const EAI_SOCKTYPE = 10; | ||
| 101 | |||
| 102 | /// system error returned in errno | ||
| 103 | pub const EAI_SYSTEM = 11; | ||
| 104 | |||
| 105 | /// invalid value for hints | ||
| 106 | pub const EAI_BADHINTS = 12; | ||
| 107 | |||
| 108 | /// resolved protocol is unknown | ||
| 109 | pub const EAI_PROTOCOL = 13; | ||
| 110 | |||
| 111 | /// argument buffer overflow | ||
| 112 | pub const EAI_OVERFLOW = 14; | ||
| 113 | pub const EAI_MAX = 15; |
lib/std/c/linux.zig+35| ... | @@ -17,6 +17,41 @@ pub const _errno = switch (builtin.abi) { | ... | @@ -17,6 +17,41 @@ pub const _errno = switch (builtin.abi) { |
| 17 | 17 | ||
| 18 | pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); | 18 | pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); |
| 19 | 19 | ||
| 20 | pub const AI_PASSIVE = 0x01; | ||
| 21 | pub const AI_CANONNAME = 0x02; | ||
| 22 | pub const AI_NUMERICHOST = 0x04; | ||
| 23 | pub const AI_V4MAPPED = 0x08; | ||
| 24 | pub const AI_ALL = 0x10; | ||
| 25 | pub const AI_ADDRCONFIG = 0x20; | ||
| 26 | pub const AI_NUMERICSERV = 0x400; | ||
| 27 | |||
| 28 | pub const NI_NUMERICHOST = 0x01; | ||
| 29 | pub const NI_NUMERICSERV = 0x02; | ||
| 30 | pub const NI_NOFQDN = 0x04; | ||
| 31 | pub const NI_NAMEREQD = 0x08; | ||
| 32 | pub const NI_DGRAM = 0x10; | ||
| 33 | pub const NI_NUMERICSCOPE = 0x100; | ||
| 34 | |||
| 35 | pub const EAI_BADFLAGS = -1; | ||
| 36 | pub const EAI_NONAME = -2; | ||
| 37 | pub const EAI_AGAIN = -3; | ||
| 38 | pub const EAI_FAIL = -4; | ||
| 39 | pub const EAI_FAMILY = -6; | ||
| 40 | pub const EAI_SOCKTYPE = -7; | ||
| 41 | pub const EAI_SERVICE = -8; | ||
| 42 | pub const EAI_MEMORY = -10; | ||
| 43 | pub const EAI_SYSTEM = -11; | ||
| 44 | pub const EAI_OVERFLOW = -12; | ||
| 45 | |||
| 46 | pub const EAI_NODATA = -5; | ||
| 47 | pub const EAI_ADDRFAMILY = -9; | ||
| 48 | pub const EAI_INPROGRESS = -100; | ||
| 49 | pub const EAI_CANCELED = -101; | ||
| 50 | pub const EAI_NOTCANCELED = -102; | ||
| 51 | pub const EAI_ALLDONE = -103; | ||
| 52 | pub const EAI_INTR = -104; | ||
| 53 | pub const EAI_IDN_ENCODE = -105; | ||
| 54 | |||
| 20 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; | 55 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; |
| 21 | pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; | 56 | pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; |
| 22 | pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int; | 57 | pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int; |
lib/std/event.zig-2| ... | @@ -7,7 +7,6 @@ pub const RwLock = @import("event/rwlock.zig").RwLock; | ... | @@ -7,7 +7,6 @@ pub const RwLock = @import("event/rwlock.zig").RwLock; |
| 7 | pub const RwLocked = @import("event/rwlocked.zig").RwLocked; | 7 | pub const RwLocked = @import("event/rwlocked.zig").RwLocked; |
| 8 | pub const Loop = @import("event/loop.zig").Loop; | 8 | pub const Loop = @import("event/loop.zig").Loop; |
| 9 | pub const fs = @import("event/fs.zig"); | 9 | pub const fs = @import("event/fs.zig"); |
| 10 | pub const net = @import("event/net.zig"); | ||
| 11 | 10 | ||
| 12 | test "import event tests" { | 11 | test "import event tests" { |
| 13 | _ = @import("event/channel.zig"); | 12 | _ = @import("event/channel.zig"); |
| ... | @@ -19,5 +18,4 @@ test "import event tests" { | ... | @@ -19,5 +18,4 @@ test "import event tests" { |
| 19 | _ = @import("event/rwlock.zig"); | 18 | _ = @import("event/rwlock.zig"); |
| 20 | _ = @import("event/rwlocked.zig"); | 19 | _ = @import("event/rwlocked.zig"); |
| 21 | _ = @import("event/loop.zig"); | 20 | _ = @import("event/loop.zig"); |
| 22 | _ = @import("event/net.zig"); | ||
| 23 | } | 21 | } |
lib/std/event/channel.zig+6-4| ... | @@ -4,9 +4,11 @@ const assert = std.debug.assert; | ... | @@ -4,9 +4,11 @@ const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
| 5 | const Loop = std.event.Loop; | 5 | const Loop = std.event.Loop; |
| 6 | 6 | ||
| 7 | /// many producer, many consumer, thread-safe, runtime configurable buffer size | 7 | /// Many producer, many consumer, thread-safe, runtime configurable buffer size. |
| 8 | /// when buffer is empty, consumers suspend and are resumed by producers | 8 | /// When buffer is empty, consumers suspend and are resumed by producers. |
| 9 | /// when buffer is full, producers suspend and are resumed by consumers | 9 | /// When buffer is full, producers suspend and are resumed by consumers. |
| 10 | /// TODO now that async function rewrite has landed, this API should be adjusted | ||
| 11 | /// to not use the event loop's allocator, and to not require allocation. | ||
| 10 | pub fn Channel(comptime T: type) type { | 12 | pub fn Channel(comptime T: type) type { |
| 11 | return struct { | 13 | return struct { |
| 12 | loop: *Loop, | 14 | loop: *Loop, |
| ... | @@ -48,7 +50,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -48,7 +50,7 @@ pub fn Channel(comptime T: type) type { |
| 48 | tick_node: *Loop.NextTickNode, | 50 | tick_node: *Loop.NextTickNode, |
| 49 | }; | 51 | }; |
| 50 | 52 | ||
| 51 | /// call destroy when done | 53 | /// Call `destroy` when done. |
| 52 | pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { | 54 | pub fn create(loop: *Loop, capacity: usize) !*SelfChannel { |
| 53 | const buffer_nodes = try loop.allocator.alloc(T, capacity); | 55 | const buffer_nodes = try loop.allocator.alloc(T, capacity); |
| 54 | errdefer loop.allocator.free(buffer_nodes); | 56 | errdefer loop.allocator.free(buffer_nodes); |
lib/std/event/loop.zig+67-20| ... | @@ -448,22 +448,67 @@ pub const Loop = struct { | ... | @@ -448,22 +448,67 @@ pub const Loop = struct { |
| 448 | self.finishOneEvent(); | 448 | self.finishOneEvent(); |
| 449 | } | 449 | } |
| 450 | 450 | ||
| 451 | pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { | 451 | pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) void { |
| 452 | defer self.linuxRemoveFd(fd); | 452 | assert(flags & os.EPOLLET == os.EPOLLET); |
| 453 | assert(flags & os.EPOLLONESHOT == os.EPOLLONESHOT); | ||
| 454 | var resume_node = ResumeNode.Basic{ | ||
| 455 | .base = ResumeNode{ | ||
| 456 | .id = .Basic, | ||
| 457 | .handle = @frame(), | ||
| 458 | .overlapped = ResumeNode.overlapped_init, | ||
| 459 | }, | ||
| 460 | }; | ||
| 461 | var need_to_delete = false; | ||
| 462 | defer if (need_to_delete) self.linuxRemoveFd(fd); | ||
| 463 | |||
| 453 | suspend { | 464 | suspend { |
| 454 | var resume_node = ResumeNode.Basic{ | 465 | if (self.linuxAddFd(fd, &resume_node.base, flags)) |_| { |
| 455 | .base = ResumeNode{ | 466 | need_to_delete = true; |
| 456 | .id = .Basic, | 467 | } else |err| switch (err) { |
| 457 | .handle = @frame(), | 468 | error.FileDescriptorNotRegistered => unreachable, |
| 458 | .overlapped = ResumeNode.overlapped_init, | 469 | error.OperationCausesCircularLoop => unreachable, |
| 470 | error.FileDescriptorIncompatibleWithEpoll => unreachable, | ||
| 471 | error.FileDescriptorAlreadyPresentInSet => unreachable, // evented writes to the same fd is not thread-safe | ||
| 472 | |||
| 473 | error.SystemResources, | ||
| 474 | error.UserResourceLimitReached, | ||
| 475 | error.Unexpected, | ||
| 476 | => { | ||
| 477 | // Fall back to a blocking poll(). Ideally this codepath is never hit, since | ||
| 478 | // epoll should be just fine. But this is better than incorrect behavior. | ||
| 479 | var poll_flags: i16 = 0; | ||
| 480 | if ((flags & os.EPOLLIN) != 0) poll_flags |= os.POLLIN; | ||
| 481 | if ((flags & os.EPOLLOUT) != 0) poll_flags |= os.POLLOUT; | ||
| 482 | var pfd = [1]os.pollfd{os.pollfd{ | ||
| 483 | .fd = fd, | ||
| 484 | .events = poll_flags, | ||
| 485 | .revents = undefined, | ||
| 486 | }}; | ||
| 487 | _ = os.poll(&pfd, -1) catch |poll_err| switch (poll_err) { | ||
| 488 | error.SystemResources, | ||
| 489 | error.Unexpected, | ||
| 490 | => { | ||
| 491 | // Even poll() didn't work. The best we can do now is sleep for a | ||
| 492 | // small duration and then hope that something changed. | ||
| 493 | std.time.sleep(1 * std.time.millisecond); | ||
| 494 | }, | ||
| 495 | }; | ||
| 496 | resume @frame(); | ||
| 459 | }, | 497 | }, |
| 460 | }; | 498 | } |
| 461 | try self.linuxAddFd(fd, &resume_node.base, flags); | ||
| 462 | } | 499 | } |
| 463 | } | 500 | } |
| 464 | 501 | ||
| 465 | pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) !void { | 502 | pub fn waitUntilFdReadable(self: *Loop, fd: os.fd_t) void { |
| 466 | return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN); | 503 | return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLIN); |
| 504 | } | ||
| 505 | |||
| 506 | pub fn waitUntilFdWritable(self: *Loop, fd: os.fd_t) void { | ||
| 507 | return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT); | ||
| 508 | } | ||
| 509 | |||
| 510 | pub fn waitUntilFdWritableOrReadable(self: *Loop, fd: os.fd_t) void { | ||
| 511 | return self.linuxWaitFd(fd, os.EPOLLET | os.EPOLLONESHOT | os.EPOLLOUT | os.EPOLLIN); | ||
| 467 | } | 512 | } |
| 468 | 513 | ||
| 469 | pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent { | 514 | pub async fn bsdWaitKev(self: *Loop, ident: usize, filter: i16, fflags: u32) !os.Kevent { |
| ... | @@ -642,7 +687,7 @@ pub const Loop = struct { | ... | @@ -642,7 +687,7 @@ pub const Loop = struct { |
| 642 | .linux => { | 687 | .linux => { |
| 643 | self.posixFsRequest(&self.os_data.fs_end_request); | 688 | self.posixFsRequest(&self.os_data.fs_end_request); |
| 644 | // writing 8 bytes to an eventfd cannot fail | 689 | // writing 8 bytes to an eventfd cannot fail |
| 645 | os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; | 690 | noasync os.write(self.os_data.final_eventfd, wakeup_bytes) catch unreachable; |
| 646 | return; | 691 | return; |
| 647 | }, | 692 | }, |
| 648 | .macosx, .freebsd, .netbsd, .dragonfly => { | 693 | .macosx, .freebsd, .netbsd, .dragonfly => { |
| ... | @@ -790,6 +835,8 @@ pub const Loop = struct { | ... | @@ -790,6 +835,8 @@ pub const Loop = struct { |
| 790 | } | 835 | } |
| 791 | } | 836 | } |
| 792 | 837 | ||
| 838 | // TODO make this whole function noasync | ||
| 839 | // https://github.com/ziglang/zig/issues/3157 | ||
| 793 | fn posixFsRun(self: *Loop) void { | 840 | fn posixFsRun(self: *Loop) void { |
| 794 | while (true) { | 841 | while (true) { |
| 795 | if (builtin.os == .linux) { | 842 | if (builtin.os == .linux) { |
| ... | @@ -799,27 +846,27 @@ pub const Loop = struct { | ... | @@ -799,27 +846,27 @@ pub const Loop = struct { |
| 799 | switch (node.data.msg) { | 846 | switch (node.data.msg) { |
| 800 | .End => return, | 847 | .End => return, |
| 801 | .WriteV => |*msg| { | 848 | .WriteV => |*msg| { |
| 802 | msg.result = os.writev(msg.fd, msg.iov); | 849 | msg.result = noasync os.writev(msg.fd, msg.iov); |
| 803 | }, | 850 | }, |
| 804 | .PWriteV => |*msg| { | 851 | .PWriteV => |*msg| { |
| 805 | msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); | 852 | msg.result = noasync os.pwritev(msg.fd, msg.iov, msg.offset); |
| 806 | }, | 853 | }, |
| 807 | .PReadV => |*msg| { | 854 | .PReadV => |*msg| { |
| 808 | msg.result = os.preadv(msg.fd, msg.iov, msg.offset); | 855 | msg.result = noasync os.preadv(msg.fd, msg.iov, msg.offset); |
| 809 | }, | 856 | }, |
| 810 | .Open => |*msg| { | 857 | .Open => |*msg| { |
| 811 | msg.result = os.openC(msg.path.ptr, msg.flags, msg.mode); | 858 | msg.result = noasync os.openC(msg.path.ptr, msg.flags, msg.mode); |
| 812 | }, | 859 | }, |
| 813 | .Close => |*msg| os.close(msg.fd), | 860 | .Close => |*msg| noasync os.close(msg.fd), |
| 814 | .WriteFile => |*msg| blk: { | 861 | .WriteFile => |*msg| blk: { |
| 815 | const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | | 862 | const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | |
| 816 | os.O_CLOEXEC | os.O_TRUNC; | 863 | os.O_CLOEXEC | os.O_TRUNC; |
| 817 | const fd = os.openC(msg.path.ptr, flags, msg.mode) catch |err| { | 864 | const fd = noasync os.openC(msg.path.ptr, flags, msg.mode) catch |err| { |
| 818 | msg.result = err; | 865 | msg.result = err; |
| 819 | break :blk; | 866 | break :blk; |
| 820 | }; | 867 | }; |
| 821 | defer os.close(fd); | 868 | defer noasync os.close(fd); |
| 822 | msg.result = os.write(fd, msg.contents); | 869 | msg.result = noasync os.write(fd, msg.contents); |
| 823 | }, | 870 | }, |
| 824 | } | 871 | } |
| 825 | switch (node.data.finish) { | 872 | switch (node.data.finish) { |
lib/std/event/net.zig deleted-358| ... | @@ -1,358 +0,0 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const testing = std.testing; | ||
| 4 | const event = std.event; | ||
| 5 | const mem = std.mem; | ||
| 6 | const os = std.os; | ||
| 7 | const Loop = std.event.Loop; | ||
| 8 | const File = std.fs.File; | ||
| 9 | const fd_t = os.fd_t; | ||
| 10 | |||
| 11 | pub const Server = struct { | ||
| 12 | handleRequestFn: async fn (*Server, *const std.net.Address, File) void, | ||
| 13 | |||
| 14 | loop: *Loop, | ||
| 15 | sockfd: ?i32, | ||
| 16 | accept_frame: ?anyframe, | ||
| 17 | listen_address: std.net.Address, | ||
| 18 | |||
| 19 | waiting_for_emfile_node: PromiseNode, | ||
| 20 | listen_resume_node: event.Loop.ResumeNode, | ||
| 21 | |||
| 22 | const PromiseNode = std.TailQueue(anyframe).Node; | ||
| 23 | |||
| 24 | pub fn init(loop: *Loop) Server { | ||
| 25 | // TODO can't initialize handler here because we need well defined copy elision | ||
| 26 | return Server{ | ||
| 27 | .loop = loop, | ||
| 28 | .sockfd = null, | ||
| 29 | .accept_frame = null, | ||
| 30 | .handleRequestFn = undefined, | ||
| 31 | .waiting_for_emfile_node = undefined, | ||
| 32 | .listen_address = undefined, | ||
| 33 | .listen_resume_node = event.Loop.ResumeNode{ | ||
| 34 | .id = event.Loop.ResumeNode.Id.Basic, | ||
| 35 | .handle = undefined, | ||
| 36 | .overlapped = event.Loop.ResumeNode.overlapped_init, | ||
| 37 | }, | ||
| 38 | }; | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn listen( | ||
| 42 | self: *Server, | ||
| 43 | address: *const std.net.Address, | ||
| 44 | handleRequestFn: async fn (*Server, *const std.net.Address, File) void, | ||
| 45 | ) !void { | ||
| 46 | self.handleRequestFn = handleRequestFn; | ||
| 47 | |||
| 48 | const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); | ||
| 49 | errdefer os.close(sockfd); | ||
| 50 | self.sockfd = sockfd; | ||
| 51 | |||
| 52 | try os.bind(sockfd, &address.os_addr); | ||
| 53 | try os.listen(sockfd, os.SOMAXCONN); | ||
| 54 | self.listen_address = std.net.Address.initPosix(try os.getsockname(sockfd)); | ||
| 55 | |||
| 56 | self.accept_frame = async Server.handler(self); | ||
| 57 | errdefer await self.accept_frame.?; | ||
| 58 | |||
| 59 | self.listen_resume_node.handle = self.accept_frame.?; | ||
| 60 | try self.loop.linuxAddFd(sockfd, &self.listen_resume_node, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); | ||
| 61 | errdefer self.loop.removeFd(sockfd); | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Stop listening | ||
| 65 | pub fn close(self: *Server) void { | ||
| 66 | self.loop.linuxRemoveFd(self.sockfd.?); | ||
| 67 | if (self.sockfd) |fd| { | ||
| 68 | os.close(fd); | ||
| 69 | self.sockfd = null; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn deinit(self: *Server) void { | ||
| 74 | if (self.accept_frame) |accept_frame| await accept_frame; | ||
| 75 | if (self.sockfd) |sockfd| os.close(sockfd); | ||
| 76 | } | ||
| 77 | |||
| 78 | pub async fn handler(self: *Server) void { | ||
| 79 | while (true) { | ||
| 80 | var accepted_addr: std.net.Address = undefined; | ||
| 81 | // TODO just inline the following function here and don't expose it as posixAsyncAccept | ||
| 82 | if (os.accept4_async(self.sockfd.?, &accepted_addr.os_addr, os.SOCK_NONBLOCK | os.SOCK_CLOEXEC)) |accepted_fd| { | ||
| 83 | if (accepted_fd == -1) { | ||
| 84 | // would block | ||
| 85 | suspend; // we will get resumed by epoll_wait in the event loop | ||
| 86 | continue; | ||
| 87 | } | ||
| 88 | var socket = File.openHandle(accepted_fd); | ||
| 89 | self.handleRequestFn(self, &accepted_addr, socket); | ||
| 90 | } else |err| switch (err) { | ||
| 91 | error.ProcessFdQuotaExceeded => @panic("TODO handle this error"), | ||
| 92 | error.ConnectionAborted => continue, | ||
| 93 | |||
| 94 | error.FileDescriptorNotASocket => unreachable, | ||
| 95 | error.OperationNotSupported => unreachable, | ||
| 96 | |||
| 97 | error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => { | ||
| 98 | @panic("TODO handle this error"); | ||
| 99 | }, | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | }; | ||
| 104 | |||
| 105 | pub async fn connectUnixSocket(loop: *Loop, path: []const u8) !i32 { | ||
| 106 | const sockfd = try os.socket( | ||
| 107 | os.AF_UNIX, | ||
| 108 | os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, | ||
| 109 | 0, | ||
| 110 | ); | ||
| 111 | errdefer os.close(sockfd); | ||
| 112 | |||
| 113 | var sock_addr = os.sockaddr_un{ | ||
| 114 | .family = os.AF_UNIX, | ||
| 115 | .path = undefined, | ||
| 116 | }; | ||
| 117 | |||
| 118 | if (path.len > @typeOf(sock_addr.path).len) return error.NameTooLong; | ||
| 119 | mem.copy(u8, sock_addr.path[0..], path); | ||
| 120 | const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len); | ||
| 121 | try os.connect_async(sockfd, &sock_addr, size); | ||
| 122 | try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); | ||
| 123 | try os.getsockoptError(sockfd); | ||
| 124 | |||
| 125 | return sockfd; | ||
| 126 | } | ||
| 127 | |||
| 128 | pub const ReadError = error{ | ||
| 129 | SystemResources, | ||
| 130 | Unexpected, | ||
| 131 | UserResourceLimitReached, | ||
| 132 | InputOutput, | ||
| 133 | |||
| 134 | FileDescriptorNotRegistered, // TODO remove this possibility | ||
| 135 | OperationCausesCircularLoop, // TODO remove this possibility | ||
| 136 | FileDescriptorAlreadyPresentInSet, // TODO remove this possibility | ||
| 137 | FileDescriptorIncompatibleWithEpoll, // TODO remove this possibility | ||
| 138 | }; | ||
| 139 | |||
| 140 | /// returns number of bytes read. 0 means EOF. | ||
| 141 | pub async fn read(loop: *std.event.Loop, fd: fd_t, buffer: []u8) ReadError!usize { | ||
| 142 | const iov = os.iovec{ | ||
| 143 | .iov_base = buffer.ptr, | ||
| 144 | .iov_len = buffer.len, | ||
| 145 | }; | ||
| 146 | const iovs: *const [1]os.iovec = &iov; | ||
| 147 | return readvPosix(loop, fd, iovs, 1); | ||
| 148 | } | ||
| 149 | |||
| 150 | pub const WriteError = error{}; | ||
| 151 | |||
| 152 | pub async fn write(loop: *std.event.Loop, fd: fd_t, buffer: []const u8) WriteError!void { | ||
| 153 | const iov = os.iovec_const{ | ||
| 154 | .iov_base = buffer.ptr, | ||
| 155 | .iov_len = buffer.len, | ||
| 156 | }; | ||
| 157 | const iovs: *const [1]os.iovec_const = &iov; | ||
| 158 | return writevPosix(loop, fd, iovs, 1); | ||
| 159 | } | ||
| 160 | |||
| 161 | pub async fn writevPosix(loop: *Loop, fd: i32, iov: [*]const os.iovec_const, count: usize) !void { | ||
| 162 | while (true) { | ||
| 163 | switch (builtin.os) { | ||
| 164 | .macosx, .linux => { | ||
| 165 | switch (os.errno(os.system.writev(fd, iov, count))) { | ||
| 166 | 0 => return, | ||
| 167 | os.EINTR => continue, | ||
| 168 | os.ESPIPE => unreachable, | ||
| 169 | os.EINVAL => unreachable, | ||
| 170 | os.EFAULT => unreachable, | ||
| 171 | os.EAGAIN => { | ||
| 172 | try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLOUT); | ||
| 173 | continue; | ||
| 174 | }, | ||
| 175 | os.EBADF => unreachable, // always a race condition | ||
| 176 | os.EDESTADDRREQ => unreachable, // connect was never called | ||
| 177 | os.EDQUOT => unreachable, | ||
| 178 | os.EFBIG => unreachable, | ||
| 179 | os.EIO => return error.InputOutput, | ||
| 180 | os.ENOSPC => unreachable, | ||
| 181 | os.EPERM => return error.AccessDenied, | ||
| 182 | os.EPIPE => unreachable, | ||
| 183 | else => |err| return os.unexpectedErrno(err), | ||
| 184 | } | ||
| 185 | }, | ||
| 186 | else => @compileError("Unsupported OS"), | ||
| 187 | } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /// returns number of bytes read. 0 means EOF. | ||
| 192 | pub async fn readvPosix(loop: *std.event.Loop, fd: i32, iov: [*]os.iovec, count: usize) !usize { | ||
| 193 | while (true) { | ||
| 194 | switch (builtin.os) { | ||
| 195 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.macosx => { | ||
| 196 | const rc = os.system.readv(fd, iov, count); | ||
| 197 | switch (os.errno(rc)) { | ||
| 198 | 0 => return rc, | ||
| 199 | os.EINTR => continue, | ||
| 200 | os.EINVAL => unreachable, | ||
| 201 | os.EFAULT => unreachable, | ||
| 202 | os.EAGAIN => { | ||
| 203 | try loop.linuxWaitFd(fd, os.EPOLLET | os.EPOLLIN); | ||
| 204 | continue; | ||
| 205 | }, | ||
| 206 | os.EBADF => unreachable, // always a race condition | ||
| 207 | os.EIO => return error.InputOutput, | ||
| 208 | os.EISDIR => unreachable, | ||
| 209 | os.ENOBUFS => return error.SystemResources, | ||
| 210 | os.ENOMEM => return error.SystemResources, | ||
| 211 | else => |err| return os.unexpectedErrno(err), | ||
| 212 | } | ||
| 213 | }, | ||
| 214 | else => @compileError("Unsupported OS"), | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | pub async fn writev(loop: *Loop, fd: fd_t, data: []const []const u8) !void { | ||
| 220 | const iovecs = try loop.allocator.alloc(os.iovec_const, data.len); | ||
| 221 | defer loop.allocator.free(iovecs); | ||
| 222 | |||
| 223 | for (data) |buf, i| { | ||
| 224 | iovecs[i] = os.iovec_const{ | ||
| 225 | .iov_base = buf.ptr, | ||
| 226 | .iov_len = buf.len, | ||
| 227 | }; | ||
| 228 | } | ||
| 229 | |||
| 230 | return writevPosix(loop, fd, iovecs.ptr, data.len); | ||
| 231 | } | ||
| 232 | |||
| 233 | pub async fn readv(loop: *Loop, fd: fd_t, data: []const []u8) !usize { | ||
| 234 | const iovecs = try loop.allocator.alloc(os.iovec, data.len); | ||
| 235 | defer loop.allocator.free(iovecs); | ||
| 236 | |||
| 237 | for (data) |buf, i| { | ||
| 238 | iovecs[i] = os.iovec{ | ||
| 239 | .iov_base = buf.ptr, | ||
| 240 | .iov_len = buf.len, | ||
| 241 | }; | ||
| 242 | } | ||
| 243 | |||
| 244 | return readvPosix(loop, fd, iovecs.ptr, data.len); | ||
| 245 | } | ||
| 246 | |||
| 247 | pub async fn connect(loop: *Loop, _address: *const std.net.Address) !File { | ||
| 248 | var address = _address.*; // TODO https://github.com/ziglang/zig/issues/1592 | ||
| 249 | |||
| 250 | const sockfd = try os.socket(os.AF_INET, os.SOCK_STREAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK, os.PROTO_tcp); | ||
| 251 | errdefer os.close(sockfd); | ||
| 252 | |||
| 253 | try os.connect_async(sockfd, &address.os_addr, @sizeOf(os.sockaddr_in)); | ||
| 254 | try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); | ||
| 255 | try os.getsockoptError(sockfd); | ||
| 256 | |||
| 257 | return File.openHandle(sockfd); | ||
| 258 | } | ||
| 259 | |||
| 260 | test "listen on a port, send bytes, receive bytes" { | ||
| 261 | // https://github.com/ziglang/zig/issues/2377 | ||
| 262 | if (true) return error.SkipZigTest; | ||
| 263 | |||
| 264 | if (builtin.os != builtin.Os.linux) { | ||
| 265 | // TODO build abstractions for other operating systems | ||
| 266 | return error.SkipZigTest; | ||
| 267 | } | ||
| 268 | |||
| 269 | const MyServer = struct { | ||
| 270 | tcp_server: Server, | ||
| 271 | |||
| 272 | const Self = @This(); | ||
| 273 | async fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: File) void { | ||
| 274 | const self = @fieldParentPtr(Self, "tcp_server", tcp_server); | ||
| 275 | var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 | ||
| 276 | defer socket.close(); | ||
| 277 | const next_handler = errorableHandler(self, _addr, socket) catch |err| { | ||
| 278 | std.debug.panic("unable to handle connection: {}\n", err); | ||
| 279 | }; | ||
| 280 | } | ||
| 281 | async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: File) !void { | ||
| 282 | const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/1592 | ||
| 283 | var socket = _socket; // TODO https://github.com/ziglang/zig/issues/1592 | ||
| 284 | |||
| 285 | const stream = &socket.outStream().stream; | ||
| 286 | try stream.print("hello from server\n"); | ||
| 287 | } | ||
| 288 | }; | ||
| 289 | |||
| 290 | const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable; | ||
| 291 | const addr = std.net.Address.initIp4(ip4addr, 0); | ||
| 292 | |||
| 293 | var loop: Loop = undefined; | ||
| 294 | try loop.initSingleThreaded(std.debug.global_allocator); | ||
| 295 | var server = MyServer{ .tcp_server = Server.init(&loop) }; | ||
| 296 | defer server.tcp_server.deinit(); | ||
| 297 | try server.tcp_server.listen(&addr, MyServer.handler); | ||
| 298 | |||
| 299 | _ = async doAsyncTest(&loop, &server.tcp_server.listen_address, &server.tcp_server); | ||
| 300 | loop.run(); | ||
| 301 | } | ||
| 302 | |||
| 303 | async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void { | ||
| 304 | errdefer @panic("test failure"); | ||
| 305 | |||
| 306 | var socket_file = try connect(loop, address); | ||
| 307 | defer socket_file.close(); | ||
| 308 | |||
| 309 | var buf: [512]u8 = undefined; | ||
| 310 | const amt_read = try socket_file.read(buf[0..]); | ||
| 311 | const msg = buf[0..amt_read]; | ||
| 312 | testing.expect(mem.eql(u8, msg, "hello from server\n")); | ||
| 313 | server.close(); | ||
| 314 | } | ||
| 315 | |||
| 316 | pub const OutStream = struct { | ||
| 317 | fd: fd_t, | ||
| 318 | stream: Stream, | ||
| 319 | loop: *Loop, | ||
| 320 | |||
| 321 | pub const Error = WriteError; | ||
| 322 | pub const Stream = event.io.OutStream(Error); | ||
| 323 | |||
| 324 | pub fn init(loop: *Loop, fd: fd_t) OutStream { | ||
| 325 | return OutStream{ | ||
| 326 | .fd = fd, | ||
| 327 | .loop = loop, | ||
| 328 | .stream = Stream{ .writeFn = writeFn }, | ||
| 329 | }; | ||
| 330 | } | ||
| 331 | |||
| 332 | async fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void { | ||
| 333 | const self = @fieldParentPtr(OutStream, "stream", out_stream); | ||
| 334 | return write(self.loop, self.fd, bytes); | ||
| 335 | } | ||
| 336 | }; | ||
| 337 | |||
| 338 | pub const InStream = struct { | ||
| 339 | fd: fd_t, | ||
| 340 | stream: Stream, | ||
| 341 | loop: *Loop, | ||
| 342 | |||
| 343 | pub const Error = ReadError; | ||
| 344 | pub const Stream = event.io.InStream(Error); | ||
| 345 | |||
| 346 | pub fn init(loop: *Loop, fd: fd_t) InStream { | ||
| 347 | return InStream{ | ||
| 348 | .fd = fd, | ||
| 349 | .loop = loop, | ||
| 350 | .stream = Stream{ .readFn = readFn }, | ||
| 351 | }; | ||
| 352 | } | ||
| 353 | |||
| 354 | async fn readFn(in_stream: *Stream, bytes: []u8) Error!usize { | ||
| 355 | const self = @fieldParentPtr(InStream, "stream", in_stream); | ||
| 356 | return read(self.loop, self.fd, bytes); | ||
| 357 | } | ||
| 358 | }; | ||
lib/std/fmt.zig+2-2| ... | @@ -53,7 +53,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { | ... | @@ -53,7 +53,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { |
| 53 | /// The format string must be comptime known and may contain placeholders following | 53 | /// The format string must be comptime known and may contain placeholders following |
| 54 | /// this format: | 54 | /// this format: |
| 55 | /// `{[position][specifier]:[fill][alignment][width].[precision]}` | 55 | /// `{[position][specifier]:[fill][alignment][width].[precision]}` |
| 56 | /// | 56 | /// |
| 57 | /// Each word between `[` and `]` is a parameter you have to replace with something: | 57 | /// Each word between `[` and `]` is a parameter you have to replace with something: |
| 58 | /// | 58 | /// |
| 59 | /// - *position* is the index of the argument that should be inserted | 59 | /// - *position* is the index of the argument that should be inserted |
| ... | @@ -78,7 +78,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { | ... | @@ -78,7 +78,7 @@ fn peekIsAlign(comptime fmt: []const u8) bool { |
| 78 | /// - `d`: output numeric value in decimal notation | 78 | /// - `d`: output numeric value in decimal notation |
| 79 | /// - `b`: output integer value in binary notation | 79 | /// - `b`: output integer value in binary notation |
| 80 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. | 80 | /// - `c`: output integer as an ASCII character. Integer type must have 8 bits at max. |
| 81 | /// - `*`: output the address of the value instead of the value itself. | 81 | /// - `*`: output the address of the value instead of the value itself. |
| 82 | /// | 82 | /// |
| 83 | /// If a formatted user type contains a function of the type | 83 | /// If a formatted user type contains a function of the type |
| 84 | /// ``` | 84 | /// ``` |
lib/std/fs.zig+1-1| ... | @@ -704,7 +704,7 @@ pub const Dir = struct { | ... | @@ -704,7 +704,7 @@ pub const Dir = struct { |
| 704 | 704 | ||
| 705 | /// Call `File.close` on the result when done. | 705 | /// Call `File.close` on the result when done. |
| 706 | pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File { | 706 | pub fn openReadC(self: Dir, sub_path: [*]const u8) File.OpenError!File { |
| 707 | const flags = os.O_LARGEFILE | os.O_RDONLY; | 707 | const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; |
| 708 | const fd = try os.openatC(self.fd, sub_path, flags, 0); | 708 | const fd = try os.openatC(self.fd, sub_path, flags, 0); |
| 709 | return File.openHandle(fd); | 709 | return File.openHandle(fd); |
| 710 | } | 710 | } |
lib/std/fs/file.zig+1-1| ... | @@ -41,7 +41,7 @@ pub const File = struct { | ... | @@ -41,7 +41,7 @@ pub const File = struct { |
| 41 | const path_w = try windows.cStrToPrefixedFileW(path); | 41 | const path_w = try windows.cStrToPrefixedFileW(path); |
| 42 | return openReadW(&path_w); | 42 | return openReadW(&path_w); |
| 43 | } | 43 | } |
| 44 | const flags = os.O_LARGEFILE | os.O_RDONLY; | 44 | const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; |
| 45 | const fd = try os.openC(path, flags, 0); | 45 | const fd = try os.openC(path, flags, 0); |
| 46 | return openHandle(fd); | 46 | return openHandle(fd); |
| 47 | } | 47 | } |
lib/std/io.zig+1-62| ... | @@ -64,68 +64,7 @@ pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; | ... | @@ -64,68 +64,7 @@ pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream; |
| 64 | pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; | 64 | pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream; |
| 65 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; | 65 | pub const COutStream = @import("io/c_out_stream.zig").COutStream; |
| 66 | pub const InStream = @import("io/in_stream.zig").InStream; | 66 | pub const InStream = @import("io/in_stream.zig").InStream; |
| 67 | 67 | pub const OutStream = @import("io/out_stream.zig").OutStream; | |
| 68 | pub fn OutStream(comptime WriteError: type) type { | ||
| 69 | return struct { | ||
| 70 | const Self = @This(); | ||
| 71 | pub const Error = WriteError; | ||
| 72 | |||
| 73 | writeFn: fn (self: *Self, bytes: []const u8) Error!void, | ||
| 74 | |||
| 75 | pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void { | ||
| 76 | return std.fmt.format(self, Error, self.writeFn, format, args); | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn write(self: *Self, bytes: []const u8) Error!void { | ||
| 80 | return self.writeFn(self, bytes); | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn writeByte(self: *Self, byte: u8) Error!void { | ||
| 84 | const slice = (*const [1]u8)(&byte)[0..]; | ||
| 85 | return self.writeFn(self, slice); | ||
| 86 | } | ||
| 87 | |||
| 88 | pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void { | ||
| 89 | const slice = (*const [1]u8)(&byte)[0..]; | ||
| 90 | var i: usize = 0; | ||
| 91 | while (i < n) : (i += 1) { | ||
| 92 | try self.writeFn(self, slice); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Write a native-endian integer. | ||
| 97 | pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void { | ||
| 98 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 99 | mem.writeIntNative(T, &bytes, value); | ||
| 100 | return self.writeFn(self, bytes); | ||
| 101 | } | ||
| 102 | |||
| 103 | /// Write a foreign-endian integer. | ||
| 104 | pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void { | ||
| 105 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 106 | mem.writeIntForeign(T, &bytes, value); | ||
| 107 | return self.writeFn(self, bytes); | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void { | ||
| 111 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 112 | mem.writeIntLittle(T, &bytes, value); | ||
| 113 | return self.writeFn(self, bytes); | ||
| 114 | } | ||
| 115 | |||
| 116 | pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void { | ||
| 117 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 118 | mem.writeIntBig(T, &bytes, value); | ||
| 119 | return self.writeFn(self, bytes); | ||
| 120 | } | ||
| 121 | |||
| 122 | pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { | ||
| 123 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 124 | mem.writeInt(T, &bytes, value, endian); | ||
| 125 | return self.writeFn(self, bytes); | ||
| 126 | } | ||
| 127 | }; | ||
| 128 | } | ||
| 129 | 68 | ||
| 130 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. | 69 | /// TODO move this to `std.fs` and add a version to `std.fs.Dir`. |
| 131 | pub fn writeFile(path: []const u8, data: []const u8) !void { | 70 | pub fn writeFile(path: []const u8, data: []const u8) !void { |
lib/std/io/in_stream.zig+42-2| ... | @@ -11,7 +11,6 @@ pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream")) | ... | @@ -11,7 +11,6 @@ pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_InStream")) |
| 11 | root.stack_size_std_io_InStream | 11 | root.stack_size_std_io_InStream |
| 12 | else | 12 | else |
| 13 | default_stack_size; | 13 | default_stack_size; |
| 14 | pub const stack_align = 16; | ||
| 15 | 14 | ||
| 16 | pub fn InStream(comptime ReadError: type) type { | 15 | pub fn InStream(comptime ReadError: type) type { |
| 17 | return struct { | 16 | return struct { |
| ... | @@ -34,7 +33,7 @@ pub fn InStream(comptime ReadError: type) type { | ... | @@ -34,7 +33,7 @@ pub fn InStream(comptime ReadError: type) type { |
| 34 | if (std.io.is_async) { | 33 | if (std.io.is_async) { |
| 35 | // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read. | 34 | // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream read. |
| 36 | @setRuntimeSafety(false); | 35 | @setRuntimeSafety(false); |
| 37 | var stack_frame: [stack_size]u8 align(stack_align) = undefined; | 36 | var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; |
| 38 | return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer); | 37 | return await @asyncCall(&stack_frame, {}, self.readFn, self, buffer); |
| 39 | } else { | 38 | } else { |
| 40 | return self.readFn(self, buffer); | 39 | return self.readFn(self, buffer); |
| ... | @@ -130,6 +129,47 @@ pub fn InStream(comptime ReadError: type) type { | ... | @@ -130,6 +129,47 @@ pub fn InStream(comptime ReadError: type) type { |
| 130 | return buf.toOwnedSlice(); | 129 | return buf.toOwnedSlice(); |
| 131 | } | 130 | } |
| 132 | 131 | ||
| 132 | /// Reads from the stream until specified byte is found. If the buffer is not | ||
| 133 | /// large enough to hold the entire contents, `error.StreamTooLong` is returned. | ||
| 134 | /// If end-of-stream is found, returns the rest of the stream. If this | ||
| 135 | /// function is called again after that, returns null. | ||
| 136 | /// Returns a slice of the stream data, with ptr equal to `buf.ptr`. The | ||
| 137 | /// delimiter byte is not included in the returned slice. | ||
| 138 | pub fn readUntilDelimiterOrEof(self: *Self, buf: []u8, delimiter: u8) !?[]u8 { | ||
| 139 | var index: usize = 0; | ||
| 140 | while (true) { | ||
| 141 | const byte = self.readByte() catch |err| switch (err) { | ||
| 142 | error.EndOfStream => { | ||
| 143 | if (index == 0) { | ||
| 144 | return null; | ||
| 145 | } else { | ||
| 146 | return buf[0..index]; | ||
| 147 | } | ||
| 148 | }, | ||
| 149 | else => |e| return e, | ||
| 150 | }; | ||
| 151 | |||
| 152 | if (byte == delimiter) return buf[0..index]; | ||
| 153 | if (index >= buf.len) return error.StreamTooLong; | ||
| 154 | |||
| 155 | buf[index] = byte; | ||
| 156 | index += 1; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | /// Reads from the stream until specified byte is found, discarding all data, | ||
| 161 | /// including the delimiter. | ||
| 162 | /// If end-of-stream is found, this function succeeds. | ||
| 163 | pub fn skipUntilDelimiterOrEof(self: *Self, delimiter: u8) !void { | ||
| 164 | while (true) { | ||
| 165 | const byte = self.readByte() catch |err| switch (err) { | ||
| 166 | error.EndOfStream => return, | ||
| 167 | else => |e| return e, | ||
| 168 | }; | ||
| 169 | if (byte == delimiter) return; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 133 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. | 173 | /// Reads 1 byte from the stream or returns `error.EndOfStream`. |
| 134 | pub fn readByte(self: *Self) !u8 { | 174 | pub fn readByte(self: *Self) !u8 { |
| 135 | var result: [1]u8 = undefined; | 175 | var result: [1]u8 = undefined; |
lib/std/io/out_stream.zig created+87| ... | @@ -0,0 +1,87 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const root = @import("root"); | ||
| 4 | const mem = std.mem; | ||
| 5 | |||
| 6 | pub const default_stack_size = 1 * 1024 * 1024; | ||
| 7 | pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream")) | ||
| 8 | root.stack_size_std_io_OutStream | ||
| 9 | else | ||
| 10 | default_stack_size; | ||
| 11 | |||
| 12 | /// TODO this is not integrated with evented I/O yet. | ||
| 13 | /// https://github.com/ziglang/zig/issues/3557 | ||
| 14 | pub fn OutStream(comptime WriteError: type) type { | ||
| 15 | return struct { | ||
| 16 | const Self = @This(); | ||
| 17 | pub const Error = WriteError; | ||
| 18 | // TODO https://github.com/ziglang/zig/issues/3557 | ||
| 19 | pub const WriteFn = if (std.io.is_async and false) | ||
| 20 | async fn (self: *Self, bytes: []const u8) Error!void | ||
| 21 | else | ||
| 22 | fn (self: *Self, bytes: []const u8) Error!void; | ||
| 23 | |||
| 24 | writeFn: WriteFn, | ||
| 25 | |||
| 26 | pub fn write(self: *Self, bytes: []const u8) Error!void { | ||
| 27 | // TODO https://github.com/ziglang/zig/issues/3557 | ||
| 28 | if (std.io.is_async and false) { | ||
| 29 | // Let's not be writing 0xaa in safe modes for upwards of 4 MiB for every stream write. | ||
| 30 | @setRuntimeSafety(false); | ||
| 31 | var stack_frame: [stack_size]u8 align(std.Target.stack_align) = undefined; | ||
| 32 | return await @asyncCall(&stack_frame, {}, self.writeFn, self, bytes); | ||
| 33 | } else { | ||
| 34 | return self.writeFn(self, bytes); | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | pub fn print(self: *Self, comptime format: []const u8, args: ...) Error!void { | ||
| 39 | return std.fmt.format(self, Error, self.writeFn, format, args); | ||
| 40 | } | ||
| 41 | |||
| 42 | pub fn writeByte(self: *Self, byte: u8) Error!void { | ||
| 43 | const slice = (*const [1]u8)(&byte)[0..]; | ||
| 44 | return self.writeFn(self, slice); | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn writeByteNTimes(self: *Self, byte: u8, n: usize) Error!void { | ||
| 48 | const slice = (*const [1]u8)(&byte)[0..]; | ||
| 49 | var i: usize = 0; | ||
| 50 | while (i < n) : (i += 1) { | ||
| 51 | try self.writeFn(self, slice); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | /// Write a native-endian integer. | ||
| 56 | pub fn writeIntNative(self: *Self, comptime T: type, value: T) Error!void { | ||
| 57 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 58 | mem.writeIntNative(T, &bytes, value); | ||
| 59 | return self.writeFn(self, bytes); | ||
| 60 | } | ||
| 61 | |||
| 62 | /// Write a foreign-endian integer. | ||
| 63 | pub fn writeIntForeign(self: *Self, comptime T: type, value: T) Error!void { | ||
| 64 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 65 | mem.writeIntForeign(T, &bytes, value); | ||
| 66 | return self.writeFn(self, bytes); | ||
| 67 | } | ||
| 68 | |||
| 69 | pub fn writeIntLittle(self: *Self, comptime T: type, value: T) Error!void { | ||
| 70 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 71 | mem.writeIntLittle(T, &bytes, value); | ||
| 72 | return self.writeFn(self, bytes); | ||
| 73 | } | ||
| 74 | |||
| 75 | pub fn writeIntBig(self: *Self, comptime T: type, value: T) Error!void { | ||
| 76 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 77 | mem.writeIntBig(T, &bytes, value); | ||
| 78 | return self.writeFn(self, bytes); | ||
| 79 | } | ||
| 80 | |||
| 81 | pub fn writeInt(self: *Self, comptime T: type, value: T, endian: builtin.Endian) Error!void { | ||
| 82 | var bytes: [(T.bit_count + 7) / 8]u8 = undefined; | ||
| 83 | mem.writeInt(T, &bytes, value, endian); | ||
| 84 | return self.writeFn(self, bytes); | ||
| 85 | } | ||
| 86 | }; | ||
| 87 | } | ||
lib/std/mem.zig+1-1| ... | @@ -99,7 +99,7 @@ pub const Allocator = struct { | ... | @@ -99,7 +99,7 @@ pub const Allocator = struct { |
| 99 | /// memory is no longer needed, to avoid a resource leak. If the | 99 | /// memory is no longer needed, to avoid a resource leak. If the |
| 100 | /// `Allocator` implementation is unknown, then correct code will | 100 | /// `Allocator` implementation is unknown, then correct code will |
| 101 | /// call `free` when done. | 101 | /// call `free` when done. |
| 102 | /// | 102 | /// |
| 103 | /// For allocating a single item, see `create`. | 103 | /// For allocating a single item, see `create`. |
| 104 | pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T { | 104 | pub fn alloc(self: *Allocator, comptime T: type, n: usize) Error![]T { |
| 105 | return self.alignedAlloc(T, null, n); | 105 | return self.alignedAlloc(T, null, n); |
lib/std/net.zig+1261-176| ... | @@ -4,244 +4,1329 @@ const assert = std.debug.assert; | ... | @@ -4,244 +4,1329 @@ const assert = std.debug.assert; |
| 4 | const net = @This(); | 4 | const net = @This(); |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const os = std.os; | 6 | const os = std.os; |
| 7 | const fs = std.fs; | ||
| 7 | 8 | ||
| 8 | pub const TmpWinAddr = struct { | 9 | test "" { |
| 9 | family: u8, | 10 | _ = @import("net/test.zig"); |
| 10 | data: [14]u8, | 11 | } |
| 11 | }; | ||
| 12 | 12 | ||
| 13 | pub const OsAddress = switch (builtin.os) { | 13 | pub const IpAddress = extern union { |
| 14 | builtin.Os.windows => TmpWinAddr, | 14 | any: os.sockaddr, |
| 15 | else => os.sockaddr, | 15 | in: os.sockaddr_in, |
| 16 | }; | 16 | in6: os.sockaddr_in6, |
| 17 | 17 | ||
| 18 | pub const Address = struct { | 18 | // TODO this crashed the compiler |
| 19 | os_addr: OsAddress, | 19 | //pub const localhost = initIp4(parseIp4("127.0.0.1") catch unreachable, 0); |
| 20 | 20 | ||
| 21 | pub fn initIp4(ip4: u32, _port: u16) Address { | 21 | pub fn parse(name: []const u8, port: u16) !IpAddress { |
| 22 | return Address{ | 22 | if (parseIp4(name, port)) |ip4| return ip4 else |err| switch (err) { |
| 23 | .os_addr = os.sockaddr{ | 23 | error.Overflow, |
| 24 | .in = os.sockaddr_in{ | 24 | error.InvalidEnd, |
| 25 | .family = os.AF_INET, | 25 | error.InvalidCharacter, |
| 26 | .port = mem.nativeToBig(u16, _port), | 26 | error.Incomplete, |
| 27 | .addr = ip4, | 27 | => {}, |
| 28 | .zero = [_]u8{0} ** 8, | 28 | } |
| 29 | }, | 29 | |
| 30 | if (parseIp6(name, port)) |ip6| return ip6 else |err| switch (err) { | ||
| 31 | error.Overflow, | ||
| 32 | error.InvalidEnd, | ||
| 33 | error.InvalidCharacter, | ||
| 34 | error.Incomplete, | ||
| 35 | => {}, | ||
| 36 | } | ||
| 37 | |||
| 38 | return error.InvalidIPAddressFormat; | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn parseExpectingFamily(name: []const u8, family: os.sa_family_t, port: u16) !IpAddress { | ||
| 42 | switch (family) { | ||
| 43 | os.AF_INET => return parseIp4(name, port), | ||
| 44 | os.AF_INET6 => return parseIp6(name, port), | ||
| 45 | os.AF_UNSPEC => return parse(name, port), | ||
| 46 | else => unreachable, | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn parseIp6(buf: []const u8, port: u16) !IpAddress { | ||
| 51 | var result = IpAddress{ | ||
| 52 | .in6 = os.sockaddr_in6{ | ||
| 53 | .scope_id = undefined, | ||
| 54 | .port = mem.nativeToBig(u16, port), | ||
| 55 | .flowinfo = 0, | ||
| 56 | .addr = undefined, | ||
| 57 | }, | ||
| 58 | }; | ||
| 59 | const ip_slice = result.in6.addr[0..]; | ||
| 60 | |||
| 61 | var x: u16 = 0; | ||
| 62 | var saw_any_digits = false; | ||
| 63 | var index: u8 = 0; | ||
| 64 | var scope_id = false; | ||
| 65 | for (buf) |c| { | ||
| 66 | if (scope_id) { | ||
| 67 | if (c >= '0' and c <= '9') { | ||
| 68 | const digit = c - '0'; | ||
| 69 | if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) { | ||
| 70 | return error.Overflow; | ||
| 71 | } | ||
| 72 | if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) { | ||
| 73 | return error.Overflow; | ||
| 74 | } | ||
| 75 | } else { | ||
| 76 | return error.InvalidCharacter; | ||
| 77 | } | ||
| 78 | } else if (c == ':') { | ||
| 79 | if (!saw_any_digits) { | ||
| 80 | return error.InvalidCharacter; | ||
| 81 | } | ||
| 82 | if (index == 14) { | ||
| 83 | return error.InvalidEnd; | ||
| 84 | } | ||
| 85 | ip_slice[index] = @truncate(u8, x >> 8); | ||
| 86 | index += 1; | ||
| 87 | ip_slice[index] = @truncate(u8, x); | ||
| 88 | index += 1; | ||
| 89 | |||
| 90 | x = 0; | ||
| 91 | saw_any_digits = false; | ||
| 92 | } else if (c == '%') { | ||
| 93 | if (!saw_any_digits) { | ||
| 94 | return error.InvalidCharacter; | ||
| 95 | } | ||
| 96 | if (index == 14) { | ||
| 97 | ip_slice[index] = @truncate(u8, x >> 8); | ||
| 98 | index += 1; | ||
| 99 | ip_slice[index] = @truncate(u8, x); | ||
| 100 | index += 1; | ||
| 101 | } | ||
| 102 | scope_id = true; | ||
| 103 | saw_any_digits = false; | ||
| 104 | } else { | ||
| 105 | const digit = try std.fmt.charToDigit(c, 16); | ||
| 106 | if (@mulWithOverflow(u16, x, 16, &x)) { | ||
| 107 | return error.Overflow; | ||
| 108 | } | ||
| 109 | if (@addWithOverflow(u16, x, digit, &x)) { | ||
| 110 | return error.Overflow; | ||
| 111 | } | ||
| 112 | saw_any_digits = true; | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | if (!saw_any_digits) { | ||
| 117 | return error.Incomplete; | ||
| 118 | } | ||
| 119 | |||
| 120 | if (scope_id) { | ||
| 121 | return result; | ||
| 122 | } | ||
| 123 | |||
| 124 | if (index == 14) { | ||
| 125 | ip_slice[14] = @truncate(u8, x >> 8); | ||
| 126 | ip_slice[15] = @truncate(u8, x); | ||
| 127 | return result; | ||
| 128 | } | ||
| 129 | |||
| 130 | return error.Incomplete; | ||
| 131 | } | ||
| 132 | |||
| 133 | pub fn parseIp4(buf: []const u8, port: u16) !IpAddress { | ||
| 134 | var result = IpAddress{ | ||
| 135 | .in = os.sockaddr_in{ | ||
| 136 | .port = mem.nativeToBig(u16, port), | ||
| 137 | .addr = undefined, | ||
| 30 | }, | 138 | }, |
| 31 | }; | 139 | }; |
| 140 | const out_ptr = @sliceToBytes((*[1]u32)(&result.in.addr)[0..]); | ||
| 141 | |||
| 142 | var x: u8 = 0; | ||
| 143 | var index: u8 = 0; | ||
| 144 | var saw_any_digits = false; | ||
| 145 | for (buf) |c| { | ||
| 146 | if (c == '.') { | ||
| 147 | if (!saw_any_digits) { | ||
| 148 | return error.InvalidCharacter; | ||
| 149 | } | ||
| 150 | if (index == 3) { | ||
| 151 | return error.InvalidEnd; | ||
| 152 | } | ||
| 153 | out_ptr[index] = x; | ||
| 154 | index += 1; | ||
| 155 | x = 0; | ||
| 156 | saw_any_digits = false; | ||
| 157 | } else if (c >= '0' and c <= '9') { | ||
| 158 | saw_any_digits = true; | ||
| 159 | x = try std.math.mul(u8, x, 10); | ||
| 160 | x = try std.math.add(u8, x, c - '0'); | ||
| 161 | } else { | ||
| 162 | return error.InvalidCharacter; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | if (index == 3 and saw_any_digits) { | ||
| 166 | out_ptr[index] = x; | ||
| 167 | return result; | ||
| 168 | } | ||
| 169 | |||
| 170 | return error.Incomplete; | ||
| 32 | } | 171 | } |
| 33 | 172 | ||
| 34 | pub fn initIp6(ip6: *const Ip6Addr, _port: u16) Address { | 173 | pub fn initIp4(addr: [4]u8, port: u16) IpAddress { |
| 35 | return Address{ | 174 | return IpAddress{ |
| 36 | .os_addr = os.sockaddr{ | 175 | .in = os.sockaddr_in{ |
| 37 | .in6 = os.sockaddr_in6{ | 176 | .port = mem.nativeToBig(u16, port), |
| 38 | .family = os.AF_INET6, | 177 | .addr = @ptrCast(*align(1) const u32, &addr).*, |
| 39 | .port = mem.nativeToBig(u16, _port), | ||
| 40 | .flowinfo = 0, | ||
| 41 | .addr = ip6.addr, | ||
| 42 | .scope_id = ip6.scope_id, | ||
| 43 | }, | ||
| 44 | }, | 178 | }, |
| 45 | }; | 179 | }; |
| 46 | } | 180 | } |
| 47 | 181 | ||
| 48 | pub fn port(self: Address) u16 { | 182 | pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) IpAddress { |
| 49 | return mem.bigToNative(u16, self.os_addr.in.port); | 183 | return IpAddress{ |
| 184 | .in6 = os.sockaddr_in6{ | ||
| 185 | .addr = addr, | ||
| 186 | .port = mem.nativeToBig(u16, port), | ||
| 187 | .flowinfo = flowinfo, | ||
| 188 | .scope_id = scope_id, | ||
| 189 | }, | ||
| 190 | }; | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Returns the port in native endian. | ||
| 194 | pub fn getPort(self: IpAddress) u16 { | ||
| 195 | const big_endian_port = switch (self.any.family) { | ||
| 196 | os.AF_INET => self.in.port, | ||
| 197 | os.AF_INET6 => self.in6.port, | ||
| 198 | else => unreachable, | ||
| 199 | }; | ||
| 200 | return mem.bigToNative(u16, big_endian_port); | ||
| 201 | } | ||
| 202 | |||
| 203 | /// `port` is native-endian. | ||
| 204 | pub fn setPort(self: *IpAddress, port: u16) void { | ||
| 205 | const ptr = switch (self.any.family) { | ||
| 206 | os.AF_INET => &self.in.port, | ||
| 207 | os.AF_INET6 => &self.in6.port, | ||
| 208 | else => unreachable, | ||
| 209 | }; | ||
| 210 | ptr.* = mem.nativeToBig(u16, port); | ||
| 50 | } | 211 | } |
| 51 | 212 | ||
| 52 | pub fn initPosix(addr: os.sockaddr) Address { | 213 | /// Asserts that `addr` is an IP address. |
| 53 | return Address{ .os_addr = addr }; | 214 | /// This function will read past the end of the pointer, with a size depending |
| 215 | /// on the address family. | ||
| 216 | pub fn initPosix(addr: *align(4) const os.sockaddr) IpAddress { | ||
| 217 | switch (addr.family) { | ||
| 218 | os.AF_INET => return IpAddress{ .in = @ptrCast(*const os.sockaddr_in, addr).* }, | ||
| 219 | os.AF_INET6 => return IpAddress{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* }, | ||
| 220 | else => unreachable, | ||
| 221 | } | ||
| 54 | } | 222 | } |
| 55 | 223 | ||
| 56 | pub fn format(self: *const Address, out_stream: var) !void { | 224 | pub fn format( |
| 57 | switch (self.os_addr.in.family) { | 225 | self: IpAddress, |
| 226 | comptime fmt: []const u8, | ||
| 227 | options: std.fmt.FormatOptions, | ||
| 228 | context: var, | ||
| 229 | comptime Errors: type, | ||
| 230 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 231 | ) !void { | ||
| 232 | switch (self.any.family) { | ||
| 58 | os.AF_INET => { | 233 | os.AF_INET => { |
| 59 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in.port); | 234 | const port = mem.bigToNative(u16, self.in.port); |
| 60 | const bytes = ([]const u8)((*self.os_addr.in.addr)[0..1]); | 235 | const bytes = @ptrCast(*const [4]u8, &self.in.addr); |
| 61 | try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port); | 236 | try std.fmt.format( |
| 237 | context, | ||
| 238 | Errors, | ||
| 239 | output, | ||
| 240 | "{}.{}.{}.{}:{}", | ||
| 241 | bytes[0], | ||
| 242 | bytes[1], | ||
| 243 | bytes[2], | ||
| 244 | bytes[3], | ||
| 245 | port, | ||
| 246 | ); | ||
| 62 | }, | 247 | }, |
| 63 | os.AF_INET6 => { | 248 | os.AF_INET6 => { |
| 64 | const native_endian_port = mem.bigToNative(u16, self.os_addr.in6.port); | 249 | const ZeroRun = struct { |
| 65 | try out_stream.print("[TODO render ip6 address]:{}", native_endian_port); | 250 | index: usize, |
| 251 | count: usize, | ||
| 252 | }; | ||
| 253 | const port = mem.bigToNative(u16, self.in6.port); | ||
| 254 | const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr); | ||
| 255 | const native_endian_parts = switch (builtin.endian) { | ||
| 256 | .Big => big_endian_parts.*, | ||
| 257 | .Little => blk: { | ||
| 258 | var buf: [8]u16 = undefined; | ||
| 259 | for (big_endian_parts) |part, i| { | ||
| 260 | buf[i] = mem.bigToNative(u16, part); | ||
| 261 | } | ||
| 262 | break :blk buf; | ||
| 263 | }, | ||
| 264 | }; | ||
| 265 | |||
| 266 | var longest_zero_run: ?ZeroRun = null; | ||
| 267 | var this_zero_run: ?ZeroRun = null; | ||
| 268 | for (native_endian_parts) |part, i| { | ||
| 269 | if (part == 0) { | ||
| 270 | if (this_zero_run) |*zr| { | ||
| 271 | zr.count += 1; | ||
| 272 | } else { | ||
| 273 | this_zero_run = ZeroRun{ | ||
| 274 | .index = i, | ||
| 275 | .count = 1, | ||
| 276 | }; | ||
| 277 | } | ||
| 278 | } else if (this_zero_run) |zr| { | ||
| 279 | if (longest_zero_run) |lzr| { | ||
| 280 | if (zr.count > lzr.count and zr.count > 1) { | ||
| 281 | longest_zero_run = zr; | ||
| 282 | } | ||
| 283 | } else { | ||
| 284 | longest_zero_run = zr; | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | try output(context, "["); | ||
| 289 | var i: usize = 0; | ||
| 290 | while (i < native_endian_parts.len) { | ||
| 291 | if (i != 0) try output(context, ":"); | ||
| 292 | |||
| 293 | if (longest_zero_run) |lzr| { | ||
| 294 | if (lzr.index == i) { | ||
| 295 | i += lzr.count; | ||
| 296 | continue; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | const part = native_endian_parts[i]; | ||
| 301 | try std.fmt.format(context, Errors, output, "{x}", part); | ||
| 302 | i += 1; | ||
| 303 | } | ||
| 304 | try std.fmt.format(context, Errors, output, "]:{}", port); | ||
| 66 | }, | 305 | }, |
| 67 | else => try out_stream.write("(unrecognized address family)"), | 306 | else => unreachable, |
| 307 | } | ||
| 308 | } | ||
| 309 | |||
| 310 | pub fn eql(a: IpAddress, b: IpAddress) bool { | ||
| 311 | const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()]; | ||
| 312 | const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()]; | ||
| 313 | return mem.eql(u8, a_bytes, b_bytes); | ||
| 314 | } | ||
| 315 | |||
| 316 | fn getOsSockLen(self: IpAddress) os.socklen_t { | ||
| 317 | switch (self.any.family) { | ||
| 318 | os.AF_INET => return @sizeOf(os.sockaddr_in), | ||
| 319 | os.AF_INET6 => return @sizeOf(os.sockaddr_in6), | ||
| 320 | else => unreachable, | ||
| 68 | } | 321 | } |
| 69 | } | 322 | } |
| 70 | }; | 323 | }; |
| 71 | 324 | ||
| 72 | pub fn parseIp4(buf: []const u8) !u32 { | 325 | pub fn connectUnixSocket(path: []const u8) !fs.File { |
| 73 | var result: u32 = undefined; | 326 | const opt_non_block = if (std.io.mode == .evented) os.SOCK_NONBLOCK else 0; |
| 74 | const out_ptr = @sliceToBytes((*[1]u32)(&result)[0..]); | 327 | const sockfd = try os.socket( |
| 328 | os.AF_UNIX, | ||
| 329 | os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block, | ||
| 330 | 0, | ||
| 331 | ); | ||
| 332 | errdefer os.close(sockfd); | ||
| 75 | 333 | ||
| 76 | var x: u8 = 0; | 334 | var sock_addr = os.sockaddr_un{ |
| 77 | var index: u8 = 0; | 335 | .family = os.AF_UNIX, |
| 78 | var saw_any_digits = false; | 336 | .path = undefined, |
| 79 | for (buf) |c| { | 337 | }; |
| 80 | if (c == '.') { | 338 | |
| 81 | if (!saw_any_digits) { | 339 | if (path.len > sock_addr.path.len) return error.NameTooLong; |
| 82 | return error.InvalidCharacter; | 340 | mem.copy(u8, &sock_addr.path, path); |
| 83 | } | 341 | |
| 84 | if (index == 3) { | 342 | const size = @intCast(u32, @sizeOf(os.sockaddr_un) - sock_addr.path.len + path.len); |
| 85 | return error.InvalidEnd; | 343 | try os.connect(sockfd, &sock_addr, size); |
| 86 | } | 344 | |
| 87 | out_ptr[index] = x; | 345 | return fs.File.openHandle(sockfd); |
| 88 | index += 1; | 346 | } |
| 89 | x = 0; | 347 | |
| 90 | saw_any_digits = false; | 348 | pub const AddressList = struct { |
| 91 | } else if (c >= '0' and c <= '9') { | 349 | arena: std.heap.ArenaAllocator, |
| 92 | saw_any_digits = true; | 350 | addrs: []IpAddress, |
| 93 | const digit = c - '0'; | 351 | canon_name: ?[]u8, |
| 94 | if (@mulWithOverflow(u8, x, 10, &x)) { | 352 | |
| 95 | return error.Overflow; | 353 | fn deinit(self: *AddressList) void { |
| 354 | // Here we copy the arena allocator into stack memory, because | ||
| 355 | // otherwise it would destroy itself while it was still working. | ||
| 356 | var arena = self.arena; | ||
| 357 | arena.deinit(); | ||
| 358 | // self is destroyed | ||
| 359 | } | ||
| 360 | }; | ||
| 361 | |||
| 362 | /// All memory allocated with `allocator` will be freed before this function returns. | ||
| 363 | pub fn tcpConnectToHost(allocator: *mem.Allocator, name: []const u8, port: u16) !fs.File { | ||
| 364 | const list = getAddressList(allocator, name, port); | ||
| 365 | defer list.deinit(); | ||
| 366 | |||
| 367 | const addrs = list.addrs.toSliceConst(); | ||
| 368 | if (addrs.len == 0) return error.UnknownHostName; | ||
| 369 | |||
| 370 | return tcpConnectToAddress(addrs[0], port); | ||
| 371 | } | ||
| 372 | |||
| 373 | pub fn tcpConnectToAddress(address: IpAddress) !fs.File { | ||
| 374 | const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; | ||
| 375 | const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; | ||
| 376 | const sockfd = try os.socket(address.any.family, sock_flags, os.IPPROTO_TCP); | ||
| 377 | errdefer os.close(sockfd); | ||
| 378 | try os.connect(sockfd, &address.any, address.getOsSockLen()); | ||
| 379 | |||
| 380 | return fs.File{ .handle = sockfd }; | ||
| 381 | } | ||
| 382 | |||
| 383 | /// Call `AddressList.deinit` on the result. | ||
| 384 | pub fn getAddressList(allocator: *mem.Allocator, name: []const u8, port: u16) !*AddressList { | ||
| 385 | const result = blk: { | ||
| 386 | var arena = std.heap.ArenaAllocator.init(allocator); | ||
| 387 | errdefer arena.deinit(); | ||
| 388 | |||
| 389 | const result = try arena.allocator.create(AddressList); | ||
| 390 | result.* = AddressList{ | ||
| 391 | .arena = arena, | ||
| 392 | .addrs = undefined, | ||
| 393 | .canon_name = null, | ||
| 394 | }; | ||
| 395 | break :blk result; | ||
| 396 | }; | ||
| 397 | const arena = &result.arena.allocator; | ||
| 398 | errdefer result.arena.deinit(); | ||
| 399 | |||
| 400 | if (builtin.link_libc) { | ||
| 401 | const c = std.c; | ||
| 402 | const name_c = try std.cstr.addNullByte(allocator, name); | ||
| 403 | defer allocator.free(name_c); | ||
| 404 | |||
| 405 | const port_c = try std.fmt.allocPrint(allocator, "{}\x00", port); | ||
| 406 | defer allocator.free(port_c); | ||
| 407 | |||
| 408 | const hints = os.addrinfo{ | ||
| 409 | .flags = c.AI_NUMERICSERV, | ||
| 410 | .family = os.AF_UNSPEC, | ||
| 411 | .socktype = os.SOCK_STREAM, | ||
| 412 | .protocol = os.IPPROTO_TCP, | ||
| 413 | .canonname = null, | ||
| 414 | .addr = null, | ||
| 415 | .addrlen = 0, | ||
| 416 | .next = null, | ||
| 417 | }; | ||
| 418 | var res: *os.addrinfo = undefined; | ||
| 419 | switch (os.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { | ||
| 420 | 0 => {}, | ||
| 421 | c.EAI_ADDRFAMILY => return error.HostLacksNetworkAddresses, | ||
| 422 | c.EAI_AGAIN => return error.TemporaryNameServerFailure, | ||
| 423 | c.EAI_BADFLAGS => unreachable, // Invalid hints | ||
| 424 | c.EAI_FAIL => return error.NameServerFailure, | ||
| 425 | c.EAI_FAMILY => return error.AddressFamilyNotSupported, | ||
| 426 | c.EAI_MEMORY => return error.OutOfMemory, | ||
| 427 | c.EAI_NODATA => return error.HostLacksNetworkAddresses, | ||
| 428 | c.EAI_NONAME => return error.UnknownHostName, | ||
| 429 | c.EAI_SERVICE => return error.ServiceUnavailable, | ||
| 430 | c.EAI_SOCKTYPE => unreachable, // Invalid socket type requested in hints | ||
| 431 | c.EAI_SYSTEM => switch (os.errno(-1)) { | ||
| 432 | else => |e| return os.unexpectedErrno(e), | ||
| 433 | }, | ||
| 434 | else => unreachable, | ||
| 435 | } | ||
| 436 | defer os.system.freeaddrinfo(res); | ||
| 437 | |||
| 438 | const addr_count = blk: { | ||
| 439 | var count: usize = 0; | ||
| 440 | var it: ?*os.addrinfo = res; | ||
| 441 | while (it) |info| : (it = info.next) { | ||
| 442 | if (info.addr != null) { | ||
| 443 | count += 1; | ||
| 444 | } | ||
| 96 | } | 445 | } |
| 97 | if (@addWithOverflow(u8, x, digit, &x)) { | 446 | break :blk count; |
| 98 | return error.Overflow; | 447 | }; |
| 448 | result.addrs = try arena.alloc(IpAddress, addr_count); | ||
| 449 | |||
| 450 | var it: ?*os.addrinfo = res; | ||
| 451 | var i: usize = 0; | ||
| 452 | while (it) |info| : (it = info.next) { | ||
| 453 | const addr = info.addr orelse continue; | ||
| 454 | result.addrs[i] = IpAddress.initPosix(@alignCast(4, addr)); | ||
| 455 | |||
| 456 | if (info.canonname) |n| { | ||
| 457 | if (result.canon_name == null) { | ||
| 458 | result.canon_name = try mem.dupe(arena, u8, mem.toSliceConst(u8, n)); | ||
| 459 | } | ||
| 99 | } | 460 | } |
| 100 | } else { | 461 | i += 1; |
| 101 | return error.InvalidCharacter; | ||
| 102 | } | 462 | } |
| 103 | } | 463 | |
| 104 | if (index == 3 and saw_any_digits) { | ||
| 105 | out_ptr[index] = x; | ||
| 106 | return result; | 464 | return result; |
| 107 | } | 465 | } |
| 466 | if (builtin.os == .linux) { | ||
| 467 | const flags = std.c.AI_NUMERICSERV; | ||
| 468 | const family = os.AF_UNSPEC; | ||
| 469 | var lookup_addrs = std.ArrayList(LookupAddr).init(allocator); | ||
| 470 | defer lookup_addrs.deinit(); | ||
| 471 | |||
| 472 | var canon = std.Buffer.initNull(arena); | ||
| 473 | defer canon.deinit(); | ||
| 474 | |||
| 475 | try linuxLookupName(&lookup_addrs, &canon, name, family, flags, port); | ||
| 476 | |||
| 477 | result.addrs = try arena.alloc(IpAddress, lookup_addrs.len); | ||
| 478 | if (!canon.isNull()) { | ||
| 479 | result.canon_name = canon.toOwnedSlice(); | ||
| 480 | } | ||
| 108 | 481 | ||
| 109 | return error.Incomplete; | 482 | for (lookup_addrs.toSliceConst()) |lookup_addr, i| { |
| 483 | result.addrs[i] = lookup_addr.addr; | ||
| 484 | assert(result.addrs[i].getPort() == port); | ||
| 485 | } | ||
| 486 | |||
| 487 | return result; | ||
| 488 | } | ||
| 489 | @compileError("std.net.getAddresses unimplemented for this OS"); | ||
| 110 | } | 490 | } |
| 111 | 491 | ||
| 112 | pub const Ip6Addr = struct { | 492 | const LookupAddr = struct { |
| 113 | scope_id: u32, | 493 | addr: IpAddress, |
| 114 | addr: [16]u8, | 494 | sortkey: i32 = 0, |
| 115 | }; | 495 | }; |
| 116 | 496 | ||
| 117 | pub fn parseIp6(buf: []const u8) !Ip6Addr { | 497 | const DAS_USABLE = 0x40000000; |
| 118 | var result: Ip6Addr = undefined; | 498 | const DAS_MATCHINGSCOPE = 0x20000000; |
| 119 | result.scope_id = 0; | 499 | const DAS_MATCHINGLABEL = 0x10000000; |
| 120 | const ip_slice = result.addr[0..]; | 500 | const DAS_PREC_SHIFT = 20; |
| 501 | const DAS_SCOPE_SHIFT = 16; | ||
| 502 | const DAS_PREFIX_SHIFT = 8; | ||
| 503 | const DAS_ORDER_SHIFT = 0; | ||
| 121 | 504 | ||
| 122 | var x: u16 = 0; | 505 | fn linuxLookupName( |
| 123 | var saw_any_digits = false; | 506 | addrs: *std.ArrayList(LookupAddr), |
| 124 | var index: u8 = 0; | 507 | canon: *std.Buffer, |
| 125 | var scope_id = false; | 508 | opt_name: ?[]const u8, |
| 126 | for (buf) |c| { | 509 | family: os.sa_family_t, |
| 127 | if (scope_id) { | 510 | flags: u32, |
| 128 | if (c >= '0' and c <= '9') { | 511 | port: u16, |
| 129 | const digit = c - '0'; | 512 | ) !void { |
| 130 | if (@mulWithOverflow(u32, result.scope_id, 10, &result.scope_id)) { | 513 | if (opt_name) |name| { |
| 131 | return error.Overflow; | 514 | // reject empty name and check len so it fits into temp bufs |
| 132 | } | 515 | try canon.replaceContents(name); |
| 133 | if (@addWithOverflow(u32, result.scope_id, digit, &result.scope_id)) { | 516 | if (IpAddress.parseExpectingFamily(name, family, port)) |addr| { |
| 134 | return error.Overflow; | 517 | try addrs.append(LookupAddr{ .addr = addr }); |
| 135 | } | 518 | } else |name_err| if ((flags & std.c.AI_NUMERICHOST) != 0) { |
| 136 | } else { | 519 | return name_err; |
| 137 | return error.InvalidCharacter; | 520 | } else { |
| 138 | } | 521 | try linuxLookupNameFromHosts(addrs, canon, name, family, port); |
| 139 | } else if (c == ':') { | 522 | if (addrs.len == 0) { |
| 140 | if (!saw_any_digits) { | 523 | try linuxLookupNameFromDnsSearch(addrs, canon, name, family, port); |
| 141 | return error.InvalidCharacter; | ||
| 142 | } | ||
| 143 | if (index == 14) { | ||
| 144 | return error.InvalidEnd; | ||
| 145 | } | ||
| 146 | ip_slice[index] = @truncate(u8, x >> 8); | ||
| 147 | index += 1; | ||
| 148 | ip_slice[index] = @truncate(u8, x); | ||
| 149 | index += 1; | ||
| 150 | |||
| 151 | x = 0; | ||
| 152 | saw_any_digits = false; | ||
| 153 | } else if (c == '%') { | ||
| 154 | if (!saw_any_digits) { | ||
| 155 | return error.InvalidCharacter; | ||
| 156 | } | ||
| 157 | if (index == 14) { | ||
| 158 | ip_slice[index] = @truncate(u8, x >> 8); | ||
| 159 | index += 1; | ||
| 160 | ip_slice[index] = @truncate(u8, x); | ||
| 161 | index += 1; | ||
| 162 | } | 524 | } |
| 163 | scope_id = true; | 525 | } |
| 164 | saw_any_digits = false; | 526 | } else { |
| 527 | try canon.resize(0); | ||
| 528 | try linuxLookupNameFromNull(addrs, family, flags, port); | ||
| 529 | } | ||
| 530 | if (addrs.len == 0) return error.UnknownHostName; | ||
| 531 | |||
| 532 | // No further processing is needed if there are fewer than 2 | ||
| 533 | // results or if there are only IPv4 results. | ||
| 534 | if (addrs.len == 1 or family == os.AF_INET) return; | ||
| 535 | const all_ip4 = for (addrs.toSliceConst()) |addr| { | ||
| 536 | if (addr.addr.any.family != os.AF_INET) break false; | ||
| 537 | } else true; | ||
| 538 | if (all_ip4) return; | ||
| 539 | |||
| 540 | // The following implements a subset of RFC 3484/6724 destination | ||
| 541 | // address selection by generating a single 31-bit sort key for | ||
| 542 | // each address. Rules 3, 4, and 7 are omitted for having | ||
| 543 | // excessive runtime and code size cost and dubious benefit. | ||
| 544 | // So far the label/precedence table cannot be customized. | ||
| 545 | // This implementation is ported from musl libc. | ||
| 546 | // A more idiomatic "ziggy" implementation would be welcome. | ||
| 547 | for (addrs.toSlice()) |*addr, i| { | ||
| 548 | var key: i32 = 0; | ||
| 549 | var sa6: os.sockaddr_in6 = undefined; | ||
| 550 | @memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6)); | ||
| 551 | var da6 = os.sockaddr_in6{ | ||
| 552 | .family = os.AF_INET6, | ||
| 553 | .scope_id = addr.addr.in6.scope_id, | ||
| 554 | .port = 65535, | ||
| 555 | .flowinfo = 0, | ||
| 556 | .addr = [1]u8{0} ** 16, | ||
| 557 | }; | ||
| 558 | var sa4: os.sockaddr_in = undefined; | ||
| 559 | @memset(@ptrCast([*]u8, &sa4), 0, @sizeOf(os.sockaddr_in)); | ||
| 560 | var da4 = os.sockaddr_in{ | ||
| 561 | .family = os.AF_INET, | ||
| 562 | .port = 65535, | ||
| 563 | .addr = 0, | ||
| 564 | .zero = [1]u8{0} ** 8, | ||
| 565 | }; | ||
| 566 | var sa: *align(4) os.sockaddr = undefined; | ||
| 567 | var da: *align(4) os.sockaddr = undefined; | ||
| 568 | var salen: os.socklen_t = undefined; | ||
| 569 | var dalen: os.socklen_t = undefined; | ||
| 570 | if (addr.addr.any.family == os.AF_INET6) { | ||
| 571 | mem.copy(u8, &da6.addr, &addr.addr.in6.addr); | ||
| 572 | da = @ptrCast(*os.sockaddr, &da6); | ||
| 573 | dalen = @sizeOf(os.sockaddr_in6); | ||
| 574 | sa = @ptrCast(*os.sockaddr, &sa6); | ||
| 575 | salen = @sizeOf(os.sockaddr_in6); | ||
| 165 | } else { | 576 | } else { |
| 166 | const digit = try std.fmt.charToDigit(c, 16); | 577 | mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); |
| 167 | if (@mulWithOverflow(u16, x, 16, &x)) { | 578 | mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff"); |
| 168 | return error.Overflow; | 579 | // TODO https://github.com/ziglang/zig/issues/863 |
| 580 | mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr); | ||
| 581 | da4.addr = addr.addr.in.addr; | ||
| 582 | da = @ptrCast(*os.sockaddr, &da4); | ||
| 583 | dalen = @sizeOf(os.sockaddr_in); | ||
| 584 | sa = @ptrCast(*os.sockaddr, &sa4); | ||
| 585 | salen = @sizeOf(os.sockaddr_in); | ||
| 586 | } | ||
| 587 | const dpolicy = policyOf(da6.addr); | ||
| 588 | const dscope: i32 = scopeOf(da6.addr); | ||
| 589 | const dlabel = dpolicy.label; | ||
| 590 | const dprec: i32 = dpolicy.prec; | ||
| 591 | const MAXADDRS = 3; | ||
| 592 | var prefixlen: i32 = 0; | ||
| 593 | const sock_flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC; | ||
| 594 | if (os.socket(addr.addr.any.family, sock_flags, os.IPPROTO_UDP)) |fd| syscalls: { | ||
| 595 | defer os.close(fd); | ||
| 596 | os.connect(fd, da, dalen) catch break :syscalls; | ||
| 597 | key |= DAS_USABLE; | ||
| 598 | os.getsockname(fd, sa, &salen) catch break :syscalls; | ||
| 599 | if (addr.addr.any.family == os.AF_INET) { | ||
| 600 | // TODO sa6.addr[12..16] should return *[4]u8, making this cast unnecessary. | ||
| 601 | mem.writeIntNative(u32, @ptrCast(*[4]u8, &sa6.addr[12]), sa4.addr); | ||
| 169 | } | 602 | } |
| 170 | if (@addWithOverflow(u16, x, digit, &x)) { | 603 | if (dscope == i32(scopeOf(sa6.addr))) key |= DAS_MATCHINGSCOPE; |
| 171 | return error.Overflow; | 604 | if (dlabel == labelOf(sa6.addr)) key |= DAS_MATCHINGLABEL; |
| 605 | prefixlen = prefixMatch(sa6.addr, da6.addr); | ||
| 606 | } else |_| {} | ||
| 607 | key |= dprec << DAS_PREC_SHIFT; | ||
| 608 | key |= (15 - dscope) << DAS_SCOPE_SHIFT; | ||
| 609 | key |= prefixlen << DAS_PREFIX_SHIFT; | ||
| 610 | key |= (MAXADDRS - @intCast(i32, i)) << DAS_ORDER_SHIFT; | ||
| 611 | addr.sortkey = key; | ||
| 612 | } | ||
| 613 | std.sort.sort(LookupAddr, addrs.toSlice(), addrCmpLessThan); | ||
| 614 | } | ||
| 615 | |||
| 616 | const Policy = struct { | ||
| 617 | addr: [16]u8, | ||
| 618 | len: u8, | ||
| 619 | mask: u8, | ||
| 620 | prec: u8, | ||
| 621 | label: u8, | ||
| 622 | }; | ||
| 623 | |||
| 624 | const defined_policies = [_]Policy{ | ||
| 625 | Policy{ | ||
| 626 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", | ||
| 627 | .len = 15, | ||
| 628 | .mask = 0xff, | ||
| 629 | .prec = 50, | ||
| 630 | .label = 0, | ||
| 631 | }, | ||
| 632 | Policy{ | ||
| 633 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00", | ||
| 634 | .len = 11, | ||
| 635 | .mask = 0xff, | ||
| 636 | .prec = 35, | ||
| 637 | .label = 4, | ||
| 638 | }, | ||
| 639 | Policy{ | ||
| 640 | .addr = "\x20\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", | ||
| 641 | .len = 1, | ||
| 642 | .mask = 0xff, | ||
| 643 | .prec = 30, | ||
| 644 | .label = 2, | ||
| 645 | }, | ||
| 646 | Policy{ | ||
| 647 | .addr = "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", | ||
| 648 | .len = 3, | ||
| 649 | .mask = 0xff, | ||
| 650 | .prec = 5, | ||
| 651 | .label = 5, | ||
| 652 | }, | ||
| 653 | Policy{ | ||
| 654 | .addr = "\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", | ||
| 655 | .len = 0, | ||
| 656 | .mask = 0xfe, | ||
| 657 | .prec = 3, | ||
| 658 | .label = 13, | ||
| 659 | }, | ||
| 660 | // These are deprecated and/or returned to the address | ||
| 661 | // pool, so despite the RFC, treating them as special | ||
| 662 | // is probably wrong. | ||
| 663 | // { "", 11, 0xff, 1, 3 }, | ||
| 664 | // { "\xfe\xc0", 1, 0xc0, 1, 11 }, | ||
| 665 | // { "\x3f\xfe", 1, 0xff, 1, 12 }, | ||
| 666 | // Last rule must match all addresses to stop loop. | ||
| 667 | Policy{ | ||
| 668 | .addr = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", | ||
| 669 | .len = 0, | ||
| 670 | .mask = 0, | ||
| 671 | .prec = 40, | ||
| 672 | .label = 1, | ||
| 673 | }, | ||
| 674 | }; | ||
| 675 | |||
| 676 | fn policyOf(a: [16]u8) *const Policy { | ||
| 677 | for (defined_policies) |*policy| { | ||
| 678 | if (!mem.eql(u8, a[0..policy.len], policy.addr[0..policy.len])) continue; | ||
| 679 | if ((a[policy.len] & policy.mask) != policy.addr[policy.len]) continue; | ||
| 680 | return policy; | ||
| 681 | } | ||
| 682 | unreachable; | ||
| 683 | } | ||
| 684 | |||
| 685 | fn scopeOf(a: [16]u8) u8 { | ||
| 686 | if (IN6_IS_ADDR_MULTICAST(a)) return a[1] & 15; | ||
| 687 | if (IN6_IS_ADDR_LINKLOCAL(a)) return 2; | ||
| 688 | if (IN6_IS_ADDR_LOOPBACK(a)) return 2; | ||
| 689 | if (IN6_IS_ADDR_SITELOCAL(a)) return 5; | ||
| 690 | return 14; | ||
| 691 | } | ||
| 692 | |||
| 693 | fn prefixMatch(s: [16]u8, d: [16]u8) u8 { | ||
| 694 | // TODO: This FIXME inherited from porting from musl libc. | ||
| 695 | // I don't want this to go into zig std lib 1.0.0. | ||
| 696 | |||
| 697 | // FIXME: The common prefix length should be limited to no greater | ||
| 698 | // than the nominal length of the prefix portion of the source | ||
| 699 | // address. However the definition of the source prefix length is | ||
| 700 | // not clear and thus this limiting is not yet implemented. | ||
| 701 | var i: u8 = 0; | ||
| 702 | while (i < 128 and ((s[i / 8] ^ d[i / 8]) & (u8(128) >> @intCast(u3, i % 8))) == 0) : (i += 1) {} | ||
| 703 | return i; | ||
| 704 | } | ||
| 705 | |||
| 706 | fn labelOf(a: [16]u8) u8 { | ||
| 707 | return policyOf(a).label; | ||
| 708 | } | ||
| 709 | |||
| 710 | fn IN6_IS_ADDR_MULTICAST(a: [16]u8) bool { | ||
| 711 | return a[0] == 0xff; | ||
| 712 | } | ||
| 713 | |||
| 714 | fn IN6_IS_ADDR_LINKLOCAL(a: [16]u8) bool { | ||
| 715 | return a[0] == 0xfe and (a[1] & 0xc0) == 0x80; | ||
| 716 | } | ||
| 717 | |||
| 718 | fn IN6_IS_ADDR_LOOPBACK(a: [16]u8) bool { | ||
| 719 | return a[0] == 0 and a[1] == 0 and | ||
| 720 | a[2] == 0 and | ||
| 721 | a[12] == 0 and a[13] == 0 and | ||
| 722 | a[14] == 0 and a[15] == 1; | ||
| 723 | } | ||
| 724 | |||
| 725 | fn IN6_IS_ADDR_SITELOCAL(a: [16]u8) bool { | ||
| 726 | return a[0] == 0xfe and (a[1] & 0xc0) == 0xc0; | ||
| 727 | } | ||
| 728 | |||
| 729 | // Parameters `b` and `a` swapped to make this descending. | ||
| 730 | fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool { | ||
| 731 | return a.sortkey < b.sortkey; | ||
| 732 | } | ||
| 733 | |||
| 734 | fn linuxLookupNameFromNull( | ||
| 735 | addrs: *std.ArrayList(LookupAddr), | ||
| 736 | family: os.sa_family_t, | ||
| 737 | flags: u32, | ||
| 738 | port: u16, | ||
| 739 | ) !void { | ||
| 740 | if ((flags & std.c.AI_PASSIVE) != 0) { | ||
| 741 | if (family != os.AF_INET6) { | ||
| 742 | (try addrs.addOne()).* = LookupAddr{ | ||
| 743 | .addr = IpAddress.initIp4([1]u8{0} ** 4, port), | ||
| 744 | }; | ||
| 745 | } | ||
| 746 | if (family != os.AF_INET) { | ||
| 747 | (try addrs.addOne()).* = LookupAddr{ | ||
| 748 | .addr = IpAddress.initIp6([1]u8{0} ** 16, port, 0, 0), | ||
| 749 | }; | ||
| 750 | } | ||
| 751 | } else { | ||
| 752 | if (family != os.AF_INET6) { | ||
| 753 | (try addrs.addOne()).* = LookupAddr{ | ||
| 754 | .addr = IpAddress.initIp4([4]u8{ 127, 0, 0, 1 }, port), | ||
| 755 | }; | ||
| 756 | } | ||
| 757 | if (family != os.AF_INET) { | ||
| 758 | (try addrs.addOne()).* = LookupAddr{ | ||
| 759 | .addr = IpAddress.initIp6(([1]u8{0} ** 15) ++ [1]u8{1}, port, 0, 0), | ||
| 760 | }; | ||
| 761 | } | ||
| 762 | } | ||
| 763 | } | ||
| 764 | |||
| 765 | fn linuxLookupNameFromHosts( | ||
| 766 | addrs: *std.ArrayList(LookupAddr), | ||
| 767 | canon: *std.Buffer, | ||
| 768 | name: []const u8, | ||
| 769 | family: os.sa_family_t, | ||
| 770 | port: u16, | ||
| 771 | ) !void { | ||
| 772 | const file = fs.File.openReadC(c"/etc/hosts") catch |err| switch (err) { | ||
| 773 | error.FileNotFound, | ||
| 774 | error.NotDir, | ||
| 775 | error.AccessDenied, | ||
| 776 | => return, | ||
| 777 | else => |e| return e, | ||
| 778 | }; | ||
| 779 | defer file.close(); | ||
| 780 | |||
| 781 | const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; | ||
| 782 | var line_buf: [512]u8 = undefined; | ||
| 783 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { | ||
| 784 | error.StreamTooLong => blk: { | ||
| 785 | // Skip to the delimiter in the stream, to fix parsing | ||
| 786 | try stream.skipUntilDelimiterOrEof('\n'); | ||
| 787 | // Use the truncated line. A truncated comment or hostname will be handled correctly. | ||
| 788 | break :blk line_buf[0..]; | ||
| 789 | }, | ||
| 790 | else => |e| return e, | ||
| 791 | }) |line| { | ||
| 792 | const no_comment_line = mem.separate(line, "#").next().?; | ||
| 793 | |||
| 794 | var line_it = mem.tokenize(no_comment_line, " \t"); | ||
| 795 | const ip_text = line_it.next() orelse continue; | ||
| 796 | var first_name_text: ?[]const u8 = null; | ||
| 797 | while (line_it.next()) |name_text| { | ||
| 798 | if (first_name_text == null) first_name_text = name_text; | ||
| 799 | if (mem.eql(u8, name_text, name)) { | ||
| 800 | break; | ||
| 172 | } | 801 | } |
| 173 | saw_any_digits = true; | 802 | } else continue; |
| 803 | |||
| 804 | const addr = IpAddress.parseExpectingFamily(ip_text, family, port) catch |err| switch (err) { | ||
| 805 | error.Overflow, | ||
| 806 | error.InvalidEnd, | ||
| 807 | error.InvalidCharacter, | ||
| 808 | error.Incomplete, | ||
| 809 | error.InvalidIPAddressFormat, | ||
| 810 | => continue, | ||
| 811 | }; | ||
| 812 | try addrs.append(LookupAddr{ .addr = addr }); | ||
| 813 | |||
| 814 | // first name is canonical name | ||
| 815 | const name_text = first_name_text.?; | ||
| 816 | if (isValidHostName(name_text)) { | ||
| 817 | try canon.replaceContents(name_text); | ||
| 174 | } | 818 | } |
| 175 | } | 819 | } |
| 820 | } | ||
| 176 | 821 | ||
| 177 | if (!saw_any_digits) { | 822 | pub fn isValidHostName(hostname: []const u8) bool { |
| 178 | return error.Incomplete; | 823 | if (hostname.len >= 254) return false; |
| 824 | if (!std.unicode.utf8ValidateSlice(hostname)) return false; | ||
| 825 | for (hostname) |byte| { | ||
| 826 | if (byte >= 0x80 or byte == '.' or byte == '-' or std.ascii.isAlNum(byte)) { | ||
| 827 | continue; | ||
| 828 | } | ||
| 829 | return false; | ||
| 179 | } | 830 | } |
| 831 | return true; | ||
| 832 | } | ||
| 180 | 833 | ||
| 181 | if (scope_id) { | 834 | fn linuxLookupNameFromDnsSearch( |
| 182 | return result; | 835 | addrs: *std.ArrayList(LookupAddr), |
| 836 | canon: *std.Buffer, | ||
| 837 | name: []const u8, | ||
| 838 | family: os.sa_family_t, | ||
| 839 | port: u16, | ||
| 840 | ) !void { | ||
| 841 | var rc: ResolvConf = undefined; | ||
| 842 | try getResolvConf(addrs.allocator, &rc); | ||
| 843 | defer rc.deinit(); | ||
| 844 | |||
| 845 | // Count dots, suppress search when >=ndots or name ends in | ||
| 846 | // a dot, which is an explicit request for global scope. | ||
| 847 | var dots: usize = 0; | ||
| 848 | for (name) |byte| { | ||
| 849 | if (byte == '.') dots += 1; | ||
| 183 | } | 850 | } |
| 184 | 851 | ||
| 185 | if (index == 14) { | 852 | const search = if (rc.search.isNull() or dots >= rc.ndots or mem.endsWith(u8, name, ".")) |
| 186 | ip_slice[14] = @truncate(u8, x >> 8); | 853 | [_]u8{} |
| 187 | ip_slice[15] = @truncate(u8, x); | 854 | else |
| 188 | return result; | 855 | rc.search.toSliceConst(); |
| 856 | |||
| 857 | var canon_name = name; | ||
| 858 | |||
| 859 | // Strip final dot for canon, fail if multiple trailing dots. | ||
| 860 | if (mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1; | ||
| 861 | if (mem.endsWith(u8, canon_name, ".")) return error.UnknownHostName; | ||
| 862 | |||
| 863 | // Name with search domain appended is setup in canon[]. This both | ||
| 864 | // provides the desired default canonical name (if the requested | ||
| 865 | // name is not a CNAME record) and serves as a buffer for passing | ||
| 866 | // the full requested name to name_from_dns. | ||
| 867 | try canon.resize(canon_name.len); | ||
| 868 | mem.copy(u8, canon.toSlice(), canon_name); | ||
| 869 | try canon.appendByte('.'); | ||
| 870 | |||
| 871 | var tok_it = mem.tokenize(search, " \t"); | ||
| 872 | while (tok_it.next()) |tok| { | ||
| 873 | canon.shrink(canon_name.len + 1); | ||
| 874 | try canon.append(tok); | ||
| 875 | try linuxLookupNameFromDns(addrs, canon, canon.toSliceConst(), family, rc, port); | ||
| 876 | if (addrs.len != 0) return; | ||
| 189 | } | 877 | } |
| 190 | 878 | ||
| 191 | return error.Incomplete; | 879 | canon.shrink(canon_name.len); |
| 880 | return linuxLookupNameFromDns(addrs, canon, name, family, rc, port); | ||
| 192 | } | 881 | } |
| 193 | 882 | ||
| 194 | test "std.net.parseIp4" { | 883 | const dpc_ctx = struct { |
| 195 | assert((try parseIp4("127.0.0.1")) == mem.bigToNative(u32, 0x7f000001)); | 884 | addrs: *std.ArrayList(LookupAddr), |
| 885 | canon: *std.Buffer, | ||
| 886 | port: u16, | ||
| 887 | }; | ||
| 196 | 888 | ||
| 197 | testParseIp4Fail("256.0.0.1", error.Overflow); | 889 | fn linuxLookupNameFromDns( |
| 198 | testParseIp4Fail("x.0.0.1", error.InvalidCharacter); | 890 | addrs: *std.ArrayList(LookupAddr), |
| 199 | testParseIp4Fail("127.0.0.1.1", error.InvalidEnd); | 891 | canon: *std.Buffer, |
| 200 | testParseIp4Fail("127.0.0.", error.Incomplete); | 892 | name: []const u8, |
| 201 | testParseIp4Fail("100..0.1", error.InvalidCharacter); | 893 | family: os.sa_family_t, |
| 894 | rc: ResolvConf, | ||
| 895 | port: u16, | ||
| 896 | ) !void { | ||
| 897 | var ctx = dpc_ctx{ | ||
| 898 | .addrs = addrs, | ||
| 899 | .canon = canon, | ||
| 900 | .port = port, | ||
| 901 | }; | ||
| 902 | const AfRr = struct { | ||
| 903 | af: os.sa_family_t, | ||
| 904 | rr: u8, | ||
| 905 | }; | ||
| 906 | const afrrs = [_]AfRr{ | ||
| 907 | AfRr{ .af = os.AF_INET6, .rr = os.RR_A }, | ||
| 908 | AfRr{ .af = os.AF_INET, .rr = os.RR_AAAA }, | ||
| 909 | }; | ||
| 910 | var qbuf: [2][280]u8 = undefined; | ||
| 911 | var abuf: [2][512]u8 = undefined; | ||
| 912 | var qp: [2][]const u8 = undefined; | ||
| 913 | const apbuf = [2][]u8{ &abuf[0], &abuf[1] }; | ||
| 914 | var nq: usize = 0; | ||
| 915 | |||
| 916 | for (afrrs) |afrr| { | ||
| 917 | if (family != afrr.af) { | ||
| 918 | const len = os.res_mkquery(0, name, 1, afrr.rr, [_]u8{}, null, &qbuf[nq]); | ||
| 919 | qp[nq] = qbuf[nq][0..len]; | ||
| 920 | nq += 1; | ||
| 921 | } | ||
| 922 | } | ||
| 923 | |||
| 924 | var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] }; | ||
| 925 | try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc); | ||
| 926 | |||
| 927 | var i: usize = 0; | ||
| 928 | while (i < nq) : (i += 1) { | ||
| 929 | dnsParse(ap[i], ctx, dnsParseCallback) catch {}; | ||
| 930 | } | ||
| 931 | |||
| 932 | if (addrs.len != 0) return; | ||
| 933 | if (ap[0].len < 4 or (ap[0][3] & 15) == 2) return error.TemporaryNameServerFailure; | ||
| 934 | if ((ap[0][3] & 15) == 0) return error.UnknownHostName; | ||
| 935 | if ((ap[0][3] & 15) == 3) return; | ||
| 936 | return error.NameServerFailure; | ||
| 202 | } | 937 | } |
| 203 | 938 | ||
| 204 | fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void { | 939 | const ResolvConf = struct { |
| 205 | if (parseIp4(buf)) |_| { | 940 | attempts: u32, |
| 206 | @panic("expected error"); | 941 | ndots: u32, |
| 207 | } else |e| { | 942 | timeout: u32, |
| 208 | assert(e == expected_err); | 943 | search: std.Buffer, |
| 944 | ns: std.ArrayList(LookupAddr), | ||
| 945 | |||
| 946 | fn deinit(rc: *ResolvConf) void { | ||
| 947 | rc.ns.deinit(); | ||
| 948 | rc.search.deinit(); | ||
| 949 | rc.* = undefined; | ||
| 950 | } | ||
| 951 | }; | ||
| 952 | |||
| 953 | /// Ignores lines longer than 512 bytes. | ||
| 954 | /// TODO: https://github.com/ziglang/zig/issues/2765 and https://github.com/ziglang/zig/issues/2761 | ||
| 955 | fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { | ||
| 956 | rc.* = ResolvConf{ | ||
| 957 | .ns = std.ArrayList(LookupAddr).init(allocator), | ||
| 958 | .search = std.Buffer.initNull(allocator), | ||
| 959 | .ndots = 1, | ||
| 960 | .timeout = 5, | ||
| 961 | .attempts = 2, | ||
| 962 | }; | ||
| 963 | errdefer rc.deinit(); | ||
| 964 | |||
| 965 | const file = fs.File.openReadC(c"/etc/resolv.conf") catch |err| switch (err) { | ||
| 966 | error.FileNotFound, | ||
| 967 | error.NotDir, | ||
| 968 | error.AccessDenied, | ||
| 969 | => return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53), | ||
| 970 | else => |e| return e, | ||
| 971 | }; | ||
| 972 | defer file.close(); | ||
| 973 | |||
| 974 | const stream = &std.io.BufferedInStream(fs.File.ReadError).init(&file.inStream().stream).stream; | ||
| 975 | var line_buf: [512]u8 = undefined; | ||
| 976 | while (stream.readUntilDelimiterOrEof(&line_buf, '\n') catch |err| switch (err) { | ||
| 977 | error.StreamTooLong => blk: { | ||
| 978 | // Skip to the delimiter in the stream, to fix parsing | ||
| 979 | try stream.skipUntilDelimiterOrEof('\n'); | ||
| 980 | // Give an empty line to the while loop, which will be skipped. | ||
| 981 | break :blk line_buf[0..0]; | ||
| 982 | }, | ||
| 983 | else => |e| return e, | ||
| 984 | }) |line| { | ||
| 985 | const no_comment_line = mem.separate(line, "#").next().?; | ||
| 986 | var line_it = mem.tokenize(no_comment_line, " \t"); | ||
| 987 | |||
| 988 | const token = line_it.next() orelse continue; | ||
| 989 | if (mem.eql(u8, token, "options")) { | ||
| 990 | while (line_it.next()) |sub_tok| { | ||
| 991 | var colon_it = mem.separate(sub_tok, ":"); | ||
| 992 | const name = colon_it.next().?; | ||
| 993 | const value_txt = colon_it.next() orelse continue; | ||
| 994 | const value = std.fmt.parseInt(u8, value_txt, 10) catch |err| switch (err) { | ||
| 995 | error.Overflow => 255, | ||
| 996 | error.InvalidCharacter => continue, | ||
| 997 | }; | ||
| 998 | if (mem.eql(u8, name, "ndots")) { | ||
| 999 | rc.ndots = std.math.min(value, 15); | ||
| 1000 | } else if (mem.eql(u8, name, "attempts")) { | ||
| 1001 | rc.attempts = std.math.min(value, 10); | ||
| 1002 | } else if (mem.eql(u8, name, "timeout")) { | ||
| 1003 | rc.timeout = std.math.min(value, 60); | ||
| 1004 | } | ||
| 1005 | } | ||
| 1006 | } else if (mem.eql(u8, token, "nameserver")) { | ||
| 1007 | const ip_txt = line_it.next() orelse continue; | ||
| 1008 | try linuxLookupNameFromNumericUnspec(&rc.ns, ip_txt, 53); | ||
| 1009 | } else if (mem.eql(u8, token, "domain") or mem.eql(u8, token, "search")) { | ||
| 1010 | try rc.search.replaceContents(line_it.rest()); | ||
| 1011 | } | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | if (rc.ns.len == 0) { | ||
| 1015 | return linuxLookupNameFromNumericUnspec(&rc.ns, "127.0.0.1", 53); | ||
| 209 | } | 1016 | } |
| 210 | } | 1017 | } |
| 211 | 1018 | ||
| 212 | test "std.net.parseIp6" { | 1019 | fn linuxLookupNameFromNumericUnspec( |
| 213 | const addr = try parseIp6("FF01:0:0:0:0:0:0:FB"); | 1020 | addrs: *std.ArrayList(LookupAddr), |
| 214 | assert(addr.addr[0] == 0xff); | 1021 | name: []const u8, |
| 215 | assert(addr.addr[1] == 0x01); | 1022 | port: u16, |
| 216 | assert(addr.addr[2] == 0x00); | 1023 | ) !void { |
| 1024 | const addr = try IpAddress.parse(name, port); | ||
| 1025 | (try addrs.addOne()).* = LookupAddr{ .addr = addr }; | ||
| 217 | } | 1026 | } |
| 218 | 1027 | ||
| 219 | pub fn connectUnixSocket(path: []const u8) !std.fs.File { | 1028 | fn resMSendRc( |
| 220 | const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0; | 1029 | queries: []const []const u8, |
| 221 | const sockfd = try os.socket( | 1030 | answers: [][]u8, |
| 222 | os.AF_UNIX, | 1031 | answer_bufs: []const []u8, |
| 223 | os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block, | 1032 | rc: ResolvConf, |
| 224 | 0, | 1033 | ) !void { |
| 225 | ); | 1034 | const timeout = 1000 * rc.timeout; |
| 226 | errdefer os.close(sockfd); | 1035 | const attempts = rc.attempts; |
| 1036 | |||
| 1037 | var sl: os.socklen_t = @sizeOf(os.sockaddr_in); | ||
| 1038 | var family: os.sa_family_t = os.AF_INET; | ||
| 1039 | |||
| 1040 | var ns_list = std.ArrayList(IpAddress).init(rc.ns.allocator); | ||
| 1041 | defer ns_list.deinit(); | ||
| 1042 | |||
| 1043 | try ns_list.resize(rc.ns.len); | ||
| 1044 | const ns = ns_list.toSlice(); | ||
| 1045 | |||
| 1046 | for (rc.ns.toSliceConst()) |iplit, i| { | ||
| 1047 | ns[i] = iplit.addr; | ||
| 1048 | assert(ns[i].getPort() == 53); | ||
| 1049 | if (iplit.addr.any.family != os.AF_INET) { | ||
| 1050 | sl = @sizeOf(os.sockaddr_in6); | ||
| 1051 | family = os.AF_INET6; | ||
| 1052 | } | ||
| 1053 | } | ||
| 227 | 1054 | ||
| 228 | var sock_addr = os.sockaddr{ | 1055 | // Get local address and open/bind a socket |
| 229 | .un = os.sockaddr_un{ | 1056 | var sa: IpAddress = undefined; |
| 230 | .family = os.AF_UNIX, | 1057 | @memset(@ptrCast([*]u8, &sa), 0, @sizeOf(IpAddress)); |
| 231 | .path = undefined, | 1058 | sa.any.family = family; |
| 1059 | const flags = os.SOCK_DGRAM | os.SOCK_CLOEXEC | os.SOCK_NONBLOCK; | ||
| 1060 | const fd = os.socket(family, flags, 0) catch |err| switch (err) { | ||
| 1061 | error.AddressFamilyNotSupported => blk: { | ||
| 1062 | // Handle case where system lacks IPv6 support | ||
| 1063 | if (family == os.AF_INET6) { | ||
| 1064 | family = os.AF_INET; | ||
| 1065 | break :blk try os.socket(os.AF_INET, flags, 0); | ||
| 1066 | } | ||
| 1067 | return err; | ||
| 232 | }, | 1068 | }, |
| 1069 | else => |e| return e, | ||
| 233 | }; | 1070 | }; |
| 1071 | defer os.close(fd); | ||
| 1072 | try os.bind(fd, &sa.any, sl); | ||
| 234 | 1073 | ||
| 235 | if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong; | 1074 | // Past this point, there are no errors. Each individual query will |
| 236 | mem.copy(u8, sock_addr.un.path[0..], path); | 1075 | // yield either no reply (indicated by zero length) or an answer |
| 237 | const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len); | 1076 | // packet which is up to the caller to interpret. |
| 238 | if (std.event.Loop.instance) |loop| { | 1077 | |
| 239 | try os.connect_async(sockfd, &sock_addr, size); | 1078 | // Convert any IPv4 addresses in a mixed environment to v4-mapped |
| 240 | try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET); | 1079 | // TODO |
| 241 | try os.getsockoptError(sockfd); | 1080 | //if (family == AF_INET6) { |
| 242 | } else { | 1081 | // setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0); |
| 243 | try os.connect(sockfd, &sock_addr, size); | 1082 | // for (i=0; i<nns; i++) { |
| 1083 | // if (ns[i].sin.sin_family != AF_INET) continue; | ||
| 1084 | // memcpy(ns[i].sin6.sin6_addr.s6_addr+12, | ||
| 1085 | // &ns[i].sin.sin_addr, 4); | ||
| 1086 | // memcpy(ns[i].sin6.sin6_addr.s6_addr, | ||
| 1087 | // "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12); | ||
| 1088 | // ns[i].sin6.sin6_family = AF_INET6; | ||
| 1089 | // ns[i].sin6.sin6_flowinfo = 0; | ||
| 1090 | // ns[i].sin6.sin6_scope_id = 0; | ||
| 1091 | // } | ||
| 1092 | //} | ||
| 1093 | |||
| 1094 | var pfd = [1]os.pollfd{os.pollfd{ | ||
| 1095 | .fd = fd, | ||
| 1096 | .events = os.POLLIN, | ||
| 1097 | .revents = undefined, | ||
| 1098 | }}; | ||
| 1099 | const retry_interval = timeout / attempts; | ||
| 1100 | var next: u32 = 0; | ||
| 1101 | var t2: u64 = std.time.milliTimestamp(); | ||
| 1102 | var t0 = t2; | ||
| 1103 | var t1 = t2 - retry_interval; | ||
| 1104 | |||
| 1105 | var servfail_retry: usize = undefined; | ||
| 1106 | |||
| 1107 | outer: while (t2 - t0 < timeout) : (t2 = std.time.milliTimestamp()) { | ||
| 1108 | if (t2 - t1 >= retry_interval) { | ||
| 1109 | // Query all configured nameservers in parallel | ||
| 1110 | var i: usize = 0; | ||
| 1111 | while (i < queries.len) : (i += 1) { | ||
| 1112 | if (answers[i].len == 0) { | ||
| 1113 | var j: usize = 0; | ||
| 1114 | while (j < ns.len) : (j += 1) { | ||
| 1115 | _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; | ||
| 1116 | } | ||
| 1117 | } | ||
| 1118 | } | ||
| 1119 | t1 = t2; | ||
| 1120 | servfail_retry = 2 * queries.len; | ||
| 1121 | } | ||
| 1122 | |||
| 1123 | // Wait for a response, or until time to retry | ||
| 1124 | const clamped_timeout = std.math.min(u31(std.math.maxInt(u31)), t1 + retry_interval - t2); | ||
| 1125 | const nevents = os.poll(&pfd, clamped_timeout) catch 0; | ||
| 1126 | if (nevents == 0) continue; | ||
| 1127 | |||
| 1128 | while (true) { | ||
| 1129 | var sl_copy = sl; | ||
| 1130 | const rlen = os.recvfrom(fd, answer_bufs[next], 0, &sa.any, &sl_copy) catch break; | ||
| 1131 | |||
| 1132 | // Ignore non-identifiable packets | ||
| 1133 | if (rlen < 4) continue; | ||
| 1134 | |||
| 1135 | // Ignore replies from addresses we didn't send to | ||
| 1136 | var j: usize = 0; | ||
| 1137 | while (j < ns.len and !ns[j].eql(sa)) : (j += 1) {} | ||
| 1138 | if (j == ns.len) continue; | ||
| 1139 | |||
| 1140 | // Find which query this answer goes with, if any | ||
| 1141 | var i: usize = next; | ||
| 1142 | while (i < queries.len and (answer_bufs[next][0] != queries[i][0] or | ||
| 1143 | answer_bufs[next][1] != queries[i][1])) : (i += 1) | ||
| 1144 | {} | ||
| 1145 | |||
| 1146 | if (i == queries.len) continue; | ||
| 1147 | if (answers[i].len != 0) continue; | ||
| 1148 | |||
| 1149 | // Only accept positive or negative responses; | ||
| 1150 | // retry immediately on server failure, and ignore | ||
| 1151 | // all other codes such as refusal. | ||
| 1152 | switch (answer_bufs[next][3] & 15) { | ||
| 1153 | 0, 3 => {}, | ||
| 1154 | 2 => if (servfail_retry != 0) { | ||
| 1155 | servfail_retry -= 1; | ||
| 1156 | _ = os.sendto(fd, queries[i], os.MSG_NOSIGNAL, &ns[j].any, sl) catch undefined; | ||
| 1157 | }, | ||
| 1158 | else => continue, | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | // Store answer in the right slot, or update next | ||
| 1162 | // available temp slot if it's already in place. | ||
| 1163 | answers[i].len = rlen; | ||
| 1164 | if (i == next) { | ||
| 1165 | while (next < queries.len and answers[next].len != 0) : (next += 1) {} | ||
| 1166 | } else { | ||
| 1167 | mem.copy(u8, answer_bufs[i], answer_bufs[next][0..rlen]); | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | if (next == queries.len) break :outer; | ||
| 1171 | } | ||
| 244 | } | 1172 | } |
| 1173 | } | ||
| 245 | 1174 | ||
| 246 | return std.fs.File.openHandle(sockfd); | 1175 | fn dnsParse( |
| 1176 | r: []const u8, | ||
| 1177 | ctx: var, | ||
| 1178 | comptime callback: var, | ||
| 1179 | ) !void { | ||
| 1180 | // This implementation is ported from musl libc. | ||
| 1181 | // A more idiomatic "ziggy" implementation would be welcome. | ||
| 1182 | if (r.len < 12) return error.InvalidDnsPacket; | ||
| 1183 | if ((r[3] & 15) != 0) return; | ||
| 1184 | var p = r.ptr + 12; | ||
| 1185 | var qdcount = r[4] * usize(256) + r[5]; | ||
| 1186 | var ancount = r[6] * usize(256) + r[7]; | ||
| 1187 | if (qdcount + ancount > 64) return error.InvalidDnsPacket; | ||
| 1188 | while (qdcount != 0) { | ||
| 1189 | qdcount -= 1; | ||
| 1190 | while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1; | ||
| 1191 | if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6) | ||
| 1192 | return error.InvalidDnsPacket; | ||
| 1193 | p += usize(5) + @boolToInt(p[0] != 0); | ||
| 1194 | } | ||
| 1195 | while (ancount != 0) { | ||
| 1196 | ancount -= 1; | ||
| 1197 | while (@ptrToInt(p) - @ptrToInt(r.ptr) < r.len and p[0] -% 1 < 127) p += 1; | ||
| 1198 | if (p[0] > 193 or (p[0] == 193 and p[1] > 254) or @ptrToInt(p) > @ptrToInt(r.ptr) + r.len - 6) | ||
| 1199 | return error.InvalidDnsPacket; | ||
| 1200 | p += usize(1) + @boolToInt(p[0] != 0); | ||
| 1201 | const len = p[8] * usize(256) + p[9]; | ||
| 1202 | if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket; | ||
| 1203 | try callback(ctx, p[1], p[10 .. 10 + len], r); | ||
| 1204 | p += 10 + len; | ||
| 1205 | } | ||
| 247 | } | 1206 | } |
| 1207 | |||
| 1208 | fn dnsParseCallback(ctx: dpc_ctx, rr: u8, data: []const u8, packet: []const u8) !void { | ||
| 1209 | switch (rr) { | ||
| 1210 | os.RR_A => { | ||
| 1211 | if (data.len != 4) return error.InvalidDnsARecord; | ||
| 1212 | const new_addr = try ctx.addrs.addOne(); | ||
| 1213 | new_addr.* = LookupAddr{ | ||
| 1214 | // TODO slice [0..4] to make this *[4]u8 without @ptrCast | ||
| 1215 | .addr = IpAddress.initIp4(@ptrCast(*const [4]u8, data.ptr).*, ctx.port), | ||
| 1216 | }; | ||
| 1217 | }, | ||
| 1218 | os.RR_AAAA => { | ||
| 1219 | if (data.len != 16) return error.InvalidDnsAAAARecord; | ||
| 1220 | const new_addr = try ctx.addrs.addOne(); | ||
| 1221 | new_addr.* = LookupAddr{ | ||
| 1222 | // TODO slice [0..16] to make this *[16]u8 without @ptrCast | ||
| 1223 | .addr = IpAddress.initIp6(@ptrCast(*const [16]u8, data.ptr).*, ctx.port, 0, 0), | ||
| 1224 | }; | ||
| 1225 | }, | ||
| 1226 | os.RR_CNAME => { | ||
| 1227 | var tmp: [256]u8 = undefined; | ||
| 1228 | // Returns len of compressed name. strlen to get canon name. | ||
| 1229 | _ = try os.dn_expand(packet, data, &tmp); | ||
| 1230 | const canon_name = mem.toSliceConst(u8, &tmp); | ||
| 1231 | if (isValidHostName(canon_name)) { | ||
| 1232 | try ctx.canon.replaceContents(canon_name); | ||
| 1233 | } | ||
| 1234 | }, | ||
| 1235 | else => return, | ||
| 1236 | } | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | pub const TcpServer = struct { | ||
| 1240 | /// Copied from `Options` on `init`. | ||
| 1241 | kernel_backlog: u32, | ||
| 1242 | |||
| 1243 | /// `undefined` until `listen` returns successfully. | ||
| 1244 | listen_address: IpAddress, | ||
| 1245 | |||
| 1246 | sockfd: ?os.fd_t, | ||
| 1247 | |||
| 1248 | pub const Options = struct { | ||
| 1249 | /// How many connections the kernel will accept on the application's behalf. | ||
| 1250 | /// If more than this many connections pool in the kernel, clients will start | ||
| 1251 | /// seeing "Connection refused". | ||
| 1252 | kernel_backlog: u32 = 128, | ||
| 1253 | }; | ||
| 1254 | |||
| 1255 | /// After this call succeeds, resources have been acquired and must | ||
| 1256 | /// be released with `deinit`. | ||
| 1257 | pub fn init(options: Options) TcpServer { | ||
| 1258 | return TcpServer{ | ||
| 1259 | .sockfd = null, | ||
| 1260 | .kernel_backlog = options.kernel_backlog, | ||
| 1261 | .listen_address = undefined, | ||
| 1262 | }; | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | /// Release all resources. The `TcpServer` memory becomes `undefined`. | ||
| 1266 | pub fn deinit(self: *TcpServer) void { | ||
| 1267 | self.close(); | ||
| 1268 | self.* = undefined; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | pub fn listen(self: *TcpServer, address: IpAddress) !void { | ||
| 1272 | const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; | ||
| 1273 | const sock_flags = os.SOCK_STREAM | os.SOCK_CLOEXEC | nonblock; | ||
| 1274 | const sockfd = try os.socket(os.AF_INET, sock_flags, os.PROTO_tcp); | ||
| 1275 | self.sockfd = sockfd; | ||
| 1276 | errdefer { | ||
| 1277 | os.close(sockfd); | ||
| 1278 | self.sockfd = null; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | var socklen = address.getOsSockLen(); | ||
| 1282 | try os.bind(sockfd, &address.any, socklen); | ||
| 1283 | try os.listen(sockfd, self.kernel_backlog); | ||
| 1284 | try os.getsockname(sockfd, &self.listen_address.any, &socklen); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | /// Stop listening. It is still necessary to call `deinit` after stopping listening. | ||
| 1288 | /// Calling `deinit` will automatically call `close`. It is safe to call `close` when | ||
| 1289 | /// not listening. | ||
| 1290 | pub fn close(self: *TcpServer) void { | ||
| 1291 | if (self.sockfd) |fd| { | ||
| 1292 | os.close(fd); | ||
| 1293 | self.sockfd = null; | ||
| 1294 | self.listen_address = undefined; | ||
| 1295 | } | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | pub const AcceptError = error{ | ||
| 1299 | ConnectionAborted, | ||
| 1300 | |||
| 1301 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 1302 | ProcessFdQuotaExceeded, | ||
| 1303 | |||
| 1304 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1305 | SystemFdQuotaExceeded, | ||
| 1306 | |||
| 1307 | /// Not enough free memory. This often means that the memory allocation is limited | ||
| 1308 | /// by the socket buffer limits, not by the system memory. | ||
| 1309 | SystemResources, | ||
| 1310 | |||
| 1311 | ProtocolFailure, | ||
| 1312 | |||
| 1313 | /// Firewall rules forbid connection. | ||
| 1314 | BlockedByFirewall, | ||
| 1315 | } || os.UnexpectedError; | ||
| 1316 | |||
| 1317 | /// If this function succeeds, the returned `fs.File` is a caller-managed resource. | ||
| 1318 | pub fn accept(self: *TcpServer) AcceptError!fs.File { | ||
| 1319 | const nonblock = if (std.io.is_async) os.SOCK_NONBLOCK else 0; | ||
| 1320 | const accept_flags = nonblock | os.SOCK_CLOEXEC; | ||
| 1321 | var accepted_addr: IpAddress = undefined; | ||
| 1322 | var adr_len: os.socklen_t = @sizeOf(IpAddress); | ||
| 1323 | if (os.accept4(self.sockfd.?, &accepted_addr.any, &adr_len, accept_flags)) |fd| { | ||
| 1324 | return fs.File.openHandle(fd); | ||
| 1325 | } else |err| switch (err) { | ||
| 1326 | // We only give SOCK_NONBLOCK when I/O mode is async, in which case this error | ||
| 1327 | // is handled by os.accept4. | ||
| 1328 | error.WouldBlock => unreachable, | ||
| 1329 | else => |e| return e, | ||
| 1330 | } | ||
| 1331 | } | ||
| 1332 | }; |
lib/std/net/test.zig created+91| ... | @@ -0,0 +1,91 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const net = std.net; | ||
| 3 | const mem = std.mem; | ||
| 4 | const testing = std.testing; | ||
| 5 | |||
| 6 | test "parse and render IPv6 addresses" { | ||
| 7 | const addr = try net.IpAddress.parseIp6("FF01:0:0:0:0:0:0:FB", 80); | ||
| 8 | var buf: [100]u8 = undefined; | ||
| 9 | const printed = try std.fmt.bufPrint(&buf, "{}", addr); | ||
| 10 | std.testing.expect(mem.eql(u8, "[ff01::fb]:80", printed)); | ||
| 11 | } | ||
| 12 | |||
| 13 | test "parse and render IPv4 addresses" { | ||
| 14 | var buffer: [18]u8 = undefined; | ||
| 15 | for ([_][]const u8{ | ||
| 16 | "0.0.0.0", | ||
| 17 | "255.255.255.255", | ||
| 18 | "1.2.3.4", | ||
| 19 | "123.255.0.91", | ||
| 20 | "127.0.0.1", | ||
| 21 | }) |ip| { | ||
| 22 | var addr = net.IpAddress.parseIp4(ip, 0); | ||
| 23 | var newIp = std.fmt.bufPrint(buffer[0..], "{}", addr) catch unreachable; | ||
| 24 | std.testing.expect(std.mem.eql(u8, ip, newIp[0 .. newIp.len - 2])); | ||
| 25 | } | ||
| 26 | |||
| 27 | testing.expectError(error.Overflow, net.IpAddress.parseIp4("256.0.0.1", 0)); | ||
| 28 | testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("x.0.0.1", 0)); | ||
| 29 | testing.expectError(error.InvalidEnd, net.IpAddress.parseIp4("127.0.0.1.1", 0)); | ||
| 30 | testing.expectError(error.Incomplete, net.IpAddress.parseIp4("127.0.0.", 0)); | ||
| 31 | testing.expectError(error.InvalidCharacter, net.IpAddress.parseIp4("100..0.1", 0)); | ||
| 32 | } | ||
| 33 | |||
| 34 | test "resolve DNS" { | ||
| 35 | if (std.builtin.os == .windows) { | ||
| 36 | // DNS resolution not implemented on Windows yet. | ||
| 37 | return error.SkipZigTest; | ||
| 38 | } | ||
| 39 | var buf: [1000 * 10]u8 = undefined; | ||
| 40 | const a = &std.heap.FixedBufferAllocator.init(&buf).allocator; | ||
| 41 | |||
| 42 | const address_list = net.getAddressList(a, "example.com", 80) catch |err| switch (err) { | ||
| 43 | // The tests are required to work even when there is no Internet connection, | ||
| 44 | // so some of these errors we must accept and skip the test. | ||
| 45 | error.UnknownHostName => return error.SkipZigTest, | ||
| 46 | error.TemporaryNameServerFailure => return error.SkipZigTest, | ||
| 47 | else => return err, | ||
| 48 | }; | ||
| 49 | address_list.deinit(); | ||
| 50 | } | ||
| 51 | |||
| 52 | test "listen on a port, send bytes, receive bytes" { | ||
| 53 | if (std.builtin.os != .linux) { | ||
| 54 | // TODO build abstractions for other operating systems | ||
| 55 | return error.SkipZigTest; | ||
| 56 | } | ||
| 57 | if (std.io.mode != .evented) { | ||
| 58 | // TODO add ability to run tests in non-blocking I/O mode | ||
| 59 | return error.SkipZigTest; | ||
| 60 | } | ||
| 61 | |||
| 62 | // TODO doing this at comptime crashed the compiler | ||
| 63 | const localhost = net.IpAddress.parse("127.0.0.1", 0); | ||
| 64 | |||
| 65 | var server = net.TcpServer.init(net.TcpServer.Options{}); | ||
| 66 | defer server.deinit(); | ||
| 67 | try server.listen(localhost); | ||
| 68 | |||
| 69 | var server_frame = async testServer(&server); | ||
| 70 | var client_frame = async testClient(server.listen_address); | ||
| 71 | |||
| 72 | try await server_frame; | ||
| 73 | try await client_frame; | ||
| 74 | } | ||
| 75 | |||
| 76 | fn testClient(addr: net.IpAddress) anyerror!void { | ||
| 77 | const socket_file = try net.tcpConnectToAddress(addr); | ||
| 78 | defer socket_file.close(); | ||
| 79 | |||
| 80 | var buf: [100]u8 = undefined; | ||
| 81 | const len = try socket_file.read(&buf); | ||
| 82 | const msg = buf[0..len]; | ||
| 83 | testing.expect(mem.eql(u8, msg, "hello from server\n")); | ||
| 84 | } | ||
| 85 | |||
| 86 | fn testServer(server: *net.TcpServer) anyerror!void { | ||
| 87 | var client_file = try server.accept(); | ||
| 88 | |||
| 89 | const stream = &client_file.outStream().stream; | ||
| 90 | try stream.print("hello from server\n"); | ||
| 91 | } | ||
lib/std/os.zig+402-72| ... | @@ -310,7 +310,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -310,7 +310,7 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 310 | EINVAL => unreachable, | 310 | EINVAL => unreachable, |
| 311 | EFAULT => unreachable, | 311 | EFAULT => unreachable, |
| 312 | EAGAIN => if (std.event.Loop.instance) |loop| { | 312 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 313 | loop.waitUntilFdReadable(fd) catch return error.WouldBlock; | 313 | loop.waitUntilFdReadable(fd); |
| 314 | continue; | 314 | continue; |
| 315 | } else { | 315 | } else { |
| 316 | return error.WouldBlock; | 316 | return error.WouldBlock; |
| ... | @@ -327,7 +327,36 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -327,7 +327,36 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | 329 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. |
| 330 | /// This function is for blocking file descriptors only. | 330 | /// If the application has a global event loop enabled, EAGAIN is handled |
| 331 | /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. | ||
| 332 | pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { | ||
| 333 | while (true) { | ||
| 334 | // TODO handle the case when iov_len is too large and get rid of this @intCast | ||
| 335 | const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len)); | ||
| 336 | switch (errno(rc)) { | ||
| 337 | 0 => return @bitCast(usize, rc), | ||
| 338 | EINTR => continue, | ||
| 339 | EINVAL => unreachable, | ||
| 340 | EFAULT => unreachable, | ||
| 341 | EAGAIN => if (std.event.Loop.instance) |loop| { | ||
| 342 | loop.waitUntilFdReadable(fd); | ||
| 343 | continue; | ||
| 344 | } else { | ||
| 345 | return error.WouldBlock; | ||
| 346 | }, | ||
| 347 | EBADF => unreachable, // always a race condition | ||
| 348 | EIO => return error.InputOutput, | ||
| 349 | EISDIR => return error.IsDir, | ||
| 350 | ENOBUFS => return error.SystemResources, | ||
| 351 | ENOMEM => return error.SystemResources, | ||
| 352 | else => |err| return unexpectedErrno(err), | ||
| 353 | } | ||
| 354 | } | ||
| 355 | } | ||
| 356 | |||
| 357 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | ||
| 358 | /// If the application has a global event loop enabled, EAGAIN is handled | ||
| 359 | /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. | ||
| 331 | pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { | 360 | pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { |
| 332 | if (comptime std.Target.current.isDarwin()) { | 361 | if (comptime std.Target.current.isDarwin()) { |
| 333 | // Darwin does not have preadv but it does have pread. | 362 | // Darwin does not have preadv but it does have pread. |
| ... | @@ -357,7 +386,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { | ... | @@ -357,7 +386,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { |
| 357 | EINVAL => unreachable, | 386 | EINVAL => unreachable, |
| 358 | EFAULT => unreachable, | 387 | EFAULT => unreachable, |
| 359 | ESPIPE => unreachable, // fd is not seekable | 388 | ESPIPE => unreachable, // fd is not seekable |
| 360 | EAGAIN => unreachable, // This function is for blocking reads. | 389 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 390 | loop.waitUntilFdReadable(fd); | ||
| 391 | continue; | ||
| 392 | } else { | ||
| 393 | return error.WouldBlock; | ||
| 394 | }, | ||
| 361 | EBADF => unreachable, // always a race condition | 395 | EBADF => unreachable, // always a race condition |
| 362 | EIO => return error.InputOutput, | 396 | EIO => return error.InputOutput, |
| 363 | EISDIR => return error.IsDir, | 397 | EISDIR => return error.IsDir, |
| ... | @@ -375,7 +409,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { | ... | @@ -375,7 +409,12 @@ pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize { |
| 375 | EINTR => continue, | 409 | EINTR => continue, |
| 376 | EINVAL => unreachable, | 410 | EINVAL => unreachable, |
| 377 | EFAULT => unreachable, | 411 | EFAULT => unreachable, |
| 378 | EAGAIN => unreachable, // This function is for blocking reads. | 412 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 413 | loop.waitUntilFdReadable(fd); | ||
| 414 | continue; | ||
| 415 | } else { | ||
| 416 | return error.WouldBlock; | ||
| 417 | }, | ||
| 379 | EBADF => unreachable, // always a race condition | 418 | EBADF => unreachable, // always a race condition |
| 380 | EIO => return error.InputOutput, | 419 | EIO => return error.InputOutput, |
| 381 | EISDIR => return error.IsDir, | 420 | EISDIR => return error.IsDir, |
| ... | @@ -395,10 +434,17 @@ pub const WriteError = error{ | ... | @@ -395,10 +434,17 @@ pub const WriteError = error{ |
| 395 | BrokenPipe, | 434 | BrokenPipe, |
| 396 | SystemResources, | 435 | SystemResources, |
| 397 | OperationAborted, | 436 | OperationAborted, |
| 437 | |||
| 438 | /// This error occurs when no global event loop is configured, | ||
| 439 | /// and reading from the file descriptor would block. | ||
| 440 | WouldBlock, | ||
| 398 | } || UnexpectedError; | 441 | } || UnexpectedError; |
| 399 | 442 | ||
| 400 | /// Write to a file descriptor. Keeps trying if it gets interrupted. | 443 | /// Write to a file descriptor. Keeps trying if it gets interrupted. |
| 401 | /// This function is for blocking file descriptors only. | 444 | /// If the application has a global event loop enabled, EAGAIN is handled |
| 445 | /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. | ||
| 446 | /// TODO evented I/O integration is disabled until | ||
| 447 | /// https://github.com/ziglang/zig/issues/3557 is solved. | ||
| 402 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { | 448 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { |
| 403 | if (builtin.os == .windows) { | 449 | if (builtin.os == .windows) { |
| 404 | return windows.WriteFile(fd, bytes); | 450 | return windows.WriteFile(fd, bytes); |
| ... | @@ -434,7 +480,14 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { | ... | @@ -434,7 +480,14 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { |
| 434 | EINTR => continue, | 480 | EINTR => continue, |
| 435 | EINVAL => unreachable, | 481 | EINVAL => unreachable, |
| 436 | EFAULT => unreachable, | 482 | EFAULT => unreachable, |
| 437 | EAGAIN => unreachable, // This function is for blocking writes. | 483 | // TODO https://github.com/ziglang/zig/issues/3557 |
| 484 | EAGAIN => return error.WouldBlock, | ||
| 485 | //EAGAIN => if (std.event.Loop.instance) |loop| { | ||
| 486 | // loop.waitUntilFdWritable(fd); | ||
| 487 | // continue; | ||
| 488 | //} else { | ||
| 489 | // return error.WouldBlock; | ||
| 490 | //}, | ||
| 438 | EBADF => unreachable, // Always a race condition. | 491 | EBADF => unreachable, // Always a race condition. |
| 439 | EDESTADDRREQ => unreachable, // `connect` was never called. | 492 | EDESTADDRREQ => unreachable, // `connect` was never called. |
| 440 | EDQUOT => return error.DiskQuota, | 493 | EDQUOT => return error.DiskQuota, |
| ... | @@ -448,9 +501,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { | ... | @@ -448,9 +501,9 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { |
| 448 | } | 501 | } |
| 449 | } | 502 | } |
| 450 | 503 | ||
| 451 | /// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted. | 504 | /// Write multiple buffers to a file descriptor. |
| 452 | /// This function is for blocking file descriptors only. For non-blocking, see | 505 | /// If the application has a global event loop enabled, EAGAIN is handled |
| 453 | /// `writevAsync`. | 506 | /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. |
| 454 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { | 507 | pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { |
| 455 | while (true) { | 508 | while (true) { |
| 456 | // TODO handle the case when iov_len is too large and get rid of this @intCast | 509 | // TODO handle the case when iov_len is too large and get rid of this @intCast |
| ... | @@ -460,7 +513,12 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { | ... | @@ -460,7 +513,12 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { |
| 460 | EINTR => continue, | 513 | EINTR => continue, |
| 461 | EINVAL => unreachable, | 514 | EINVAL => unreachable, |
| 462 | EFAULT => unreachable, | 515 | EFAULT => unreachable, |
| 463 | EAGAIN => unreachable, // This function is for blocking writes. | 516 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 517 | loop.waitUntilFdWritable(fd); | ||
| 518 | continue; | ||
| 519 | } else { | ||
| 520 | return error.WouldBlock; | ||
| 521 | }, | ||
| 464 | EBADF => unreachable, // Always a race condition. | 522 | EBADF => unreachable, // Always a race condition. |
| 465 | EDESTADDRREQ => unreachable, // `connect` was never called. | 523 | EDESTADDRREQ => unreachable, // `connect` was never called. |
| 466 | EDQUOT => return error.DiskQuota, | 524 | EDQUOT => return error.DiskQuota, |
| ... | @@ -476,8 +534,6 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { | ... | @@ -476,8 +534,6 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void { |
| 476 | 534 | ||
| 477 | /// Write multiple buffers to a file descriptor, with a position offset. | 535 | /// Write multiple buffers to a file descriptor, with a position offset. |
| 478 | /// Keeps trying if it gets interrupted. | 536 | /// Keeps trying if it gets interrupted. |
| 479 | /// This function is for blocking file descriptors only. For non-blocking, see | ||
| 480 | /// `pwritevAsync`. | ||
| 481 | pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { | 537 | pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { |
| 482 | if (comptime std.Target.current.isDarwin()) { | 538 | if (comptime std.Target.current.isDarwin()) { |
| 483 | // Darwin does not have pwritev but it does have pwrite. | 539 | // Darwin does not have pwritev but it does have pwrite. |
| ... | @@ -506,7 +562,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void | ... | @@ -506,7 +562,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void |
| 506 | ESPIPE => unreachable, // `fd` is not seekable. | 562 | ESPIPE => unreachable, // `fd` is not seekable. |
| 507 | EINVAL => unreachable, | 563 | EINVAL => unreachable, |
| 508 | EFAULT => unreachable, | 564 | EFAULT => unreachable, |
| 509 | EAGAIN => unreachable, // This function is for blocking writes. | 565 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 566 | loop.waitUntilFdWritable(fd); | ||
| 567 | continue; | ||
| 568 | } else { | ||
| 569 | return error.WouldBlock; | ||
| 570 | }, | ||
| 510 | EBADF => unreachable, // Always a race condition. | 571 | EBADF => unreachable, // Always a race condition. |
| 511 | EDESTADDRREQ => unreachable, // `connect` was never called. | 572 | EDESTADDRREQ => unreachable, // `connect` was never called. |
| 512 | EDQUOT => return error.DiskQuota, | 573 | EDQUOT => return error.DiskQuota, |
| ... | @@ -528,7 +589,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void | ... | @@ -528,7 +589,12 @@ pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void |
| 528 | EINTR => continue, | 589 | EINTR => continue, |
| 529 | EINVAL => unreachable, | 590 | EINVAL => unreachable, |
| 530 | EFAULT => unreachable, | 591 | EFAULT => unreachable, |
| 531 | EAGAIN => unreachable, // This function is for blocking writes. | 592 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 593 | loop.waitUntilFdWritable(fd); | ||
| 594 | continue; | ||
| 595 | } else { | ||
| 596 | return error.WouldBlock; | ||
| 597 | }, | ||
| 532 | EBADF => unreachable, // Always a race condition. | 598 | EBADF => unreachable, // Always a race condition. |
| 533 | EDESTADDRREQ => unreachable, // `connect` was never called. | 599 | EDESTADDRREQ => unreachable, // `connect` was never called. |
| 534 | EDQUOT => return error.DiskQuota, | 600 | EDQUOT => return error.DiskQuota, |
| ... | @@ -1510,16 +1576,17 @@ pub const SocketError = error{ | ... | @@ -1510,16 +1576,17 @@ pub const SocketError = error{ |
| 1510 | ProtocolNotSupported, | 1576 | ProtocolNotSupported, |
| 1511 | } || UnexpectedError; | 1577 | } || UnexpectedError; |
| 1512 | 1578 | ||
| 1513 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 { | 1579 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t { |
| 1514 | const rc = system.socket(domain, socket_type, protocol); | 1580 | const rc = system.socket(domain, socket_type, protocol); |
| 1515 | switch (errno(rc)) { | 1581 | switch (errno(rc)) { |
| 1516 | 0 => return @intCast(i32, rc), | 1582 | 0 => return @intCast(fd_t, rc), |
| 1517 | EACCES => return error.PermissionDenied, | 1583 | EACCES => return error.PermissionDenied, |
| 1518 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | 1584 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 1519 | EINVAL => return error.ProtocolFamilyNotAvailable, | 1585 | EINVAL => return error.ProtocolFamilyNotAvailable, |
| 1520 | EMFILE => return error.ProcessFdQuotaExceeded, | 1586 | EMFILE => return error.ProcessFdQuotaExceeded, |
| 1521 | ENFILE => return error.SystemFdQuotaExceeded, | 1587 | ENFILE => return error.SystemFdQuotaExceeded, |
| 1522 | ENOBUFS, ENOMEM => return error.SystemResources, | 1588 | ENOBUFS => return error.SystemResources, |
| 1589 | ENOMEM => return error.SystemResources, | ||
| 1523 | EPROTONOSUPPORT => return error.ProtocolNotSupported, | 1590 | EPROTONOSUPPORT => return error.ProtocolNotSupported, |
| 1524 | else => |err| return unexpectedErrno(err), | 1591 | else => |err| return unexpectedErrno(err), |
| 1525 | } | 1592 | } |
| ... | @@ -1561,17 +1628,17 @@ pub const BindError = error{ | ... | @@ -1561,17 +1628,17 @@ pub const BindError = error{ |
| 1561 | } || UnexpectedError; | 1628 | } || UnexpectedError; |
| 1562 | 1629 | ||
| 1563 | /// addr is `*const T` where T is one of the sockaddr | 1630 | /// addr is `*const T` where T is one of the sockaddr |
| 1564 | pub fn bind(fd: i32, addr: *const sockaddr) BindError!void { | 1631 | pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void { |
| 1565 | const rc = system.bind(fd, addr, @sizeOf(sockaddr)); | 1632 | const rc = system.bind(sockfd, addr, len); |
| 1566 | switch (errno(rc)) { | 1633 | switch (errno(rc)) { |
| 1567 | 0 => return, | 1634 | 0 => return, |
| 1568 | EACCES => return error.AccessDenied, | 1635 | EACCES => return error.AccessDenied, |
| 1569 | EADDRINUSE => return error.AddressInUse, | 1636 | EADDRINUSE => return error.AddressInUse, |
| 1570 | EBADF => unreachable, // always a race condition if this error is returned | 1637 | EBADF => unreachable, // always a race condition if this error is returned |
| 1571 | EINVAL => unreachable, | 1638 | EINVAL => unreachable, // invalid parameters |
| 1572 | ENOTSOCK => unreachable, | 1639 | ENOTSOCK => unreachable, // invalid `sockfd` |
| 1573 | EADDRNOTAVAIL => return error.AddressNotAvailable, | 1640 | EADDRNOTAVAIL => return error.AddressNotAvailable, |
| 1574 | EFAULT => unreachable, | 1641 | EFAULT => unreachable, // invalid `addr` pointer |
| 1575 | ELOOP => return error.SymLinkLoop, | 1642 | ELOOP => return error.SymLinkLoop, |
| 1576 | ENAMETOOLONG => return error.NameTooLong, | 1643 | ENAMETOOLONG => return error.NameTooLong, |
| 1577 | ENOENT => return error.FileNotFound, | 1644 | ENOENT => return error.FileNotFound, |
| ... | @@ -1622,12 +1689,6 @@ pub const AcceptError = error{ | ... | @@ -1622,12 +1689,6 @@ pub const AcceptError = error{ |
| 1622 | /// by the socket buffer limits, not by the system memory. | 1689 | /// by the socket buffer limits, not by the system memory. |
| 1623 | SystemResources, | 1690 | SystemResources, |
| 1624 | 1691 | ||
| 1625 | /// The file descriptor sockfd does not refer to a socket. | ||
| 1626 | FileDescriptorNotASocket, | ||
| 1627 | |||
| 1628 | /// The referenced socket is not of type SOCK_STREAM. | ||
| 1629 | OperationNotSupported, | ||
| 1630 | |||
| 1631 | ProtocolFailure, | 1692 | ProtocolFailure, |
| 1632 | 1693 | ||
| 1633 | /// Firewall rules forbid connection. | 1694 | /// Firewall rules forbid connection. |
| ... | @@ -1644,7 +1705,7 @@ pub const AcceptError = error{ | ... | @@ -1644,7 +1705,7 @@ pub const AcceptError = error{ |
| 1644 | pub fn accept4( | 1705 | pub fn accept4( |
| 1645 | /// This argument is a socket that has been created with `socket`, bound to a local address | 1706 | /// This argument is a socket that has been created with `socket`, bound to a local address |
| 1646 | /// with `bind`, and is listening for connections after a `listen`. | 1707 | /// with `bind`, and is listening for connections after a `listen`. |
| 1647 | sockfd: i32, | 1708 | sockfd: fd_t, |
| 1648 | /// This argument is a pointer to a sockaddr structure. This structure is filled in with the | 1709 | /// This argument is a pointer to a sockaddr structure. This structure is filled in with the |
| 1649 | /// address of the peer socket, as known to the communications layer. The exact format of the | 1710 | /// address of the peer socket, as known to the communications layer. The exact format of the |
| 1650 | /// address returned addr is determined by the socket's address family (see `socket` and the | 1711 | /// address returned addr is determined by the socket's address family (see `socket` and the |
| ... | @@ -1665,15 +1726,15 @@ pub fn accept4( | ... | @@ -1665,15 +1726,15 @@ pub fn accept4( |
| 1665 | /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the | 1726 | /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the |
| 1666 | /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. | 1727 | /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. |
| 1667 | flags: u32, | 1728 | flags: u32, |
| 1668 | ) AcceptError!i32 { | 1729 | ) AcceptError!fd_t { |
| 1669 | while (true) { | 1730 | while (true) { |
| 1670 | const rc = system.accept4(sockfd, addr, addr_size, flags); | 1731 | const rc = system.accept4(sockfd, addr, addr_size, flags); |
| 1671 | switch (errno(rc)) { | 1732 | switch (errno(rc)) { |
| 1672 | 0 => return @intCast(i32, rc), | 1733 | 0 => return @intCast(fd_t, rc), |
| 1673 | EINTR => continue, | 1734 | EINTR => continue, |
| 1674 | 1735 | ||
| 1675 | EAGAIN => if (std.event.Loop.instance) |loop| { | 1736 | EAGAIN => if (std.event.Loop.instance) |loop| { |
| 1676 | loop.waitUntilFdReadable(sockfd) catch return error.WouldBlock; | 1737 | loop.waitUntilFdReadable(sockfd); |
| 1677 | continue; | 1738 | continue; |
| 1678 | } else { | 1739 | } else { |
| 1679 | return error.WouldBlock; | 1740 | return error.WouldBlock; |
| ... | @@ -1682,12 +1743,12 @@ pub fn accept4( | ... | @@ -1682,12 +1743,12 @@ pub fn accept4( |
| 1682 | ECONNABORTED => return error.ConnectionAborted, | 1743 | ECONNABORTED => return error.ConnectionAborted, |
| 1683 | EFAULT => unreachable, | 1744 | EFAULT => unreachable, |
| 1684 | EINVAL => unreachable, | 1745 | EINVAL => unreachable, |
| 1746 | ENOTSOCK => unreachable, | ||
| 1685 | EMFILE => return error.ProcessFdQuotaExceeded, | 1747 | EMFILE => return error.ProcessFdQuotaExceeded, |
| 1686 | ENFILE => return error.SystemFdQuotaExceeded, | 1748 | ENFILE => return error.SystemFdQuotaExceeded, |
| 1687 | ENOBUFS => return error.SystemResources, | 1749 | ENOBUFS => return error.SystemResources, |
| 1688 | ENOMEM => return error.SystemResources, | 1750 | ENOMEM => return error.SystemResources, |
| 1689 | ENOTSOCK => return error.FileDescriptorNotASocket, | 1751 | EOPNOTSUPP => unreachable, |
| 1690 | EOPNOTSUPP => return error.OperationNotSupported, | ||
| 1691 | EPROTO => return error.ProtocolFailure, | 1752 | EPROTO => return error.ProtocolFailure, |
| 1692 | EPERM => return error.BlockedByFirewall, | 1753 | EPERM => return error.BlockedByFirewall, |
| 1693 | 1754 | ||
| ... | @@ -1809,11 +1870,9 @@ pub const GetSockNameError = error{ | ... | @@ -1809,11 +1870,9 @@ pub const GetSockNameError = error{ |
| 1809 | SystemResources, | 1870 | SystemResources, |
| 1810 | } || UnexpectedError; | 1871 | } || UnexpectedError; |
| 1811 | 1872 | ||
| 1812 | pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr { | 1873 | pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { |
| 1813 | var addr: sockaddr = undefined; | 1874 | switch (errno(system.getsockname(sockfd, addr, addrlen))) { |
| 1814 | var addrlen: socklen_t = @sizeOf(sockaddr); | 1875 | 0 => return, |
| 1815 | switch (errno(system.getsockname(sockfd, &addr, &addrlen))) { | ||
| 1816 | 0 => return addr, | ||
| 1817 | else => |err| return unexpectedErrno(err), | 1876 | else => |err| return unexpectedErrno(err), |
| 1818 | 1877 | ||
| 1819 | EBADF => unreachable, // always a race condition | 1878 | EBADF => unreachable, // always a race condition |
| ... | @@ -1856,12 +1915,14 @@ pub const ConnectError = error{ | ... | @@ -1856,12 +1915,14 @@ pub const ConnectError = error{ |
| 1856 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note | 1915 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note |
| 1857 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. | 1916 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. |
| 1858 | ConnectionTimedOut, | 1917 | ConnectionTimedOut, |
| 1918 | |||
| 1919 | /// This error occurs when no global event loop is configured, | ||
| 1920 | /// and connecting to the socket would block. | ||
| 1921 | WouldBlock, | ||
| 1859 | } || UnexpectedError; | 1922 | } || UnexpectedError; |
| 1860 | 1923 | ||
| 1861 | /// Initiate a connection on a socket. | 1924 | /// Initiate a connection on a socket. |
| 1862 | /// This is for blocking file descriptors only. | 1925 | pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { |
| 1863 | /// For non-blocking, see `connect_async`. | ||
| 1864 | pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { | ||
| 1865 | while (true) { | 1926 | while (true) { |
| 1866 | switch (errno(system.connect(sockfd, sock_addr, len))) { | 1927 | switch (errno(system.connect(sockfd, sock_addr, len))) { |
| 1867 | 0 => return, | 1928 | 0 => return, |
| ... | @@ -1870,41 +1931,16 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v | ... | @@ -1870,41 +1931,16 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v |
| 1870 | EADDRINUSE => return error.AddressInUse, | 1931 | EADDRINUSE => return error.AddressInUse, |
| 1871 | EADDRNOTAVAIL => return error.AddressNotAvailable, | 1932 | EADDRNOTAVAIL => return error.AddressNotAvailable, |
| 1872 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | 1933 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, |
| 1873 | EAGAIN => return error.SystemResources, | 1934 | EAGAIN, EINPROGRESS => { |
| 1935 | const loop = std.event.Loop.instance orelse return error.WouldBlock; | ||
| 1936 | loop.waitUntilFdWritableOrReadable(sockfd); | ||
| 1937 | return getsockoptError(sockfd); | ||
| 1938 | }, | ||
| 1874 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | 1939 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. |
| 1875 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | 1940 | EBADF => unreachable, // sockfd is not a valid open file descriptor. |
| 1876 | ECONNREFUSED => return error.ConnectionRefused, | 1941 | ECONNREFUSED => return error.ConnectionRefused, |
| 1877 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | 1942 | EFAULT => unreachable, // The socket structure address is outside the user's address space. |
| 1878 | EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. | ||
| 1879 | EINTR => continue, | ||
| 1880 | EISCONN => unreachable, // The socket is already connected. | ||
| 1881 | ENETUNREACH => return error.NetworkUnreachable, | ||
| 1882 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1883 | EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 1884 | ETIMEDOUT => return error.ConnectionTimedOut, | ||
| 1885 | else => |err| return unexpectedErrno(err), | ||
| 1886 | } | ||
| 1887 | } | ||
| 1888 | } | ||
| 1889 | |||
| 1890 | /// Same as `connect` except it is for non-blocking socket file descriptors. | ||
| 1891 | /// It expects to receive EINPROGRESS`. | ||
| 1892 | pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { | ||
| 1893 | while (true) { | ||
| 1894 | switch (errno(system.connect(sockfd, sock_addr, len))) { | ||
| 1895 | EINVAL => unreachable, | ||
| 1896 | EINTR => continue, | 1943 | EINTR => continue, |
| 1897 | 0, EINPROGRESS => return, | ||
| 1898 | EACCES => return error.PermissionDenied, | ||
| 1899 | EPERM => return error.PermissionDenied, | ||
| 1900 | EADDRINUSE => return error.AddressInUse, | ||
| 1901 | EADDRNOTAVAIL => return error.AddressNotAvailable, | ||
| 1902 | EAFNOSUPPORT => return error.AddressFamilyNotSupported, | ||
| 1903 | EAGAIN => return error.SystemResources, | ||
| 1904 | EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 1905 | EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 1906 | ECONNREFUSED => return error.ConnectionRefused, | ||
| 1907 | EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 1908 | EISCONN => unreachable, // The socket is already connected. | 1944 | EISCONN => unreachable, // The socket is already connected. |
| 1909 | ENETUNREACH => return error.NetworkUnreachable, | 1945 | ENETUNREACH => return error.NetworkUnreachable, |
| 1910 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | 1946 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. |
| ... | @@ -2835,3 +2871,297 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { | ... | @@ -2835,3 +2871,297 @@ pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { |
| 2835 | 2871 | ||
| 2836 | @compileError("TODO implement gethostname for this OS"); | 2872 | @compileError("TODO implement gethostname for this OS"); |
| 2837 | } | 2873 | } |
| 2874 | |||
| 2875 | pub fn res_mkquery( | ||
| 2876 | op: u4, | ||
| 2877 | dname: []const u8, | ||
| 2878 | class: u8, | ||
| 2879 | ty: u8, | ||
| 2880 | data: []const u8, | ||
| 2881 | newrr: ?[*]const u8, | ||
| 2882 | buf: []u8, | ||
| 2883 | ) usize { | ||
| 2884 | // This implementation is ported from musl libc. | ||
| 2885 | // A more idiomatic "ziggy" implementation would be welcome. | ||
| 2886 | var name = dname; | ||
| 2887 | if (mem.endsWith(u8, name, ".")) name.len -= 1; | ||
| 2888 | assert(name.len <= 253); | ||
| 2889 | const n = 17 + name.len + @boolToInt(name.len != 0); | ||
| 2890 | |||
| 2891 | // Construct query template - ID will be filled later | ||
| 2892 | var q: [280]u8 = undefined; | ||
| 2893 | @memset(&q, 0, n); | ||
| 2894 | q[2] = u8(op) * 8 + 1; | ||
| 2895 | q[5] = 1; | ||
| 2896 | mem.copy(u8, q[13..], name); | ||
| 2897 | var i: usize = 13; | ||
| 2898 | var j: usize = undefined; | ||
| 2899 | while (q[i] != 0) : (i = j + 1) { | ||
| 2900 | j = i; | ||
| 2901 | while (q[j] != 0 and q[j] != '.') : (j += 1) {} | ||
| 2902 | // TODO determine the circumstances for this and whether or | ||
| 2903 | // not this should be an error. | ||
| 2904 | if (j - i - 1 > 62) unreachable; | ||
| 2905 | q[i - 1] = @intCast(u8, j - i); | ||
| 2906 | } | ||
| 2907 | q[i + 1] = ty; | ||
| 2908 | q[i + 3] = class; | ||
| 2909 | |||
| 2910 | // Make a reasonably unpredictable id | ||
| 2911 | var ts: timespec = undefined; | ||
| 2912 | clock_gettime(CLOCK_REALTIME, &ts) catch {}; | ||
| 2913 | const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count); | ||
| 2914 | const unsec = @bitCast(UInt, ts.tv_nsec); | ||
| 2915 | const id = @truncate(u32, unsec + unsec / 65536); | ||
| 2916 | q[0] = @truncate(u8, id / 256); | ||
| 2917 | q[1] = @truncate(u8, id); | ||
| 2918 | |||
| 2919 | mem.copy(u8, buf, q[0..n]); | ||
| 2920 | return n; | ||
| 2921 | } | ||
| 2922 | |||
| 2923 | pub const SendError = error{ | ||
| 2924 | /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied | ||
| 2925 | /// on the destination socket file, or search permission is denied for one of the | ||
| 2926 | /// directories the path prefix. (See path_resolution(7).) | ||
| 2927 | /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though | ||
| 2928 | /// it was a unicast address. | ||
| 2929 | AccessDenied, | ||
| 2930 | |||
| 2931 | /// The socket is marked nonblocking and the requested operation would block, and | ||
| 2932 | /// there is no global event loop configured. | ||
| 2933 | /// It's also possible to get this error under the following condition: | ||
| 2934 | /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously | ||
| 2935 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was | ||
| 2936 | /// determined that all port numbers in the ephemeral port range are currently in use. See | ||
| 2937 | /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2938 | WouldBlock, | ||
| 2939 | |||
| 2940 | /// Another Fast Open is already in progress. | ||
| 2941 | FastOpenAlreadyInProgress, | ||
| 2942 | |||
| 2943 | /// Connection reset by peer. | ||
| 2944 | ConnectionResetByPeer, | ||
| 2945 | |||
| 2946 | /// The socket type requires that message be sent atomically, and the size of the message | ||
| 2947 | /// to be sent made this impossible. The message is not transmitted. | ||
| 2948 | /// | ||
| 2949 | MessageTooBig, | ||
| 2950 | |||
| 2951 | /// The output queue for a network interface was full. This generally indicates that the | ||
| 2952 | /// interface has stopped sending, but may be caused by transient congestion. (Normally, | ||
| 2953 | /// this does not occur in Linux. Packets are just silently dropped when a device queue | ||
| 2954 | /// overflows.) | ||
| 2955 | /// This is also caused when there is not enough kernel memory available. | ||
| 2956 | SystemResources, | ||
| 2957 | |||
| 2958 | /// The local end has been shut down on a connection oriented socket. In this case, the | ||
| 2959 | /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. | ||
| 2960 | BrokenPipe, | ||
| 2961 | } || UnexpectedError; | ||
| 2962 | |||
| 2963 | /// Transmit a message to another socket. | ||
| 2964 | /// | ||
| 2965 | /// The `sendto` call may be used only when the socket is in a connected state (so that the intended | ||
| 2966 | /// recipient is known). The following call | ||
| 2967 | /// | ||
| 2968 | /// send(sockfd, buf, len, flags); | ||
| 2969 | /// | ||
| 2970 | /// is equivalent to | ||
| 2971 | /// | ||
| 2972 | /// sendto(sockfd, buf, len, flags, NULL, 0); | ||
| 2973 | /// | ||
| 2974 | /// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments | ||
| 2975 | /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted | ||
| 2976 | /// that the socket was actually connected. | ||
| 2977 | /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size. | ||
| 2978 | /// | ||
| 2979 | /// If the message is too long to pass atomically through the underlying protocol, | ||
| 2980 | /// `SendError.MessageTooBig` is returned, and the message is not transmitted. | ||
| 2981 | /// | ||
| 2982 | /// There is no indication of failure to deliver. | ||
| 2983 | /// | ||
| 2984 | /// When the message does not fit into the send buffer of the socket, `sendto` normally blocks, | ||
| 2985 | /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail | ||
| 2986 | /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is | ||
| 2987 | /// possible to send more data. | ||
| 2988 | pub fn sendto( | ||
| 2989 | /// The file descriptor of the sending socket. | ||
| 2990 | sockfd: fd_t, | ||
| 2991 | /// Message to send. | ||
| 2992 | buf: []const u8, | ||
| 2993 | flags: u32, | ||
| 2994 | dest_addr: ?*const sockaddr, | ||
| 2995 | addrlen: socklen_t, | ||
| 2996 | ) SendError!usize { | ||
| 2997 | while (true) { | ||
| 2998 | const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); | ||
| 2999 | switch (errno(rc)) { | ||
| 3000 | 0 => return rc, | ||
| 3001 | |||
| 3002 | EACCES => return error.AccessDenied, | ||
| 3003 | EAGAIN => if (std.event.Loop.instance) |loop| { | ||
| 3004 | loop.waitUntilFdWritable(sockfd); | ||
| 3005 | continue; | ||
| 3006 | } else { | ||
| 3007 | return error.WouldBlock; | ||
| 3008 | }, | ||
| 3009 | EALREADY => return error.FastOpenAlreadyInProgress, | ||
| 3010 | EBADF => unreachable, // always a race condition | ||
| 3011 | ECONNRESET => return error.ConnectionResetByPeer, | ||
| 3012 | EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. | ||
| 3013 | EFAULT => unreachable, // An invalid user space address was specified for an argument. | ||
| 3014 | EINTR => continue, | ||
| 3015 | EINVAL => unreachable, // Invalid argument passed. | ||
| 3016 | EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified | ||
| 3017 | EMSGSIZE => return error.MessageTooBig, | ||
| 3018 | ENOBUFS => return error.SystemResources, | ||
| 3019 | ENOMEM => return error.SystemResources, | ||
| 3020 | ENOTCONN => unreachable, // The socket is not connected, and no target has been given. | ||
| 3021 | ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 3022 | EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. | ||
| 3023 | EPIPE => return error.BrokenPipe, | ||
| 3024 | else => |err| return unexpectedErrno(err), | ||
| 3025 | } | ||
| 3026 | } | ||
| 3027 | } | ||
| 3028 | |||
| 3029 | /// Transmit a message to another socket. | ||
| 3030 | /// | ||
| 3031 | /// The `send` call may be used only when the socket is in a connected state (so that the intended | ||
| 3032 | /// recipient is known). The only difference between `send` and `write` is the presence of | ||
| 3033 | /// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following | ||
| 3034 | /// call | ||
| 3035 | /// | ||
| 3036 | /// send(sockfd, buf, len, flags); | ||
| 3037 | /// | ||
| 3038 | /// is equivalent to | ||
| 3039 | /// | ||
| 3040 | /// sendto(sockfd, buf, len, flags, NULL, 0); | ||
| 3041 | /// | ||
| 3042 | /// There is no indication of failure to deliver. | ||
| 3043 | /// | ||
| 3044 | /// When the message does not fit into the send buffer of the socket, `send` normally blocks, | ||
| 3045 | /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail | ||
| 3046 | /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is | ||
| 3047 | /// possible to send more data. | ||
| 3048 | pub fn send( | ||
| 3049 | /// The file descriptor of the sending socket. | ||
| 3050 | sockfd: fd_t, | ||
| 3051 | buf: []const u8, | ||
| 3052 | flags: u32, | ||
| 3053 | ) SendError!usize { | ||
| 3054 | return sendto(sockfd, buf, flags, null, 0); | ||
| 3055 | } | ||
| 3056 | |||
| 3057 | pub const PollError = error{ | ||
| 3058 | /// The kernel had no space to allocate file descriptor tables. | ||
| 3059 | SystemResources, | ||
| 3060 | } || UnexpectedError; | ||
| 3061 | |||
| 3062 | pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { | ||
| 3063 | while (true) { | ||
| 3064 | const rc = system.poll(fds.ptr, fds.len, timeout); | ||
| 3065 | switch (errno(rc)) { | ||
| 3066 | 0 => return rc, | ||
| 3067 | EFAULT => unreachable, | ||
| 3068 | EINTR => continue, | ||
| 3069 | EINVAL => unreachable, | ||
| 3070 | ENOMEM => return error.SystemResources, | ||
| 3071 | else => |err| return unexpectedErrno(err), | ||
| 3072 | } | ||
| 3073 | } | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | pub const RecvFromError = error{ | ||
| 3077 | /// The socket is marked nonblocking and the requested operation would block, and | ||
| 3078 | /// there is no global event loop configured. | ||
| 3079 | WouldBlock, | ||
| 3080 | |||
| 3081 | /// A remote host refused to allow the network connection, typically because it is not | ||
| 3082 | /// running the requested service. | ||
| 3083 | ConnectionRefused, | ||
| 3084 | |||
| 3085 | /// Could not allocate kernel memory. | ||
| 3086 | SystemResources, | ||
| 3087 | } || UnexpectedError; | ||
| 3088 | |||
| 3089 | pub fn recvfrom( | ||
| 3090 | sockfd: fd_t, | ||
| 3091 | buf: []u8, | ||
| 3092 | flags: u32, | ||
| 3093 | src_addr: ?*sockaddr, | ||
| 3094 | addrlen: ?*socklen_t, | ||
| 3095 | ) RecvFromError!usize { | ||
| 3096 | while (true) { | ||
| 3097 | const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); | ||
| 3098 | switch (errno(rc)) { | ||
| 3099 | 0 => return rc, | ||
| 3100 | EBADF => unreachable, // always a race condition | ||
| 3101 | EFAULT => unreachable, | ||
| 3102 | EINVAL => unreachable, | ||
| 3103 | ENOTCONN => unreachable, | ||
| 3104 | ENOTSOCK => unreachable, | ||
| 3105 | EINTR => continue, | ||
| 3106 | EAGAIN => if (std.event.Loop.instance) |loop| { | ||
| 3107 | loop.waitUntilFdReadable(sockfd); | ||
| 3108 | continue; | ||
| 3109 | } else { | ||
| 3110 | return error.WouldBlock; | ||
| 3111 | }, | ||
| 3112 | ENOMEM => return error.SystemResources, | ||
| 3113 | ECONNREFUSED => return error.ConnectionRefused, | ||
| 3114 | else => |err| return unexpectedErrno(err), | ||
| 3115 | } | ||
| 3116 | } | ||
| 3117 | } | ||
| 3118 | |||
| 3119 | pub const DnExpandError = error{InvalidDnsPacket}; | ||
| 3120 | |||
| 3121 | pub fn dn_expand( | ||
| 3122 | msg: []const u8, | ||
| 3123 | comp_dn: []const u8, | ||
| 3124 | exp_dn: []u8, | ||
| 3125 | ) DnExpandError!usize { | ||
| 3126 | // This implementation is ported from musl libc. | ||
| 3127 | // A more idiomatic "ziggy" implementation would be welcome. | ||
| 3128 | var p = comp_dn.ptr; | ||
| 3129 | var len: usize = std.math.maxInt(usize); | ||
| 3130 | const end = msg.ptr + msg.len; | ||
| 3131 | if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket; | ||
| 3132 | var dest = exp_dn.ptr; | ||
| 3133 | const dend = dest + std.math.min(exp_dn.len, 254); | ||
| 3134 | // detect reference loop using an iteration counter | ||
| 3135 | var i: usize = 0; | ||
| 3136 | while (i < msg.len) : (i += 2) { | ||
| 3137 | // loop invariants: p<end, dest<dend | ||
| 3138 | if ((p[0] & 0xc0) != 0) { | ||
| 3139 | if (p + 1 == end) return error.InvalidDnsPacket; | ||
| 3140 | var j = ((p[0] & usize(0x3f)) << 8) | p[1]; | ||
| 3141 | if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr); | ||
| 3142 | if (j >= msg.len) return error.InvalidDnsPacket; | ||
| 3143 | p = msg.ptr + j; | ||
| 3144 | } else if (p[0] != 0) { | ||
| 3145 | if (dest != exp_dn.ptr) { | ||
| 3146 | dest.* = '.'; | ||
| 3147 | dest += 1; | ||
| 3148 | } | ||
| 3149 | var j = p[0]; | ||
| 3150 | p += 1; | ||
| 3151 | if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) { | ||
| 3152 | return error.InvalidDnsPacket; | ||
| 3153 | } | ||
| 3154 | while (j != 0) { | ||
| 3155 | j -= 1; | ||
| 3156 | dest.* = p[0]; | ||
| 3157 | dest += 1; | ||
| 3158 | p += 1; | ||
| 3159 | } | ||
| 3160 | } else { | ||
| 3161 | dest.* = 0; | ||
| 3162 | if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr); | ||
| 3163 | return len; | ||
| 3164 | } | ||
| 3165 | } | ||
| 3166 | return error.InvalidDnsPacket; | ||
| 3167 | } |
lib/std/os/bits/darwin.zig+27-8| ... | @@ -8,26 +8,34 @@ pub const pid_t = c_int; | ... | @@ -8,26 +8,34 @@ pub const pid_t = c_int; |
| 8 | pub const in_port_t = u16; | 8 | pub const in_port_t = u16; |
| 9 | pub const sa_family_t = u8; | 9 | pub const sa_family_t = u8; |
| 10 | pub const socklen_t = u32; | 10 | pub const socklen_t = u32; |
| 11 | pub const sockaddr = extern union { | 11 | pub const sockaddr = extern struct { |
| 12 | in: sockaddr_in, | ||
| 13 | in6: sockaddr_in6, | ||
| 14 | }; | ||
| 15 | pub const sockaddr_in = extern struct { | ||
| 16 | len: u8, | 12 | len: u8, |
| 17 | family: sa_family_t, | 13 | family: sa_family_t, |
| 14 | data: [14]u8, | ||
| 15 | }; | ||
| 16 | pub const sockaddr_in = extern struct { | ||
| 17 | len: u8 = @sizeOf(sockaddr_in), | ||
| 18 | family: sa_family_t = AF_INET, | ||
| 18 | port: in_port_t, | 19 | port: in_port_t, |
| 19 | addr: u32, | 20 | addr: u32, |
| 20 | zero: [8]u8, | 21 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
| 21 | }; | 22 | }; |
| 22 | pub const sockaddr_in6 = extern struct { | 23 | pub const sockaddr_in6 = extern struct { |
| 23 | len: u8, | 24 | len: u8 = @sizeOf(sockaddr_in6), |
| 24 | family: sa_family_t, | 25 | family: sa_family_t = AF_INET6, |
| 25 | port: in_port_t, | 26 | port: in_port_t, |
| 26 | flowinfo: u32, | 27 | flowinfo: u32, |
| 27 | addr: [16]u8, | 28 | addr: [16]u8, |
| 28 | scope_id: u32, | 29 | scope_id: u32, |
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 32 | /// UNIX domain socket | ||
| 33 | pub const sockaddr_un = extern struct { | ||
| 34 | len: u8 = @sizeOf(sockaddr_un), | ||
| 35 | family: sa_family_t = AF_UNIX, | ||
| 36 | path: [104]u8, | ||
| 37 | }; | ||
| 38 | |||
| 31 | pub const timeval = extern struct { | 39 | pub const timeval = extern struct { |
| 32 | tv_sec: c_long, | 40 | tv_sec: c_long, |
| 33 | tv_usec: i32, | 41 | tv_usec: i32, |
| ... | @@ -1196,3 +1204,14 @@ pub const AT_SYMLINK_FOLLOW = 0x0040; | ... | @@ -1196,3 +1204,14 @@ pub const AT_SYMLINK_FOLLOW = 0x0040; |
| 1196 | 1204 | ||
| 1197 | /// Path refers to directory | 1205 | /// Path refers to directory |
| 1198 | pub const AT_REMOVEDIR = 0x0080; | 1206 | pub const AT_REMOVEDIR = 0x0080; |
| 1207 | |||
| 1208 | pub const addrinfo = extern struct { | ||
| 1209 | flags: i32, | ||
| 1210 | family: i32, | ||
| 1211 | socktype: i32, | ||
| 1212 | protocol: i32, | ||
| 1213 | addrlen: socklen_t, | ||
| 1214 | canonname: ?[*]u8, | ||
| 1215 | addr: ?*sockaddr, | ||
| 1216 | next: ?*addrinfo, | ||
| 1217 | }; |
lib/std/os/bits/freebsd.zig+369-46| ... | @@ -141,28 +141,40 @@ pub const dirent = extern struct { | ... | @@ -141,28 +141,40 @@ pub const dirent = extern struct { |
| 141 | pub const in_port_t = u16; | 141 | pub const in_port_t = u16; |
| 142 | pub const sa_family_t = u16; | 142 | pub const sa_family_t = u16; |
| 143 | 143 | ||
| 144 | pub const sockaddr = extern union { | 144 | pub const sockaddr = extern struct { |
| 145 | in: sockaddr_in, | 145 | /// total length |
| 146 | in6: sockaddr_in6, | 146 | len: u8, |
| 147 | |||
| 148 | /// address family | ||
| 149 | family: sa_family_t, | ||
| 150 | |||
| 151 | /// actually longer; address value | ||
| 152 | data: [14]u8, | ||
| 147 | }; | 153 | }; |
| 148 | 154 | ||
| 149 | pub const sockaddr_in = extern struct { | 155 | pub const sockaddr_in = extern struct { |
| 150 | len: u8, | 156 | len: u8 = @sizeOf(sockaddr_in), |
| 151 | family: sa_family_t, | 157 | family: sa_family_t = AF_INET, |
| 152 | port: in_port_t, | 158 | port: in_port_t, |
| 153 | addr: [16]u8, | 159 | addr: u32, |
| 154 | zero: [8]u8, | 160 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
| 155 | }; | 161 | }; |
| 156 | 162 | ||
| 157 | pub const sockaddr_in6 = extern struct { | 163 | pub const sockaddr_in6 = extern struct { |
| 158 | len: u8, | 164 | len: u8 = @sizeOf(sockaddr_in6), |
| 159 | family: sa_family_t, | 165 | family: sa_family_t = AF_INET6, |
| 160 | port: in_port_t, | 166 | port: in_port_t, |
| 161 | flowinfo: u32, | 167 | flowinfo: u32, |
| 162 | addr: [16]u8, | 168 | addr: [16]u8, |
| 163 | scope_id: u32, | 169 | scope_id: u32, |
| 164 | }; | 170 | }; |
| 165 | 171 | ||
| 172 | pub const sockaddr_un = extern struct { | ||
| 173 | len: u8 = @sizeOf(sockaddr_un), | ||
| 174 | family: sa_family_t = AF_UNIX, | ||
| 175 | path: [104]u8, | ||
| 176 | }; | ||
| 177 | |||
| 166 | pub const CTL_KERN = 1; | 178 | pub const CTL_KERN = 1; |
| 167 | pub const CTL_DEBUG = 5; | 179 | pub const CTL_DEBUG = 5; |
| 168 | 180 | ||
| ... | @@ -336,43 +348,6 @@ pub const SOCK_SEQPACKET = 5; | ... | @@ -336,43 +348,6 @@ pub const SOCK_SEQPACKET = 5; |
| 336 | pub const SOCK_CLOEXEC = 0x10000000; | 348 | pub const SOCK_CLOEXEC = 0x10000000; |
| 337 | pub const SOCK_NONBLOCK = 0x20000000; | 349 | pub const SOCK_NONBLOCK = 0x20000000; |
| 338 | 350 | ||
| 339 | pub const PROTO_ip = 0o000; | ||
| 340 | pub const PROTO_icmp = 0o001; | ||
| 341 | pub const PROTO_igmp = 0o002; | ||
| 342 | pub const PROTO_ggp = 0o003; | ||
| 343 | pub const PROTO_ipencap = 0o004; | ||
| 344 | pub const PROTO_st = 0o005; | ||
| 345 | pub const PROTO_tcp = 0o006; | ||
| 346 | pub const PROTO_egp = 0o010; | ||
| 347 | pub const PROTO_pup = 0o014; | ||
| 348 | pub const PROTO_udp = 0o021; | ||
| 349 | pub const PROTO_hmp = 0o024; | ||
| 350 | pub const PROTO_xns_idp = 0o026; | ||
| 351 | pub const PROTO_rdp = 0o033; | ||
| 352 | pub const PROTO_iso_tp4 = 0o035; | ||
| 353 | pub const PROTO_xtp = 0o044; | ||
| 354 | pub const PROTO_ddp = 0o045; | ||
| 355 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 356 | pub const PROTO_ipv6 = 0o051; | ||
| 357 | pub const PROTO_ipv6_route = 0o053; | ||
| 358 | pub const PROTO_ipv6_frag = 0o054; | ||
| 359 | pub const PROTO_idrp = 0o055; | ||
| 360 | pub const PROTO_rsvp = 0o056; | ||
| 361 | pub const PROTO_gre = 0o057; | ||
| 362 | pub const PROTO_esp = 0o062; | ||
| 363 | pub const PROTO_ah = 0o063; | ||
| 364 | pub const PROTO_skip = 0o071; | ||
| 365 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 366 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 367 | pub const PROTO_ipv6_opts = 0o074; | ||
| 368 | pub const PROTO_rspf = 0o111; | ||
| 369 | pub const PROTO_vmtp = 0o121; | ||
| 370 | pub const PROTO_ospf = 0o131; | ||
| 371 | pub const PROTO_ipip = 0o136; | ||
| 372 | pub const PROTO_encap = 0o142; | ||
| 373 | pub const PROTO_pim = 0o147; | ||
| 374 | pub const PROTO_raw = 0o377; | ||
| 375 | |||
| 376 | pub const PF_UNSPEC = 0; | 351 | pub const PF_UNSPEC = 0; |
| 377 | pub const PF_LOCAL = 1; | 352 | pub const PF_LOCAL = 1; |
| 378 | pub const PF_UNIX = PF_LOCAL; | 353 | pub const PF_UNIX = PF_LOCAL; |
| ... | @@ -963,3 +938,351 @@ pub const AT_REMOVEDIR = 0x0800; | ... | @@ -963,3 +938,351 @@ pub const AT_REMOVEDIR = 0x0800; |
| 963 | 938 | ||
| 964 | /// Fail if not under dirfd | 939 | /// Fail if not under dirfd |
| 965 | pub const AT_BENEATH = 0x1000; | 940 | pub const AT_BENEATH = 0x1000; |
| 941 | |||
| 942 | /// dummy for IP | ||
| 943 | pub const IPPROTO_IP = 0; | ||
| 944 | |||
| 945 | /// control message protocol | ||
| 946 | pub const IPPROTO_ICMP = 1; | ||
| 947 | |||
| 948 | /// tcp | ||
| 949 | pub const IPPROTO_TCP = 6; | ||
| 950 | |||
| 951 | /// user datagram protocol | ||
| 952 | pub const IPPROTO_UDP = 17; | ||
| 953 | |||
| 954 | /// IP6 header | ||
| 955 | pub const IPPROTO_IPV6 = 41; | ||
| 956 | |||
| 957 | /// raw IP packet | ||
| 958 | pub const IPPROTO_RAW = 255; | ||
| 959 | |||
| 960 | /// IP6 hop-by-hop options | ||
| 961 | pub const IPPROTO_HOPOPTS = 0; | ||
| 962 | |||
| 963 | /// group mgmt protocol | ||
| 964 | pub const IPPROTO_IGMP = 2; | ||
| 965 | |||
| 966 | /// gateway^2 (deprecated) | ||
| 967 | pub const IPPROTO_GGP = 3; | ||
| 968 | |||
| 969 | /// IPv4 encapsulation | ||
| 970 | pub const IPPROTO_IPV4 = 4; | ||
| 971 | |||
| 972 | /// for compatibility | ||
| 973 | pub const IPPROTO_IPIP = IPPROTO_IPV4; | ||
| 974 | |||
| 975 | /// Stream protocol II | ||
| 976 | pub const IPPROTO_ST = 7; | ||
| 977 | |||
| 978 | /// exterior gateway protocol | ||
| 979 | pub const IPPROTO_EGP = 8; | ||
| 980 | |||
| 981 | /// private interior gateway | ||
| 982 | pub const IPPROTO_PIGP = 9; | ||
| 983 | |||
| 984 | /// BBN RCC Monitoring | ||
| 985 | pub const IPPROTO_RCCMON = 10; | ||
| 986 | |||
| 987 | /// network voice protocol | ||
| 988 | pub const IPPROTO_NVPII = 11; | ||
| 989 | |||
| 990 | /// pup | ||
| 991 | pub const IPPROTO_PUP = 12; | ||
| 992 | |||
| 993 | /// Argus | ||
| 994 | pub const IPPROTO_ARGUS = 13; | ||
| 995 | |||
| 996 | /// EMCON | ||
| 997 | pub const IPPROTO_EMCON = 14; | ||
| 998 | |||
| 999 | /// Cross Net Debugger | ||
| 1000 | pub const IPPROTO_XNET = 15; | ||
| 1001 | |||
| 1002 | /// Chaos | ||
| 1003 | pub const IPPROTO_CHAOS = 16; | ||
| 1004 | |||
| 1005 | /// Multiplexing | ||
| 1006 | pub const IPPROTO_MUX = 18; | ||
| 1007 | |||
| 1008 | /// DCN Measurement Subsystems | ||
| 1009 | pub const IPPROTO_MEAS = 19; | ||
| 1010 | |||
| 1011 | /// Host Monitoring | ||
| 1012 | pub const IPPROTO_HMP = 20; | ||
| 1013 | |||
| 1014 | /// Packet Radio Measurement | ||
| 1015 | pub const IPPROTO_PRM = 21; | ||
| 1016 | |||
| 1017 | /// xns idp | ||
| 1018 | pub const IPPROTO_IDP = 22; | ||
| 1019 | |||
| 1020 | /// Trunk-1 | ||
| 1021 | pub const IPPROTO_TRUNK1 = 23; | ||
| 1022 | |||
| 1023 | /// Trunk-2 | ||
| 1024 | pub const IPPROTO_TRUNK2 = 24; | ||
| 1025 | |||
| 1026 | /// Leaf-1 | ||
| 1027 | pub const IPPROTO_LEAF1 = 25; | ||
| 1028 | |||
| 1029 | /// Leaf-2 | ||
| 1030 | pub const IPPROTO_LEAF2 = 26; | ||
| 1031 | |||
| 1032 | /// Reliable Data | ||
| 1033 | pub const IPPROTO_RDP = 27; | ||
| 1034 | |||
| 1035 | /// Reliable Transaction | ||
| 1036 | pub const IPPROTO_IRTP = 28; | ||
| 1037 | |||
| 1038 | /// tp-4 w/ class negotiation | ||
| 1039 | pub const IPPROTO_TP = 29; | ||
| 1040 | |||
| 1041 | /// Bulk Data Transfer | ||
| 1042 | pub const IPPROTO_BLT = 30; | ||
| 1043 | |||
| 1044 | /// Network Services | ||
| 1045 | pub const IPPROTO_NSP = 31; | ||
| 1046 | |||
| 1047 | /// Merit Internodal | ||
| 1048 | pub const IPPROTO_INP = 32; | ||
| 1049 | |||
| 1050 | /// Datagram Congestion Control Protocol | ||
| 1051 | pub const IPPROTO_DCCP = 33; | ||
| 1052 | |||
| 1053 | /// Third Party Connect | ||
| 1054 | pub const IPPROTO_3PC = 34; | ||
| 1055 | |||
| 1056 | /// InterDomain Policy Routing | ||
| 1057 | pub const IPPROTO_IDPR = 35; | ||
| 1058 | |||
| 1059 | /// XTP | ||
| 1060 | pub const IPPROTO_XTP = 36; | ||
| 1061 | |||
| 1062 | /// Datagram Delivery | ||
| 1063 | pub const IPPROTO_DDP = 37; | ||
| 1064 | |||
| 1065 | /// Control Message Transport | ||
| 1066 | pub const IPPROTO_CMTP = 38; | ||
| 1067 | |||
| 1068 | /// TP++ Transport | ||
| 1069 | pub const IPPROTO_TPXX = 39; | ||
| 1070 | |||
| 1071 | /// IL transport protocol | ||
| 1072 | pub const IPPROTO_IL = 40; | ||
| 1073 | |||
| 1074 | /// Source Demand Routing | ||
| 1075 | pub const IPPROTO_SDRP = 42; | ||
| 1076 | |||
| 1077 | /// IP6 routing header | ||
| 1078 | pub const IPPROTO_ROUTING = 43; | ||
| 1079 | |||
| 1080 | /// IP6 fragmentation header | ||
| 1081 | pub const IPPROTO_FRAGMENT = 44; | ||
| 1082 | |||
| 1083 | /// InterDomain Routing | ||
| 1084 | pub const IPPROTO_IDRP = 45; | ||
| 1085 | |||
| 1086 | /// resource reservation | ||
| 1087 | pub const IPPROTO_RSVP = 46; | ||
| 1088 | |||
| 1089 | /// General Routing Encap. | ||
| 1090 | pub const IPPROTO_GRE = 47; | ||
| 1091 | |||
| 1092 | /// Mobile Host Routing | ||
| 1093 | pub const IPPROTO_MHRP = 48; | ||
| 1094 | |||
| 1095 | /// BHA | ||
| 1096 | pub const IPPROTO_BHA = 49; | ||
| 1097 | |||
| 1098 | /// IP6 Encap Sec. Payload | ||
| 1099 | pub const IPPROTO_ESP = 50; | ||
| 1100 | |||
| 1101 | /// IP6 Auth Header | ||
| 1102 | pub const IPPROTO_AH = 51; | ||
| 1103 | |||
| 1104 | /// Integ. Net Layer Security | ||
| 1105 | pub const IPPROTO_INLSP = 52; | ||
| 1106 | |||
| 1107 | /// IP with encryption | ||
| 1108 | pub const IPPROTO_SWIPE = 53; | ||
| 1109 | |||
| 1110 | /// Next Hop Resolution | ||
| 1111 | pub const IPPROTO_NHRP = 54; | ||
| 1112 | |||
| 1113 | /// IP Mobility | ||
| 1114 | pub const IPPROTO_MOBILE = 55; | ||
| 1115 | |||
| 1116 | /// Transport Layer Security | ||
| 1117 | pub const IPPROTO_TLSP = 56; | ||
| 1118 | |||
| 1119 | /// SKIP | ||
| 1120 | pub const IPPROTO_SKIP = 57; | ||
| 1121 | |||
| 1122 | /// ICMP6 | ||
| 1123 | pub const IPPROTO_ICMPV6 = 58; | ||
| 1124 | |||
| 1125 | /// IP6 no next header | ||
| 1126 | pub const IPPROTO_NONE = 59; | ||
| 1127 | |||
| 1128 | /// IP6 destination option | ||
| 1129 | pub const IPPROTO_DSTOPTS = 60; | ||
| 1130 | |||
| 1131 | /// any host internal protocol | ||
| 1132 | pub const IPPROTO_AHIP = 61; | ||
| 1133 | |||
| 1134 | /// CFTP | ||
| 1135 | pub const IPPROTO_CFTP = 62; | ||
| 1136 | |||
| 1137 | /// "hello" routing protocol | ||
| 1138 | pub const IPPROTO_HELLO = 63; | ||
| 1139 | |||
| 1140 | /// SATNET/Backroom EXPAK | ||
| 1141 | pub const IPPROTO_SATEXPAK = 64; | ||
| 1142 | |||
| 1143 | /// Kryptolan | ||
| 1144 | pub const IPPROTO_KRYPTOLAN = 65; | ||
| 1145 | |||
| 1146 | /// Remote Virtual Disk | ||
| 1147 | pub const IPPROTO_RVD = 66; | ||
| 1148 | |||
| 1149 | /// Pluribus Packet Core | ||
| 1150 | pub const IPPROTO_IPPC = 67; | ||
| 1151 | |||
| 1152 | /// Any distributed FS | ||
| 1153 | pub const IPPROTO_ADFS = 68; | ||
| 1154 | |||
| 1155 | /// Satnet Monitoring | ||
| 1156 | pub const IPPROTO_SATMON = 69; | ||
| 1157 | |||
| 1158 | /// VISA Protocol | ||
| 1159 | pub const IPPROTO_VISA = 70; | ||
| 1160 | |||
| 1161 | /// Packet Core Utility | ||
| 1162 | pub const IPPROTO_IPCV = 71; | ||
| 1163 | |||
| 1164 | /// Comp. Prot. Net. Executive | ||
| 1165 | pub const IPPROTO_CPNX = 72; | ||
| 1166 | |||
| 1167 | /// Comp. Prot. HeartBeat | ||
| 1168 | pub const IPPROTO_CPHB = 73; | ||
| 1169 | |||
| 1170 | /// Wang Span Network | ||
| 1171 | pub const IPPROTO_WSN = 74; | ||
| 1172 | |||
| 1173 | /// Packet Video Protocol | ||
| 1174 | pub const IPPROTO_PVP = 75; | ||
| 1175 | |||
| 1176 | /// BackRoom SATNET Monitoring | ||
| 1177 | pub const IPPROTO_BRSATMON = 76; | ||
| 1178 | |||
| 1179 | /// Sun net disk proto (temp.) | ||
| 1180 | pub const IPPROTO_ND = 77; | ||
| 1181 | |||
| 1182 | /// WIDEBAND Monitoring | ||
| 1183 | pub const IPPROTO_WBMON = 78; | ||
| 1184 | |||
| 1185 | /// WIDEBAND EXPAK | ||
| 1186 | pub const IPPROTO_WBEXPAK = 79; | ||
| 1187 | |||
| 1188 | /// ISO cnlp | ||
| 1189 | pub const IPPROTO_EON = 80; | ||
| 1190 | |||
| 1191 | /// VMTP | ||
| 1192 | pub const IPPROTO_VMTP = 81; | ||
| 1193 | |||
| 1194 | /// Secure VMTP | ||
| 1195 | pub const IPPROTO_SVMTP = 82; | ||
| 1196 | |||
| 1197 | /// Banyon VINES | ||
| 1198 | pub const IPPROTO_VINES = 83; | ||
| 1199 | |||
| 1200 | /// TTP | ||
| 1201 | pub const IPPROTO_TTP = 84; | ||
| 1202 | |||
| 1203 | /// NSFNET-IGP | ||
| 1204 | pub const IPPROTO_IGP = 85; | ||
| 1205 | |||
| 1206 | /// dissimilar gateway prot. | ||
| 1207 | pub const IPPROTO_DGP = 86; | ||
| 1208 | |||
| 1209 | /// TCF | ||
| 1210 | pub const IPPROTO_TCF = 87; | ||
| 1211 | |||
| 1212 | /// Cisco/GXS IGRP | ||
| 1213 | pub const IPPROTO_IGRP = 88; | ||
| 1214 | |||
| 1215 | /// OSPFIGP | ||
| 1216 | pub const IPPROTO_OSPFIGP = 89; | ||
| 1217 | |||
| 1218 | /// Strite RPC protocol | ||
| 1219 | pub const IPPROTO_SRPC = 90; | ||
| 1220 | |||
| 1221 | /// Locus Address Resoloution | ||
| 1222 | pub const IPPROTO_LARP = 91; | ||
| 1223 | |||
| 1224 | /// Multicast Transport | ||
| 1225 | pub const IPPROTO_MTP = 92; | ||
| 1226 | |||
| 1227 | /// AX.25 Frames | ||
| 1228 | pub const IPPROTO_AX25 = 93; | ||
| 1229 | |||
| 1230 | /// IP encapsulated in IP | ||
| 1231 | pub const IPPROTO_IPEIP = 94; | ||
| 1232 | |||
| 1233 | /// Mobile Int.ing control | ||
| 1234 | pub const IPPROTO_MICP = 95; | ||
| 1235 | |||
| 1236 | /// Semaphore Comm. security | ||
| 1237 | pub const IPPROTO_SCCSP = 96; | ||
| 1238 | |||
| 1239 | /// Ethernet IP encapsulation | ||
| 1240 | pub const IPPROTO_ETHERIP = 97; | ||
| 1241 | |||
| 1242 | /// encapsulation header | ||
| 1243 | pub const IPPROTO_ENCAP = 98; | ||
| 1244 | |||
| 1245 | /// any private encr. scheme | ||
| 1246 | pub const IPPROTO_APES = 99; | ||
| 1247 | |||
| 1248 | /// GMTP | ||
| 1249 | pub const IPPROTO_GMTP = 100; | ||
| 1250 | |||
| 1251 | /// payload compression (IPComp) | ||
| 1252 | pub const IPPROTO_IPCOMP = 108; | ||
| 1253 | |||
| 1254 | /// SCTP | ||
| 1255 | pub const IPPROTO_SCTP = 132; | ||
| 1256 | |||
| 1257 | /// IPv6 Mobility Header | ||
| 1258 | pub const IPPROTO_MH = 135; | ||
| 1259 | |||
| 1260 | /// UDP-Lite | ||
| 1261 | pub const IPPROTO_UDPLITE = 136; | ||
| 1262 | |||
| 1263 | /// IP6 Host Identity Protocol | ||
| 1264 | pub const IPPROTO_HIP = 139; | ||
| 1265 | |||
| 1266 | /// IP6 Shim6 Protocol | ||
| 1267 | pub const IPPROTO_SHIM6 = 140; | ||
| 1268 | |||
| 1269 | /// Protocol Independent Mcast | ||
| 1270 | pub const IPPROTO_PIM = 103; | ||
| 1271 | |||
| 1272 | /// CARP | ||
| 1273 | pub const IPPROTO_CARP = 112; | ||
| 1274 | |||
| 1275 | /// PGM | ||
| 1276 | pub const IPPROTO_PGM = 113; | ||
| 1277 | |||
| 1278 | /// MPLS-in-IP | ||
| 1279 | pub const IPPROTO_MPLS = 137; | ||
| 1280 | |||
| 1281 | /// PFSYNC | ||
| 1282 | pub const IPPROTO_PFSYNC = 240; | ||
| 1283 | |||
| 1284 | /// Reserved | ||
| 1285 | pub const IPPROTO_RESERVED_253 = 253; | ||
| 1286 | |||
| 1287 | /// Reserved | ||
| 1288 | pub const IPPROTO_RESERVED_254 = 254; |
lib/std/os/bits/linux.zig+77-46| ... | @@ -227,43 +227,6 @@ pub const SEEK_SET = 0; | ... | @@ -227,43 +227,6 @@ pub const SEEK_SET = 0; |
| 227 | pub const SEEK_CUR = 1; | 227 | pub const SEEK_CUR = 1; |
| 228 | pub const SEEK_END = 2; | 228 | pub const SEEK_END = 2; |
| 229 | 229 | ||
| 230 | pub const PROTO_ip = 0o000; | ||
| 231 | pub const PROTO_icmp = 0o001; | ||
| 232 | pub const PROTO_igmp = 0o002; | ||
| 233 | pub const PROTO_ggp = 0o003; | ||
| 234 | pub const PROTO_ipencap = 0o004; | ||
| 235 | pub const PROTO_st = 0o005; | ||
| 236 | pub const PROTO_tcp = 0o006; | ||
| 237 | pub const PROTO_egp = 0o010; | ||
| 238 | pub const PROTO_pup = 0o014; | ||
| 239 | pub const PROTO_udp = 0o021; | ||
| 240 | pub const PROTO_hmp = 0o024; | ||
| 241 | pub const PROTO_xns_idp = 0o026; | ||
| 242 | pub const PROTO_rdp = 0o033; | ||
| 243 | pub const PROTO_iso_tp4 = 0o035; | ||
| 244 | pub const PROTO_xtp = 0o044; | ||
| 245 | pub const PROTO_ddp = 0o045; | ||
| 246 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 247 | pub const PROTO_ipv6 = 0o051; | ||
| 248 | pub const PROTO_ipv6_route = 0o053; | ||
| 249 | pub const PROTO_ipv6_frag = 0o054; | ||
| 250 | pub const PROTO_idrp = 0o055; | ||
| 251 | pub const PROTO_rsvp = 0o056; | ||
| 252 | pub const PROTO_gre = 0o057; | ||
| 253 | pub const PROTO_esp = 0o062; | ||
| 254 | pub const PROTO_ah = 0o063; | ||
| 255 | pub const PROTO_skip = 0o071; | ||
| 256 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 257 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 258 | pub const PROTO_ipv6_opts = 0o074; | ||
| 259 | pub const PROTO_rspf = 0o111; | ||
| 260 | pub const PROTO_vmtp = 0o121; | ||
| 261 | pub const PROTO_ospf = 0o131; | ||
| 262 | pub const PROTO_ipip = 0o136; | ||
| 263 | pub const PROTO_encap = 0o142; | ||
| 264 | pub const PROTO_pim = 0o147; | ||
| 265 | pub const PROTO_raw = 0o377; | ||
| 266 | |||
| 267 | pub const SHUT_RD = 0; | 230 | pub const SHUT_RD = 0; |
| 268 | pub const SHUT_WR = 1; | 231 | pub const SHUT_WR = 1; |
| 269 | pub const SHUT_RDWR = 2; | 232 | pub const SHUT_RDWR = 2; |
| ... | @@ -846,30 +809,31 @@ pub const in_port_t = u16; | ... | @@ -846,30 +809,31 @@ pub const in_port_t = u16; |
| 846 | pub const sa_family_t = u16; | 809 | pub const sa_family_t = u16; |
| 847 | pub const socklen_t = u32; | 810 | pub const socklen_t = u32; |
| 848 | 811 | ||
| 849 | /// This intentionally only has ip4 and ip6 | 812 | pub const sockaddr = extern struct { |
| 850 | pub const sockaddr = extern union { | 813 | family: sa_family_t, |
| 851 | in: sockaddr_in, | 814 | data: [14]u8, |
| 852 | in6: sockaddr_in6, | ||
| 853 | un: sockaddr_un, | ||
| 854 | }; | 815 | }; |
| 855 | 816 | ||
| 817 | /// IPv4 socket address | ||
| 856 | pub const sockaddr_in = extern struct { | 818 | pub const sockaddr_in = extern struct { |
| 857 | family: sa_family_t, | 819 | family: sa_family_t = AF_INET, |
| 858 | port: in_port_t, | 820 | port: in_port_t, |
| 859 | addr: u32, | 821 | addr: u32, |
| 860 | zero: [8]u8, | 822 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
| 861 | }; | 823 | }; |
| 862 | 824 | ||
| 825 | /// IPv6 socket address | ||
| 863 | pub const sockaddr_in6 = extern struct { | 826 | pub const sockaddr_in6 = extern struct { |
| 864 | family: sa_family_t, | 827 | family: sa_family_t = AF_INET6, |
| 865 | port: in_port_t, | 828 | port: in_port_t, |
| 866 | flowinfo: u32, | 829 | flowinfo: u32, |
| 867 | addr: [16]u8, | 830 | addr: [16]u8, |
| 868 | scope_id: u32, | 831 | scope_id: u32, |
| 869 | }; | 832 | }; |
| 870 | 833 | ||
| 834 | /// UNIX domain socket address | ||
| 871 | pub const sockaddr_un = extern struct { | 835 | pub const sockaddr_un = extern struct { |
| 872 | family: sa_family_t, | 836 | family: sa_family_t = AF_UNIX, |
| 873 | path: [108]u8, | 837 | path: [108]u8, |
| 874 | }; | 838 | }; |
| 875 | 839 | ||
| ... | @@ -1388,3 +1352,70 @@ pub const Statx = extern struct { | ... | @@ -1388,3 +1352,70 @@ pub const Statx = extern struct { |
| 1388 | 1352 | ||
| 1389 | __pad2: [14]u64, | 1353 | __pad2: [14]u64, |
| 1390 | }; | 1354 | }; |
| 1355 | |||
| 1356 | pub const addrinfo = extern struct { | ||
| 1357 | flags: i32, | ||
| 1358 | family: i32, | ||
| 1359 | socktype: i32, | ||
| 1360 | protocol: i32, | ||
| 1361 | addrlen: socklen_t, | ||
| 1362 | addr: ?*sockaddr, | ||
| 1363 | canonname: ?[*]u8, | ||
| 1364 | next: ?*addrinfo, | ||
| 1365 | }; | ||
| 1366 | |||
| 1367 | pub const IPPORT_RESERVED = 1024; | ||
| 1368 | |||
| 1369 | pub const IPPROTO_IP = 0; | ||
| 1370 | pub const IPPROTO_HOPOPTS = 0; | ||
| 1371 | pub const IPPROTO_ICMP = 1; | ||
| 1372 | pub const IPPROTO_IGMP = 2; | ||
| 1373 | pub const IPPROTO_IPIP = 4; | ||
| 1374 | pub const IPPROTO_TCP = 6; | ||
| 1375 | pub const IPPROTO_EGP = 8; | ||
| 1376 | pub const IPPROTO_PUP = 12; | ||
| 1377 | pub const IPPROTO_UDP = 17; | ||
| 1378 | pub const IPPROTO_IDP = 22; | ||
| 1379 | pub const IPPROTO_TP = 29; | ||
| 1380 | pub const IPPROTO_DCCP = 33; | ||
| 1381 | pub const IPPROTO_IPV6 = 41; | ||
| 1382 | pub const IPPROTO_ROUTING = 43; | ||
| 1383 | pub const IPPROTO_FRAGMENT = 44; | ||
| 1384 | pub const IPPROTO_RSVP = 46; | ||
| 1385 | pub const IPPROTO_GRE = 47; | ||
| 1386 | pub const IPPROTO_ESP = 50; | ||
| 1387 | pub const IPPROTO_AH = 51; | ||
| 1388 | pub const IPPROTO_ICMPV6 = 58; | ||
| 1389 | pub const IPPROTO_NONE = 59; | ||
| 1390 | pub const IPPROTO_DSTOPTS = 60; | ||
| 1391 | pub const IPPROTO_MTP = 92; | ||
| 1392 | pub const IPPROTO_BEETPH = 94; | ||
| 1393 | pub const IPPROTO_ENCAP = 98; | ||
| 1394 | pub const IPPROTO_PIM = 103; | ||
| 1395 | pub const IPPROTO_COMP = 108; | ||
| 1396 | pub const IPPROTO_SCTP = 132; | ||
| 1397 | pub const IPPROTO_MH = 135; | ||
| 1398 | pub const IPPROTO_UDPLITE = 136; | ||
| 1399 | pub const IPPROTO_MPLS = 137; | ||
| 1400 | pub const IPPROTO_RAW = 255; | ||
| 1401 | pub const IPPROTO_MAX = 256; | ||
| 1402 | |||
| 1403 | pub const RR_A = 1; | ||
| 1404 | pub const RR_CNAME = 5; | ||
| 1405 | pub const RR_AAAA = 28; | ||
| 1406 | |||
| 1407 | pub const nfds_t = usize; | ||
| 1408 | pub const pollfd = extern struct { | ||
| 1409 | fd: fd_t, | ||
| 1410 | events: i16, | ||
| 1411 | revents: i16, | ||
| 1412 | }; | ||
| 1413 | |||
| 1414 | pub const POLLIN = 0x001; | ||
| 1415 | pub const POLLPRI = 0x002; | ||
| 1416 | pub const POLLOUT = 0x004; | ||
| 1417 | pub const POLLERR = 0x008; | ||
| 1418 | pub const POLLHUP = 0x010; | ||
| 1419 | pub const POLLNVAL = 0x020; | ||
| 1420 | pub const POLLRDNORM = 0x040; | ||
| 1421 | pub const POLLRDBAND = 0x080; |
lib/std/os/bits/netbsd.zig+135-31| ... | @@ -137,21 +137,27 @@ pub const dirent = extern struct { | ... | @@ -137,21 +137,27 @@ pub const dirent = extern struct { |
| 137 | pub const in_port_t = u16; | 137 | pub const in_port_t = u16; |
| 138 | pub const sa_family_t = u8; | 138 | pub const sa_family_t = u8; |
| 139 | 139 | ||
| 140 | pub const sockaddr = extern union { | 140 | pub const sockaddr = extern struct { |
| 141 | in: sockaddr_in, | 141 | /// total length |
| 142 | in6: sockaddr_in6, | 142 | len: u8, |
| 143 | |||
| 144 | /// address family | ||
| 145 | family: sa_family_t, | ||
| 146 | |||
| 147 | /// actually longer; address value | ||
| 148 | data: [14]u8, | ||
| 143 | }; | 149 | }; |
| 144 | 150 | ||
| 145 | pub const sockaddr_in = extern struct { | 151 | pub const sockaddr_in = extern struct { |
| 146 | len: u8, | 152 | len: u8 = @sizeOf(sockaddr_in), |
| 147 | family: sa_family_t, | 153 | family: sa_family_t, |
| 148 | port: in_port_t, | 154 | port: in_port_t, |
| 149 | addr: u32, | 155 | addr: u32, |
| 150 | zero: [8]u8, | 156 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, |
| 151 | }; | 157 | }; |
| 152 | 158 | ||
| 153 | pub const sockaddr_in6 = extern struct { | 159 | pub const sockaddr_in6 = extern struct { |
| 154 | len: u8, | 160 | len: u8 = @sizeOf(sockaddr_in6), |
| 155 | family: sa_family_t, | 161 | family: sa_family_t, |
| 156 | port: in_port_t, | 162 | port: in_port_t, |
| 157 | flowinfo: u32, | 163 | flowinfo: u32, |
| ... | @@ -159,6 +165,18 @@ pub const sockaddr_in6 = extern struct { | ... | @@ -159,6 +165,18 @@ pub const sockaddr_in6 = extern struct { |
| 159 | scope_id: u32, | 165 | scope_id: u32, |
| 160 | }; | 166 | }; |
| 161 | 167 | ||
| 168 | /// Definitions for UNIX IPC domain. | ||
| 169 | pub const sockaddr_un = extern struct { | ||
| 170 | /// total sockaddr length | ||
| 171 | len: u8 = @sizeOf(sockaddr_un), | ||
| 172 | |||
| 173 | /// AF_LOCAL | ||
| 174 | family: sa_family_t, | ||
| 175 | |||
| 176 | /// path name | ||
| 177 | path: [104]u8, | ||
| 178 | }; | ||
| 179 | |||
| 162 | pub const CTL_KERN = 1; | 180 | pub const CTL_KERN = 1; |
| 163 | pub const CTL_DEBUG = 5; | 181 | pub const CTL_DEBUG = 5; |
| 164 | 182 | ||
| ... | @@ -316,31 +334,6 @@ pub const SOCK_SEQPACKET = 5; | ... | @@ -316,31 +334,6 @@ pub const SOCK_SEQPACKET = 5; |
| 316 | pub const SOCK_CLOEXEC = 0x10000000; | 334 | pub const SOCK_CLOEXEC = 0x10000000; |
| 317 | pub const SOCK_NONBLOCK = 0x20000000; | 335 | pub const SOCK_NONBLOCK = 0x20000000; |
| 318 | 336 | ||
| 319 | pub const PROTO_ip = 0; | ||
| 320 | pub const PROTO_icmp = 1; | ||
| 321 | pub const PROTO_igmp = 2; | ||
| 322 | pub const PROTO_ggp = 3; | ||
| 323 | pub const PROTO_ipencap = 4; | ||
| 324 | pub const PROTO_tcp = 6; | ||
| 325 | pub const PROTO_egp = 8; | ||
| 326 | pub const PROTO_pup = 12; | ||
| 327 | pub const PROTO_udp = 17; | ||
| 328 | pub const PROTO_xns_idp = 22; | ||
| 329 | pub const PROTO_iso_tp4 = 29; | ||
| 330 | pub const PROTO_ipv6 = 41; | ||
| 331 | pub const PROTO_ipv6_route = 43; | ||
| 332 | pub const PROTO_ipv6_frag = 44; | ||
| 333 | pub const PROTO_rsvp = 46; | ||
| 334 | pub const PROTO_gre = 47; | ||
| 335 | pub const PROTO_esp = 50; | ||
| 336 | pub const PROTO_ah = 51; | ||
| 337 | pub const PROTO_ipv6_icmp = 58; | ||
| 338 | pub const PROTO_ipv6_nonxt = 59; | ||
| 339 | pub const PROTO_ipv6_opts = 60; | ||
| 340 | pub const PROTO_encap = 98; | ||
| 341 | pub const PROTO_pim = 103; | ||
| 342 | pub const PROTO_raw = 255; | ||
| 343 | |||
| 344 | pub const PF_UNSPEC = 0; | 337 | pub const PF_UNSPEC = 0; |
| 345 | pub const PF_LOCAL = 1; | 338 | pub const PF_LOCAL = 1; |
| 346 | pub const PF_UNIX = PF_LOCAL; | 339 | pub const PF_UNIX = PF_LOCAL; |
| ... | @@ -827,3 +820,114 @@ pub fn S_IWHT(m: u32) bool { | ... | @@ -827,3 +820,114 @@ pub fn S_IWHT(m: u32) bool { |
| 827 | } | 820 | } |
| 828 | 821 | ||
| 829 | pub const HOST_NAME_MAX = 255; | 822 | pub const HOST_NAME_MAX = 255; |
| 823 | |||
| 824 | /// dummy for IP | ||
| 825 | pub const IPPROTO_IP = 0; | ||
| 826 | |||
| 827 | /// IP6 hop-by-hop options | ||
| 828 | pub const IPPROTO_HOPOPTS = 0; | ||
| 829 | |||
| 830 | /// control message protocol | ||
| 831 | pub const IPPROTO_ICMP = 1; | ||
| 832 | |||
| 833 | /// group mgmt protocol | ||
| 834 | pub const IPPROTO_IGMP = 2; | ||
| 835 | |||
| 836 | /// gateway^2 (deprecated) | ||
| 837 | pub const IPPROTO_GGP = 3; | ||
| 838 | |||
| 839 | /// IP header | ||
| 840 | pub const IPPROTO_IPV4 = 4; | ||
| 841 | |||
| 842 | /// IP inside IP | ||
| 843 | pub const IPPROTO_IPIP = 4; | ||
| 844 | |||
| 845 | /// tcp | ||
| 846 | pub const IPPROTO_TCP = 6; | ||
| 847 | |||
| 848 | /// exterior gateway protocol | ||
| 849 | pub const IPPROTO_EGP = 8; | ||
| 850 | |||
| 851 | /// pup | ||
| 852 | pub const IPPROTO_PUP = 12; | ||
| 853 | |||
| 854 | /// user datagram protocol | ||
| 855 | pub const IPPROTO_UDP = 17; | ||
| 856 | |||
| 857 | /// xns idp | ||
| 858 | pub const IPPROTO_IDP = 22; | ||
| 859 | |||
| 860 | /// tp-4 w/ class negotiation | ||
| 861 | pub const IPPROTO_TP = 29; | ||
| 862 | |||
| 863 | /// DCCP | ||
| 864 | pub const IPPROTO_DCCP = 33; | ||
| 865 | |||
| 866 | /// IP6 header | ||
| 867 | pub const IPPROTO_IPV6 = 41; | ||
| 868 | |||
| 869 | /// IP6 routing header | ||
| 870 | pub const IPPROTO_ROUTING = 43; | ||
| 871 | |||
| 872 | /// IP6 fragmentation header | ||
| 873 | pub const IPPROTO_FRAGMENT = 44; | ||
| 874 | |||
| 875 | /// resource reservation | ||
| 876 | pub const IPPROTO_RSVP = 46; | ||
| 877 | |||
| 878 | /// GRE encaps RFC 1701 | ||
| 879 | pub const IPPROTO_GRE = 47; | ||
| 880 | |||
| 881 | /// encap. security payload | ||
| 882 | pub const IPPROTO_ESP = 50; | ||
| 883 | |||
| 884 | /// authentication header | ||
| 885 | pub const IPPROTO_AH = 51; | ||
| 886 | |||
| 887 | /// IP Mobility RFC 2004 | ||
| 888 | pub const IPPROTO_MOBILE = 55; | ||
| 889 | |||
| 890 | /// IPv6 ICMP | ||
| 891 | pub const IPPROTO_IPV6_ICMP = 58; | ||
| 892 | |||
| 893 | /// ICMP6 | ||
| 894 | pub const IPPROTO_ICMPV6 = 58; | ||
| 895 | |||
| 896 | /// IP6 no next header | ||
| 897 | pub const IPPROTO_NONE = 59; | ||
| 898 | |||
| 899 | /// IP6 destination option | ||
| 900 | pub const IPPROTO_DSTOPTS = 60; | ||
| 901 | |||
| 902 | /// ISO cnlp | ||
| 903 | pub const IPPROTO_EON = 80; | ||
| 904 | |||
| 905 | /// Ethernet-in-IP | ||
| 906 | pub const IPPROTO_ETHERIP = 97; | ||
| 907 | |||
| 908 | /// encapsulation header | ||
| 909 | pub const IPPROTO_ENCAP = 98; | ||
| 910 | |||
| 911 | /// Protocol indep. multicast | ||
| 912 | pub const IPPROTO_PIM = 103; | ||
| 913 | |||
| 914 | /// IP Payload Comp. Protocol | ||
| 915 | pub const IPPROTO_IPCOMP = 108; | ||
| 916 | |||
| 917 | /// VRRP RFC 2338 | ||
| 918 | pub const IPPROTO_VRRP = 112; | ||
| 919 | |||
| 920 | /// Common Address Resolution Protocol | ||
| 921 | pub const IPPROTO_CARP = 112; | ||
| 922 | |||
| 923 | /// L2TPv3 | ||
| 924 | pub const IPPROTO_L2TP = 115; | ||
| 925 | |||
| 926 | /// SCTP | ||
| 927 | pub const IPPROTO_SCTP = 132; | ||
| 928 | |||
| 929 | /// PFSYNC | ||
| 930 | pub const IPPROTO_PFSYNC = 240; | ||
| 931 | |||
| 932 | /// raw IP packet | ||
| 933 | pub const IPPROTO_RAW = 255; |
lib/std/os/bits/windows.zig+60| ... | @@ -161,3 +161,63 @@ pub const F_OK = 0; | ... | @@ -161,3 +161,63 @@ pub const F_OK = 0; |
| 161 | 161 | ||
| 162 | /// Remove directory instead of unlinking file | 162 | /// Remove directory instead of unlinking file |
| 163 | pub const AT_REMOVEDIR = 0x200; | 163 | pub const AT_REMOVEDIR = 0x200; |
| 164 | |||
| 165 | pub const in_port_t = u16; | ||
| 166 | pub const sa_family_t = u16; | ||
| 167 | pub const socklen_t = u32; | ||
| 168 | |||
| 169 | pub const sockaddr = extern struct { | ||
| 170 | family: sa_family_t, | ||
| 171 | data: [14]u8, | ||
| 172 | }; | ||
| 173 | pub const sockaddr_in = extern struct { | ||
| 174 | family: sa_family_t = AF_INET, | ||
| 175 | port: in_port_t, | ||
| 176 | addr: in_addr, | ||
| 177 | zero: [8]u8 = [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, | ||
| 178 | }; | ||
| 179 | pub const sockaddr_in6 = extern struct { | ||
| 180 | family: sa_family_t = AF_INET6, | ||
| 181 | port: in_port_t, | ||
| 182 | flowinfo: u32, | ||
| 183 | addr: in6_addr, | ||
| 184 | scope_id: u32, | ||
| 185 | }; | ||
| 186 | pub const in6_addr = [16]u8; | ||
| 187 | pub const in_addr = u32; | ||
| 188 | |||
| 189 | pub const AF_UNSPEC = 0; | ||
| 190 | pub const AF_UNIX = 1; | ||
| 191 | pub const AF_INET = 2; | ||
| 192 | pub const AF_IMPLINK = 3; | ||
| 193 | pub const AF_PUP = 4; | ||
| 194 | pub const AF_CHAOS = 5; | ||
| 195 | pub const AF_NS = 6; | ||
| 196 | pub const AF_IPX = AF_NS; | ||
| 197 | pub const AF_ISO = 7; | ||
| 198 | pub const AF_OSI = AF_ISO; | ||
| 199 | pub const AF_ECMA = 8; | ||
| 200 | pub const AF_DATAKIT = 9; | ||
| 201 | pub const AF_CCITT = 10; | ||
| 202 | pub const AF_SNA = 11; | ||
| 203 | pub const AF_DECnet = 12; | ||
| 204 | pub const AF_DLI = 13; | ||
| 205 | pub const AF_LAT = 14; | ||
| 206 | pub const AF_HYLINK = 15; | ||
| 207 | pub const AF_APPLETALK = 16; | ||
| 208 | pub const AF_NETBIOS = 17; | ||
| 209 | pub const AF_VOICEVIEW = 18; | ||
| 210 | pub const AF_FIREFOX = 19; | ||
| 211 | pub const AF_UNKNOWN1 = 20; | ||
| 212 | pub const AF_BAN = 21; | ||
| 213 | pub const AF_ATM = 22; | ||
| 214 | pub const AF_INET6 = 23; | ||
| 215 | pub const AF_CLUSTER = 24; | ||
| 216 | pub const AF_12844 = 25; | ||
| 217 | pub const AF_IRDA = 26; | ||
| 218 | pub const AF_NETDES = 28; | ||
| 219 | pub const AF_TCNPROCESS = 29; | ||
| 220 | pub const AF_TCNMESSAGE = 30; | ||
| 221 | pub const AF_ICLFXBM = 31; | ||
| 222 | pub const AF_BTH = 32; | ||
| 223 | pub const AF_MAX = 33; |
lib/std/os/linux.zig+22| ... | @@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize { | ... | @@ -226,6 +226,28 @@ pub fn munmap(address: [*]const u8, length: usize) usize { |
| 226 | return syscall2(SYS_munmap, @ptrToInt(address), length); | 226 | return syscall2(SYS_munmap, @ptrToInt(address), length); |
| 227 | } | 227 | } |
| 228 | 228 | ||
| 229 | pub fn poll(fds: [*]pollfd, n: nfds_t, timeout: i32) usize { | ||
| 230 | if (@hasDecl(@This(), "SYS_poll")) { | ||
| 231 | return syscall3(SYS_poll, @ptrToInt(fds), n, @bitCast(u32, timeout)); | ||
| 232 | } else { | ||
| 233 | return syscall6( | ||
| 234 | SYS_ppoll, | ||
| 235 | @ptrToInt(fds), | ||
| 236 | n, | ||
| 237 | @ptrToInt(if (timeout >= 0) | ||
| 238 | &timespec{ | ||
| 239 | .tv_sec = @divTrunc(timeout, 1000), | ||
| 240 | .tv_nsec = @rem(timeout, 1000) * 1000000, | ||
| 241 | } | ||
| 242 | else | ||
| 243 | null), | ||
| 244 | 0, | ||
| 245 | 0, | ||
| 246 | NSIG / 8, | ||
| 247 | ); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 229 | pub fn read(fd: i32, buf: [*]u8, count: usize) usize { | 251 | pub fn read(fd: i32, buf: [*]u8, count: usize) usize { |
| 230 | return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | 252 | return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); |
| 231 | } | 253 | } |
lib/std/target.zig+2| ... | @@ -205,6 +205,8 @@ pub const Target = union(enum) { | ... | @@ -205,6 +205,8 @@ pub const Target = union(enum) { |
| 205 | }, | 205 | }, |
| 206 | }; | 206 | }; |
| 207 | 207 | ||
| 208 | pub const stack_align = 16; | ||
| 209 | |||
| 208 | pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { | 210 | pub fn zigTriple(self: Target, allocator: *mem.Allocator) ![]u8 { |
| 209 | return std.fmt.allocPrint( | 211 | return std.fmt.allocPrint( |
| 210 | allocator, | 212 | allocator, |
src-self-hosted/stage1.zig+1| ... | @@ -128,6 +128,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error | ... | @@ -128,6 +128,7 @@ export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, error |
| 128 | export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { | 128 | export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error { |
| 129 | const c_out_stream = &std.io.COutStream.init(output_file).stream; | 129 | const c_out_stream = &std.io.COutStream.init(output_file).stream; |
| 130 | _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) { | 130 | _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) { |
| 131 | error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode | ||
| 131 | error.SystemResources => return Error.SystemResources, | 132 | error.SystemResources => return Error.SystemResources, |
| 132 | error.OperationAborted => return Error.OperationAborted, | 133 | error.OperationAborted => return Error.OperationAborted, |
| 133 | error.BrokenPipe => return Error.BrokenPipe, | 134 | error.BrokenPipe => return Error.BrokenPipe, |
src-self-hosted/translate_c.zig+14-10| ... | @@ -151,18 +151,22 @@ pub fn translate( | ... | @@ -151,18 +151,22 @@ pub fn translate( |
| 151 | }; | 151 | }; |
| 152 | defer ZigClangASTUnit_delete(ast_unit); | 152 | defer ZigClangASTUnit_delete(ast_unit); |
| 153 | 153 | ||
| 154 | var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); | 154 | const tree = blk: { |
| 155 | errdefer tree_arena.deinit(); | 155 | var tree_arena = std.heap.ArenaAllocator.init(backing_allocator); |
| 156 | 156 | errdefer tree_arena.deinit(); | |
| 157 | const tree = try tree_arena.allocator.create(ast.Tree); | 157 | |
| 158 | tree.* = ast.Tree{ | 158 | const tree = try tree_arena.allocator.create(ast.Tree); |
| 159 | .source = undefined, // need to use Buffer.toOwnedSlice later | 159 | tree.* = ast.Tree{ |
| 160 | .root_node = undefined, | 160 | .source = undefined, // need to use Buffer.toOwnedSlice later |
| 161 | .arena_allocator = tree_arena, | 161 | .root_node = undefined, |
| 162 | .tokens = undefined, // can't reference the allocator yet | 162 | .arena_allocator = tree_arena, |
| 163 | .errors = undefined, // can't reference the allocator yet | 163 | .tokens = undefined, // can't reference the allocator yet |
| 164 | .errors = undefined, // can't reference the allocator yet | ||
| 165 | }; | ||
| 166 | break :blk tree; | ||
| 164 | }; | 167 | }; |
| 165 | const arena = &tree.arena_allocator.allocator; // now we can reference the allocator | 168 | const arena = &tree.arena_allocator.allocator; // now we can reference the allocator |
| 169 | errdefer tree.arena_allocator.deinit(); | ||
| 166 | tree.tokens = ast.Tree.TokenList.init(arena); | 170 | tree.tokens = ast.Tree.TokenList.init(arena); |
| 167 | tree.errors = ast.Tree.ErrorList.init(arena); | 171 | tree.errors = ast.Tree.ErrorList.init(arena); |
| 168 | 172 |
src/analyze.cpp+10-1| ... | @@ -4381,6 +4381,10 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode | ... | @@ -4381,6 +4381,10 @@ static Error analyze_callee_async(CodeGen *g, ZigFn *fn, ZigFn *callee, AstNode |
| 4381 | if (callee->anal_state == FnAnalStateComplete) { | 4381 | if (callee->anal_state == FnAnalStateComplete) { |
| 4382 | analyze_fn_async(g, callee, true); | 4382 | analyze_fn_async(g, callee, true); |
| 4383 | if (callee->anal_state == FnAnalStateInvalid) { | 4383 | if (callee->anal_state == FnAnalStateInvalid) { |
| 4384 | if (g->trace_err != nullptr) { | ||
| 4385 | g->trace_err = add_error_note(g, g->trace_err, call_node, | ||
| 4386 | buf_sprintf("while checking if '%s' is async", buf_ptr(&fn->symbol_name))); | ||
| 4387 | } | ||
| 4384 | return ErrorSemanticAnalyzeFail; | 4388 | return ErrorSemanticAnalyzeFail; |
| 4385 | } | 4389 | } |
| 4386 | callee_is_async = fn_is_async(callee); | 4390 | callee_is_async = fn_is_async(callee); |
| ... | @@ -6128,6 +6132,9 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { | ... | @@ -6128,6 +6132,9 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { |
| 6128 | param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i); | 6132 | param_name = buf_sprintf("@arg%" ZIG_PRI_usize, arg_i); |
| 6129 | } | 6133 | } |
| 6130 | ZigType *param_type = param_info->type; | 6134 | ZigType *param_type = param_info->type; |
| 6135 | if ((err = type_resolve(g, param_type, ResolveStatusSizeKnown))) { | ||
| 6136 | return err; | ||
| 6137 | } | ||
| 6131 | 6138 | ||
| 6132 | fields.append({buf_ptr(param_name), param_type, 0}); | 6139 | fields.append({buf_ptr(param_name), param_type, 0}); |
| 6133 | } | 6140 | } |
| ... | @@ -7538,7 +7545,9 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { | ... | @@ -7538,7 +7545,9 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { |
| 7538 | 7545 | ||
| 7539 | uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) { | 7546 | uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) { |
| 7540 | assert(struct_type->id == ZigTypeIdStruct); | 7547 | assert(struct_type->id == ZigTypeIdStruct); |
| 7541 | assert(type_is_resolved(struct_type, ResolveStatusSizeKnown)); | 7548 | if (struct_type->data.structure.layout != ContainerLayoutAuto) { |
| 7549 | assert(type_is_resolved(struct_type, ResolveStatusSizeKnown)); | ||
| 7550 | } | ||
| 7542 | if (struct_type->data.structure.host_int_bytes == nullptr) | 7551 | if (struct_type->data.structure.host_int_bytes == nullptr) |
| 7543 | return 0; | 7552 | return 0; |
| 7544 | return struct_type->data.structure.host_int_bytes[field->gen_index]; | 7553 | return struct_type->data.structure.host_int_bytes[field->gen_index]; |
src/ir.cpp+10-3| ... | @@ -17692,7 +17692,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct | ... | @@ -17692,7 +17692,8 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct |
| 17692 | { | 17692 | { |
| 17693 | size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; | 17693 | size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; |
| 17694 | uint64_t new_index = offset + index; | 17694 | uint64_t new_index = offset + index; |
| 17695 | assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len); | 17695 | ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, |
| 17696 | &elem_ptr_instruction->base); | ||
| 17696 | out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; | 17697 | out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; |
| 17697 | out_val->data.x_ptr.data.base_array.array_val = | 17698 | out_val->data.x_ptr.data.base_array.array_val = |
| 17698 | ptr_field->data.x_ptr.data.base_array.array_val; | 17699 | ptr_field->data.x_ptr.data.base_array.array_val; |
| ... | @@ -17854,7 +17855,10 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction | ... | @@ -17854,7 +17855,10 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction |
| 17854 | case OnePossibleValueNo: | 17855 | case OnePossibleValueNo: |
| 17855 | break; | 17856 | break; |
| 17856 | } | 17857 | } |
| 17857 | if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusAlignmentKnown))) | 17858 | ResolveStatus needed_resolve_status = |
| 17859 | (struct_type->data.structure.layout == ContainerLayoutAuto) ? | ||
| 17860 | ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown; | ||
| 17861 | if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status))) | ||
| 17858 | return ira->codegen->invalid_instruction; | 17862 | return ira->codegen->invalid_instruction; |
| 17859 | assert(struct_ptr->value.type->id == ZigTypeIdPointer); | 17863 | assert(struct_ptr->value.type->id == ZigTypeIdPointer); |
| 17860 | uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host; | 17864 | uint32_t ptr_bit_offset = struct_ptr->value.type->data.pointer.bit_offset_in_host; |
| ... | @@ -17873,6 +17877,9 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction | ... | @@ -17873,6 +17877,9 @@ static IrInstruction *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInstruction |
| 17873 | return ira->codegen->invalid_instruction; | 17877 | return ira->codegen->invalid_instruction; |
| 17874 | 17878 | ||
| 17875 | if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { | 17879 | if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { |
| 17880 | if ((err = type_resolve(ira->codegen, struct_type, ResolveStatusSizeKnown))) | ||
| 17881 | return ira->codegen->invalid_instruction; | ||
| 17882 | |||
| 17876 | ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); | 17883 | ConstExprValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); |
| 17877 | if (struct_val == nullptr) | 17884 | if (struct_val == nullptr) |
| 17878 | return ira->codegen->invalid_instruction; | 17885 | return ira->codegen->invalid_instruction; |
| ... | @@ -17919,7 +17926,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ | ... | @@ -17919,7 +17926,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ |
| 17919 | Error err; | 17926 | Error err; |
| 17920 | 17927 | ||
| 17921 | ZigType *bare_type = container_ref_type(container_type); | 17928 | ZigType *bare_type = container_ref_type(container_type); |
| 17922 | if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusSizeKnown))) | 17929 | if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown))) |
| 17923 | return ira->codegen->invalid_instruction; | 17930 | return ira->codegen->invalid_instruction; |
| 17924 | 17931 | ||
| 17925 | assert(container_ptr->value.type->id == ZigTypeIdPointer); | 17932 | assert(container_ptr->value.type->id == ZigTypeIdPointer); |
test/compile_errors.zig+2-2| ... | @@ -162,9 +162,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -162,9 +162,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 162 | \\ const obj = AstObject{ .lhsExpr = lhsExpr }; | 162 | \\ const obj = AstObject{ .lhsExpr = lhsExpr }; |
| 163 | \\} | 163 | \\} |
| 164 | , | 164 | , |
| 165 | "tmp.zig:1:17: error: struct 'LhsExpr' depends on itself", | 165 | "tmp.zig:4:19: error: union 'AstObject' depends on itself", |
| 166 | "tmp.zig:5:5: note: while checking this field", | ||
| 167 | "tmp.zig:2:5: note: while checking this field", | 166 | "tmp.zig:2:5: note: while checking this field", |
| 167 | "tmp.zig:5:5: note: while checking this field", | ||
| 168 | ); | 168 | ); |
| 169 | 169 | ||
| 170 | cases.add( | 170 | cases.add( |