authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-30 21:30:16-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-10-30 21:30:16-04:00
log61d5a0bf48d034208aea37d72dac5b3531334be7
tree57e545a972ae44c3bc6bab98396f9cb22203edd1
parent6a15e8a7a771bcbf2534cceecd77231344aafbf8
parent7b7ba51642c832c77ec2668491843be3b0114124
signaturelock-open Commit is signed but in an unrecognized format.

Merge branch 'std.net'


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 }
7474
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 }
7878
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 }
8282
...@@ -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 }
9393
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 }
9797
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 }
101101
...@@ -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 }
113113
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 }
117117
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 }
122122
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 }
134134
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
117pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;117pub extern "c" fn connect(sockfd: fd_t, sock_addr: *const sockaddr, addrlen: socklen_t) c_int;
118pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;118pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, flags: c_uint) c_int;
119pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;119pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
120pub extern "c" fn send(sockfd: fd_t, buf: *const c_void, len: usize, flags: u32) isize;
121pub 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
130pub extern fn recv(sockfd: fd_t, arg1: ?*c_void, arg2: usize, arg3: c_int) isize;
131pub 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
120pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;140pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
121pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;141pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
122pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;142pub 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
173pub 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
180pub extern "c" fn freeaddrinfo(res: *addrinfo) void;
181
182pub 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
192pub extern "c" fn gai_strerror(errcode: c_int) [*]const u8;
193
194pub extern "c" fn poll(fds: [*]pollfd, nfds: nfds_t, timeout: c_int) c_int;
195
196pub 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}
5757
58pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;58pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
59
60/// get address to use bind()
61pub const AI_PASSIVE = 0x00000001;
62
63/// fill ai_canonname
64pub const AI_CANONNAME = 0x00000002;
65
66/// prevent host name resolution
67pub const AI_NUMERICHOST = 0x00000004;
68
69/// prevent service name resolution
70pub const AI_NUMERICSERV = 0x00001000;
71
72/// address family for hostname not supported
73pub const EAI_ADDRFAMILY = 1;
74
75/// temporary failure in name resolution
76pub const EAI_AGAIN = 2;
77
78/// invalid value for ai_flags
79pub const EAI_BADFLAGS = 3;
80
81/// non-recoverable failure in name resolution
82pub const EAI_FAIL = 4;
83
84/// ai_family not supported
85pub const EAI_FAMILY = 5;
86
87/// memory allocation failure
88pub const EAI_MEMORY = 6;
89
90/// no address associated with hostname
91pub const EAI_NODATA = 7;
92
93/// hostname nor servname provided, or not known
94pub const EAI_NONAME = 8;
95
96/// servname not supported for ai_socktype
97pub const EAI_SERVICE = 9;
98
99/// ai_socktype not supported
100pub const EAI_SOCKTYPE = 10;
101
102/// system error returned in errno
103pub const EAI_SYSTEM = 11;
104
105/// invalid value for hints
106pub const EAI_BADHINTS = 12;
107
108/// resolved protocol is unknown
109pub const EAI_PROTOCOL = 13;
110
111/// argument buffer overflow
112pub const EAI_OVERFLOW = 14;
113pub 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) {
1717
18pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));18pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
1919
20pub const AI_PASSIVE = 0x01;
21pub const AI_CANONNAME = 0x02;
22pub const AI_NUMERICHOST = 0x04;
23pub const AI_V4MAPPED = 0x08;
24pub const AI_ALL = 0x10;
25pub const AI_ADDRCONFIG = 0x20;
26pub const AI_NUMERICSERV = 0x400;
27
28pub const NI_NUMERICHOST = 0x01;
29pub const NI_NUMERICSERV = 0x02;
30pub const NI_NOFQDN = 0x04;
31pub const NI_NAMEREQD = 0x08;
32pub const NI_DGRAM = 0x10;
33pub const NI_NUMERICSCOPE = 0x100;
34
35pub const EAI_BADFLAGS = -1;
36pub const EAI_NONAME = -2;
37pub const EAI_AGAIN = -3;
38pub const EAI_FAIL = -4;
39pub const EAI_FAMILY = -6;
40pub const EAI_SOCKTYPE = -7;
41pub const EAI_SERVICE = -8;
42pub const EAI_MEMORY = -10;
43pub const EAI_SYSTEM = -11;
44pub const EAI_OVERFLOW = -12;
45
46pub const EAI_NODATA = -5;
47pub const EAI_ADDRFAMILY = -9;
48pub const EAI_INPROGRESS = -100;
49pub const EAI_CANCELED = -101;
50pub const EAI_NOTCANCELED = -102;
51pub const EAI_ALLDONE = -103;
52pub const EAI_INTR = -104;
53pub const EAI_IDN_ENCODE = -105;
54
20pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;55pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
21pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;56pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;
22pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;57pub 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;
7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;7pub const RwLocked = @import("event/rwlocked.zig").RwLocked;
8pub const Loop = @import("event/loop.zig").Loop;8pub const Loop = @import("event/loop.zig").Loop;
9pub const fs = @import("event/fs.zig");9pub const fs = @import("event/fs.zig");
10pub const net = @import("event/net.zig");
1110
12test "import event tests" {11test "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;
4const testing = std.testing;4const testing = std.testing;
5const Loop = std.event.Loop;5const Loop = std.event.Loop;
66
7/// many producer, many consumer, thread-safe, runtime configurable buffer size7/// Many producer, many consumer, thread-safe, runtime configurable buffer size.
8/// when buffer is empty, consumers suspend and are resumed by producers8/// When buffer is empty, consumers suspend and are resumed by producers.
9/// when buffer is full, producers suspend and are resumed by consumers9/// 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.
10pub fn Channel(comptime T: type) type {12pub 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 };
5052
51 /// call destroy when done53 /// 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 }
450450
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 }
464501
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 }
468513
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 fail689 // 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 }
792837
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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const testing = std.testing;
4const event = std.event;
5const mem = std.mem;
6const os = std.os;
7const Loop = std.event.Loop;
8const File = std.fs.File;
9const fd_t = os.fd_t;
10
11pub 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
105pub 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
128pub 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.
141pub 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
150pub const WriteError = error{};
151
152pub 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
161pub 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.
192pub 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
219pub 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
233pub 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
247pub 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
260test "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
303async 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
316pub 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
338pub 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 following53/// 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 inserted59/// - *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 notation78/// - `d`: output numeric value in decimal notation
79/// - `b`: output integer value in binary notation79/// - `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 type83/// 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 {
704704
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;
64pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;64pub const SliceSeekableInStream = @import("io/seekable_stream.zig").SliceSeekableInStream;
65pub const COutStream = @import("io/c_out_stream.zig").COutStream;65pub const COutStream = @import("io/c_out_stream.zig").COutStream;
66pub const InStream = @import("io/in_stream.zig").InStream;66pub const InStream = @import("io/in_stream.zig").InStream;
6767pub const OutStream = @import("io/out_stream.zig").OutStream;
68pub 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}
12968
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`.
131pub fn writeFile(path: []const u8, data: []const u8) !void {70pub 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_InStream11 root.stack_size_std_io_InStream
12else12else
13 default_stack_size;13 default_stack_size;
14pub const stack_align = 16;
1514
16pub fn InStream(comptime ReadError: type) type {15pub 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 }
132131
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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const root = @import("root");
4const mem = std.mem;
5
6pub const default_stack_size = 1 * 1024 * 1024;
7pub const stack_size: usize = if (@hasDecl(root, "stack_size_std_io_OutStream"))
8 root.stack_size_std_io_OutStream
9else
10 default_stack_size;
11
12/// TODO this is not integrated with evented I/O yet.
13/// https://github.com/ziglang/zig/issues/3557
14pub 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 the99 /// memory is no longer needed, to avoid a resource leak. If the
100 /// `Allocator` implementation is unknown, then correct code will100 /// `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;
4const net = @This();4const net = @This();
5const mem = std.mem;5const mem = std.mem;
6const os = std.os;6const os = std.os;
7const fs = std.fs;
78
8pub const TmpWinAddr = struct {9test "" {
9 family: u8,10 _ = @import("net/test.zig");
10 data: [14]u8,11}
11};
1212
13pub const OsAddress = switch (builtin.os) {13pub 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,
1717
18pub 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);
2020
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 }
33172
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 }
47181
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 }
51212
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 }
55223
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};
71324
72pub fn parseIp4(buf: []const u8) !u32 {325pub 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);
75333
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;348pub 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.
363pub 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
373pub 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.
384pub 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 }
108481
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}
111491
112pub const Ip6Addr = struct {492const LookupAddr = struct {
113 scope_id: u32,493 addr: IpAddress,
114 addr: [16]u8,494 sortkey: i32 = 0,
115};495};
116496
117pub fn parseIp6(buf: []const u8) !Ip6Addr {497const DAS_USABLE = 0x40000000;
118 var result: Ip6Addr = undefined;498const DAS_MATCHINGSCOPE = 0x20000000;
119 result.scope_id = 0;499const DAS_MATCHINGLABEL = 0x10000000;
120 const ip_slice = result.addr[0..];500const DAS_PREC_SHIFT = 20;
501const DAS_SCOPE_SHIFT = 16;
502const DAS_PREFIX_SHIFT = 8;
503const DAS_ORDER_SHIFT = 0;
121504
122 var x: u16 = 0;505fn 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
616const Policy = struct {
617 addr: [16]u8,
618 len: u8,
619 mask: u8,
620 prec: u8,
621 label: u8,
622};
623
624const 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
676fn 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
685fn 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
693fn 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
706fn labelOf(a: [16]u8) u8 {
707 return policyOf(a).label;
708}
709
710fn IN6_IS_ADDR_MULTICAST(a: [16]u8) bool {
711 return a[0] == 0xff;
712}
713
714fn IN6_IS_ADDR_LINKLOCAL(a: [16]u8) bool {
715 return a[0] == 0xfe and (a[1] & 0xc0) == 0x80;
716}
717
718fn 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
725fn 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.
730fn addrCmpLessThan(b: LookupAddr, a: LookupAddr) bool {
731 return a.sortkey < b.sortkey;
732}
733
734fn 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
765fn 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}
176821
177 if (!saw_any_digits) {822pub 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}
180833
181 if (scope_id) {834fn 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 }
184851
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 }
190878
191 return error.Incomplete;879 canon.shrink(canon_name.len);
880 return linuxLookupNameFromDns(addrs, canon, name, family, rc, port);
192}881}
193882
194test "std.net.parseIp4" {883const 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};
196888
197 testParseIp4Fail("256.0.0.1", error.Overflow);889fn 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}
203938
204fn testParseIp4Fail(buf: []const u8, expected_err: anyerror) void {939const 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
955fn 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}
2111018
212test "std.net.parseIp6" {1019fn 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}
2181027
219pub fn connectUnixSocket(path: []const u8) !std.fs.File {1028fn 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 }
2271054
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);
2341073
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}
2451174
246 return std.fs.File.openHandle(sockfd);1175fn 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
1208fn 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
1239pub 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 @@
1const std = @import("../std.zig");
2const net = std.net;
3const mem = std.mem;
4const testing = std.testing;
5
6test "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
13test "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
34test "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
52test "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
76fn 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
86fn 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}
328328
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.
332pub 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.
331pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {360pub 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 seekable388 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 condition395 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 condition418 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;
399442
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.
402pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {448pub 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}
450503
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, see505/// 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.
454pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {507pub 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 @intCast509 // 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 {
476534
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`.
481pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {537pub 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;
15121578
1513pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!i32 {1579pub 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;
15621629
1563/// addr is `*const T` where T is one of the sockaddr1630/// addr is `*const T` where T is one of the sockaddr
1564pub fn bind(fd: i32, addr: *const sockaddr) BindError!void {1631pub 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 returned1637 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,
16241691
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,
16321693
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{
1644pub fn accept4(1705pub fn accept4(
1645 /// This argument is a socket that has been created with `socket`, bound to a local address1706 /// 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 the1709 /// 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 the1710 /// 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 the1711 /// 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 the1726 /// * `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,
16741735
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,
16931754
...@@ -1809,11 +1870,9 @@ pub const GetSockNameError = error{...@@ -1809,11 +1870,9 @@ pub const GetSockNameError = error{
1809 SystemResources,1870 SystemResources,
1810} || UnexpectedError;1871} || UnexpectedError;
18111872
1812pub fn getsockname(sockfd: i32) GetSockNameError!sockaddr {1873pub 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),
18181877
1819 EBADF => unreachable, // always a race condition1878 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. Note1915 /// 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;
18601923
1861/// Initiate a connection on a socket.1924/// Initiate a connection on a socket.
1862/// This is for blocking file descriptors only.1925pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
1863/// For non-blocking, see `connect_async`.
1864pub 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`.
1892pub 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 {
28352871
2836 @compileError("TODO implement gethostname for this OS");2872 @compileError("TODO implement gethostname for this OS");
2837}2873}
2874
2875pub 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
2923pub 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.
2988pub 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.
3048pub 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
3057pub const PollError = error{
3058 /// The kernel had no space to allocate file descriptor tables.
3059 SystemResources,
3060} || UnexpectedError;
3061
3062pub 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
3076pub 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
3089pub 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
3119pub const DnExpandError = error{InvalidDnsPacket};
3120
3121pub 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;
8pub const in_port_t = u16;8pub const in_port_t = u16;
9pub const sa_family_t = u8;9pub const sa_family_t = u8;
10pub const socklen_t = u32;10pub const socklen_t = u32;
11pub const sockaddr = extern union {11pub const sockaddr = extern struct {
12 in: sockaddr_in,
13 in6: sockaddr_in6,
14};
15pub 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};
16pub 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};
22pub const sockaddr_in6 = extern struct {23pub 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};
3031
32/// UNIX domain socket
33pub const sockaddr_un = extern struct {
34 len: u8 = @sizeOf(sockaddr_un),
35 family: sa_family_t = AF_UNIX,
36 path: [104]u8,
37};
38
31pub const timeval = extern struct {39pub 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;
11961204
1197/// Path refers to directory1205/// Path refers to directory
1198pub const AT_REMOVEDIR = 0x0080;1206pub const AT_REMOVEDIR = 0x0080;
1207
1208pub 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 {
141pub const in_port_t = u16;141pub const in_port_t = u16;
142pub const sa_family_t = u16;142pub const sa_family_t = u16;
143143
144pub const sockaddr = extern union {144pub 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};
148154
149pub const sockaddr_in = extern struct {155pub 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};
156162
157pub const sockaddr_in6 = extern struct {163pub 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};
165171
172pub const sockaddr_un = extern struct {
173 len: u8 = @sizeOf(sockaddr_un),
174 family: sa_family_t = AF_UNIX,
175 path: [104]u8,
176};
177
166pub const CTL_KERN = 1;178pub const CTL_KERN = 1;
167pub const CTL_DEBUG = 5;179pub const CTL_DEBUG = 5;
168180
...@@ -336,43 +348,6 @@ pub const SOCK_SEQPACKET = 5;...@@ -336,43 +348,6 @@ pub const SOCK_SEQPACKET = 5;
336pub const SOCK_CLOEXEC = 0x10000000;348pub const SOCK_CLOEXEC = 0x10000000;
337pub const SOCK_NONBLOCK = 0x20000000;349pub const SOCK_NONBLOCK = 0x20000000;
338350
339pub const PROTO_ip = 0o000;
340pub const PROTO_icmp = 0o001;
341pub const PROTO_igmp = 0o002;
342pub const PROTO_ggp = 0o003;
343pub const PROTO_ipencap = 0o004;
344pub const PROTO_st = 0o005;
345pub const PROTO_tcp = 0o006;
346pub const PROTO_egp = 0o010;
347pub const PROTO_pup = 0o014;
348pub const PROTO_udp = 0o021;
349pub const PROTO_hmp = 0o024;
350pub const PROTO_xns_idp = 0o026;
351pub const PROTO_rdp = 0o033;
352pub const PROTO_iso_tp4 = 0o035;
353pub const PROTO_xtp = 0o044;
354pub const PROTO_ddp = 0o045;
355pub const PROTO_idpr_cmtp = 0o046;
356pub const PROTO_ipv6 = 0o051;
357pub const PROTO_ipv6_route = 0o053;
358pub const PROTO_ipv6_frag = 0o054;
359pub const PROTO_idrp = 0o055;
360pub const PROTO_rsvp = 0o056;
361pub const PROTO_gre = 0o057;
362pub const PROTO_esp = 0o062;
363pub const PROTO_ah = 0o063;
364pub const PROTO_skip = 0o071;
365pub const PROTO_ipv6_icmp = 0o072;
366pub const PROTO_ipv6_nonxt = 0o073;
367pub const PROTO_ipv6_opts = 0o074;
368pub const PROTO_rspf = 0o111;
369pub const PROTO_vmtp = 0o121;
370pub const PROTO_ospf = 0o131;
371pub const PROTO_ipip = 0o136;
372pub const PROTO_encap = 0o142;
373pub const PROTO_pim = 0o147;
374pub const PROTO_raw = 0o377;
375
376pub const PF_UNSPEC = 0;351pub const PF_UNSPEC = 0;
377pub const PF_LOCAL = 1;352pub const PF_LOCAL = 1;
378pub const PF_UNIX = PF_LOCAL;353pub const PF_UNIX = PF_LOCAL;
...@@ -963,3 +938,351 @@ pub const AT_REMOVEDIR = 0x0800;...@@ -963,3 +938,351 @@ pub const AT_REMOVEDIR = 0x0800;
963938
964/// Fail if not under dirfd939/// Fail if not under dirfd
965pub const AT_BENEATH = 0x1000;940pub const AT_BENEATH = 0x1000;
941
942/// dummy for IP
943pub const IPPROTO_IP = 0;
944
945/// control message protocol
946pub const IPPROTO_ICMP = 1;
947
948/// tcp
949pub const IPPROTO_TCP = 6;
950
951/// user datagram protocol
952pub const IPPROTO_UDP = 17;
953
954/// IP6 header
955pub const IPPROTO_IPV6 = 41;
956
957/// raw IP packet
958pub const IPPROTO_RAW = 255;
959
960/// IP6 hop-by-hop options
961pub const IPPROTO_HOPOPTS = 0;
962
963/// group mgmt protocol
964pub const IPPROTO_IGMP = 2;
965
966/// gateway^2 (deprecated)
967pub const IPPROTO_GGP = 3;
968
969/// IPv4 encapsulation
970pub const IPPROTO_IPV4 = 4;
971
972/// for compatibility
973pub const IPPROTO_IPIP = IPPROTO_IPV4;
974
975/// Stream protocol II
976pub const IPPROTO_ST = 7;
977
978/// exterior gateway protocol
979pub const IPPROTO_EGP = 8;
980
981/// private interior gateway
982pub const IPPROTO_PIGP = 9;
983
984/// BBN RCC Monitoring
985pub const IPPROTO_RCCMON = 10;
986
987/// network voice protocol
988pub const IPPROTO_NVPII = 11;
989
990/// pup
991pub const IPPROTO_PUP = 12;
992
993/// Argus
994pub const IPPROTO_ARGUS = 13;
995
996/// EMCON
997pub const IPPROTO_EMCON = 14;
998
999/// Cross Net Debugger
1000pub const IPPROTO_XNET = 15;
1001
1002/// Chaos
1003pub const IPPROTO_CHAOS = 16;
1004
1005/// Multiplexing
1006pub const IPPROTO_MUX = 18;
1007
1008/// DCN Measurement Subsystems
1009pub const IPPROTO_MEAS = 19;
1010
1011/// Host Monitoring
1012pub const IPPROTO_HMP = 20;
1013
1014/// Packet Radio Measurement
1015pub const IPPROTO_PRM = 21;
1016
1017/// xns idp
1018pub const IPPROTO_IDP = 22;
1019
1020/// Trunk-1
1021pub const IPPROTO_TRUNK1 = 23;
1022
1023/// Trunk-2
1024pub const IPPROTO_TRUNK2 = 24;
1025
1026/// Leaf-1
1027pub const IPPROTO_LEAF1 = 25;
1028
1029/// Leaf-2
1030pub const IPPROTO_LEAF2 = 26;
1031
1032/// Reliable Data
1033pub const IPPROTO_RDP = 27;
1034
1035/// Reliable Transaction
1036pub const IPPROTO_IRTP = 28;
1037
1038/// tp-4 w/ class negotiation
1039pub const IPPROTO_TP = 29;
1040
1041/// Bulk Data Transfer
1042pub const IPPROTO_BLT = 30;
1043
1044/// Network Services
1045pub const IPPROTO_NSP = 31;
1046
1047/// Merit Internodal
1048pub const IPPROTO_INP = 32;
1049
1050/// Datagram Congestion Control Protocol
1051pub const IPPROTO_DCCP = 33;
1052
1053/// Third Party Connect
1054pub const IPPROTO_3PC = 34;
1055
1056/// InterDomain Policy Routing
1057pub const IPPROTO_IDPR = 35;
1058
1059/// XTP
1060pub const IPPROTO_XTP = 36;
1061
1062/// Datagram Delivery
1063pub const IPPROTO_DDP = 37;
1064
1065/// Control Message Transport
1066pub const IPPROTO_CMTP = 38;
1067
1068/// TP++ Transport
1069pub const IPPROTO_TPXX = 39;
1070
1071/// IL transport protocol
1072pub const IPPROTO_IL = 40;
1073
1074/// Source Demand Routing
1075pub const IPPROTO_SDRP = 42;
1076
1077/// IP6 routing header
1078pub const IPPROTO_ROUTING = 43;
1079
1080/// IP6 fragmentation header
1081pub const IPPROTO_FRAGMENT = 44;
1082
1083/// InterDomain Routing
1084pub const IPPROTO_IDRP = 45;
1085
1086/// resource reservation
1087pub const IPPROTO_RSVP = 46;
1088
1089/// General Routing Encap.
1090pub const IPPROTO_GRE = 47;
1091
1092/// Mobile Host Routing
1093pub const IPPROTO_MHRP = 48;
1094
1095/// BHA
1096pub const IPPROTO_BHA = 49;
1097
1098/// IP6 Encap Sec. Payload
1099pub const IPPROTO_ESP = 50;
1100
1101/// IP6 Auth Header
1102pub const IPPROTO_AH = 51;
1103
1104/// Integ. Net Layer Security
1105pub const IPPROTO_INLSP = 52;
1106
1107/// IP with encryption
1108pub const IPPROTO_SWIPE = 53;
1109
1110/// Next Hop Resolution
1111pub const IPPROTO_NHRP = 54;
1112
1113/// IP Mobility
1114pub const IPPROTO_MOBILE = 55;
1115
1116/// Transport Layer Security
1117pub const IPPROTO_TLSP = 56;
1118
1119/// SKIP
1120pub const IPPROTO_SKIP = 57;
1121
1122/// ICMP6
1123pub const IPPROTO_ICMPV6 = 58;
1124
1125/// IP6 no next header
1126pub const IPPROTO_NONE = 59;
1127
1128/// IP6 destination option
1129pub const IPPROTO_DSTOPTS = 60;
1130
1131/// any host internal protocol
1132pub const IPPROTO_AHIP = 61;
1133
1134/// CFTP
1135pub const IPPROTO_CFTP = 62;
1136
1137/// "hello" routing protocol
1138pub const IPPROTO_HELLO = 63;
1139
1140/// SATNET/Backroom EXPAK
1141pub const IPPROTO_SATEXPAK = 64;
1142
1143/// Kryptolan
1144pub const IPPROTO_KRYPTOLAN = 65;
1145
1146/// Remote Virtual Disk
1147pub const IPPROTO_RVD = 66;
1148
1149/// Pluribus Packet Core
1150pub const IPPROTO_IPPC = 67;
1151
1152/// Any distributed FS
1153pub const IPPROTO_ADFS = 68;
1154
1155/// Satnet Monitoring
1156pub const IPPROTO_SATMON = 69;
1157
1158/// VISA Protocol
1159pub const IPPROTO_VISA = 70;
1160
1161/// Packet Core Utility
1162pub const IPPROTO_IPCV = 71;
1163
1164/// Comp. Prot. Net. Executive
1165pub const IPPROTO_CPNX = 72;
1166
1167/// Comp. Prot. HeartBeat
1168pub const IPPROTO_CPHB = 73;
1169
1170/// Wang Span Network
1171pub const IPPROTO_WSN = 74;
1172
1173/// Packet Video Protocol
1174pub const IPPROTO_PVP = 75;
1175
1176/// BackRoom SATNET Monitoring
1177pub const IPPROTO_BRSATMON = 76;
1178
1179/// Sun net disk proto (temp.)
1180pub const IPPROTO_ND = 77;
1181
1182/// WIDEBAND Monitoring
1183pub const IPPROTO_WBMON = 78;
1184
1185/// WIDEBAND EXPAK
1186pub const IPPROTO_WBEXPAK = 79;
1187
1188/// ISO cnlp
1189pub const IPPROTO_EON = 80;
1190
1191/// VMTP
1192pub const IPPROTO_VMTP = 81;
1193
1194/// Secure VMTP
1195pub const IPPROTO_SVMTP = 82;
1196
1197/// Banyon VINES
1198pub const IPPROTO_VINES = 83;
1199
1200/// TTP
1201pub const IPPROTO_TTP = 84;
1202
1203/// NSFNET-IGP
1204pub const IPPROTO_IGP = 85;
1205
1206/// dissimilar gateway prot.
1207pub const IPPROTO_DGP = 86;
1208
1209/// TCF
1210pub const IPPROTO_TCF = 87;
1211
1212/// Cisco/GXS IGRP
1213pub const IPPROTO_IGRP = 88;
1214
1215/// OSPFIGP
1216pub const IPPROTO_OSPFIGP = 89;
1217
1218/// Strite RPC protocol
1219pub const IPPROTO_SRPC = 90;
1220
1221/// Locus Address Resoloution
1222pub const IPPROTO_LARP = 91;
1223
1224/// Multicast Transport
1225pub const IPPROTO_MTP = 92;
1226
1227/// AX.25 Frames
1228pub const IPPROTO_AX25 = 93;
1229
1230/// IP encapsulated in IP
1231pub const IPPROTO_IPEIP = 94;
1232
1233/// Mobile Int.ing control
1234pub const IPPROTO_MICP = 95;
1235
1236/// Semaphore Comm. security
1237pub const IPPROTO_SCCSP = 96;
1238
1239/// Ethernet IP encapsulation
1240pub const IPPROTO_ETHERIP = 97;
1241
1242/// encapsulation header
1243pub const IPPROTO_ENCAP = 98;
1244
1245/// any private encr. scheme
1246pub const IPPROTO_APES = 99;
1247
1248/// GMTP
1249pub const IPPROTO_GMTP = 100;
1250
1251/// payload compression (IPComp)
1252pub const IPPROTO_IPCOMP = 108;
1253
1254/// SCTP
1255pub const IPPROTO_SCTP = 132;
1256
1257/// IPv6 Mobility Header
1258pub const IPPROTO_MH = 135;
1259
1260/// UDP-Lite
1261pub const IPPROTO_UDPLITE = 136;
1262
1263/// IP6 Host Identity Protocol
1264pub const IPPROTO_HIP = 139;
1265
1266/// IP6 Shim6 Protocol
1267pub const IPPROTO_SHIM6 = 140;
1268
1269/// Protocol Independent Mcast
1270pub const IPPROTO_PIM = 103;
1271
1272/// CARP
1273pub const IPPROTO_CARP = 112;
1274
1275/// PGM
1276pub const IPPROTO_PGM = 113;
1277
1278/// MPLS-in-IP
1279pub const IPPROTO_MPLS = 137;
1280
1281/// PFSYNC
1282pub const IPPROTO_PFSYNC = 240;
1283
1284/// Reserved
1285pub const IPPROTO_RESERVED_253 = 253;
1286
1287/// Reserved
1288pub 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;
227pub const SEEK_CUR = 1;227pub const SEEK_CUR = 1;
228pub const SEEK_END = 2;228pub const SEEK_END = 2;
229229
230pub const PROTO_ip = 0o000;
231pub const PROTO_icmp = 0o001;
232pub const PROTO_igmp = 0o002;
233pub const PROTO_ggp = 0o003;
234pub const PROTO_ipencap = 0o004;
235pub const PROTO_st = 0o005;
236pub const PROTO_tcp = 0o006;
237pub const PROTO_egp = 0o010;
238pub const PROTO_pup = 0o014;
239pub const PROTO_udp = 0o021;
240pub const PROTO_hmp = 0o024;
241pub const PROTO_xns_idp = 0o026;
242pub const PROTO_rdp = 0o033;
243pub const PROTO_iso_tp4 = 0o035;
244pub const PROTO_xtp = 0o044;
245pub const PROTO_ddp = 0o045;
246pub const PROTO_idpr_cmtp = 0o046;
247pub const PROTO_ipv6 = 0o051;
248pub const PROTO_ipv6_route = 0o053;
249pub const PROTO_ipv6_frag = 0o054;
250pub const PROTO_idrp = 0o055;
251pub const PROTO_rsvp = 0o056;
252pub const PROTO_gre = 0o057;
253pub const PROTO_esp = 0o062;
254pub const PROTO_ah = 0o063;
255pub const PROTO_skip = 0o071;
256pub const PROTO_ipv6_icmp = 0o072;
257pub const PROTO_ipv6_nonxt = 0o073;
258pub const PROTO_ipv6_opts = 0o074;
259pub const PROTO_rspf = 0o111;
260pub const PROTO_vmtp = 0o121;
261pub const PROTO_ospf = 0o131;
262pub const PROTO_ipip = 0o136;
263pub const PROTO_encap = 0o142;
264pub const PROTO_pim = 0o147;
265pub const PROTO_raw = 0o377;
266
267pub const SHUT_RD = 0;230pub const SHUT_RD = 0;
268pub const SHUT_WR = 1;231pub const SHUT_WR = 1;
269pub const SHUT_RDWR = 2;232pub const SHUT_RDWR = 2;
...@@ -846,30 +809,31 @@ pub const in_port_t = u16;...@@ -846,30 +809,31 @@ pub const in_port_t = u16;
846pub const sa_family_t = u16;809pub const sa_family_t = u16;
847pub const socklen_t = u32;810pub const socklen_t = u32;
848811
849/// This intentionally only has ip4 and ip6812pub const sockaddr = extern struct {
850pub 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};
855816
817/// IPv4 socket address
856pub const sockaddr_in = extern struct {818pub 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};
862824
825/// IPv6 socket address
863pub const sockaddr_in6 = extern struct {826pub 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};
870833
834/// UNIX domain socket address
871pub const sockaddr_un = extern struct {835pub 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};
875839
...@@ -1388,3 +1352,70 @@ pub const Statx = extern struct {...@@ -1388,3 +1352,70 @@ pub const Statx = extern struct {
13881352
1389 __pad2: [14]u64,1353 __pad2: [14]u64,
1390};1354};
1355
1356pub 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
1367pub const IPPORT_RESERVED = 1024;
1368
1369pub const IPPROTO_IP = 0;
1370pub const IPPROTO_HOPOPTS = 0;
1371pub const IPPROTO_ICMP = 1;
1372pub const IPPROTO_IGMP = 2;
1373pub const IPPROTO_IPIP = 4;
1374pub const IPPROTO_TCP = 6;
1375pub const IPPROTO_EGP = 8;
1376pub const IPPROTO_PUP = 12;
1377pub const IPPROTO_UDP = 17;
1378pub const IPPROTO_IDP = 22;
1379pub const IPPROTO_TP = 29;
1380pub const IPPROTO_DCCP = 33;
1381pub const IPPROTO_IPV6 = 41;
1382pub const IPPROTO_ROUTING = 43;
1383pub const IPPROTO_FRAGMENT = 44;
1384pub const IPPROTO_RSVP = 46;
1385pub const IPPROTO_GRE = 47;
1386pub const IPPROTO_ESP = 50;
1387pub const IPPROTO_AH = 51;
1388pub const IPPROTO_ICMPV6 = 58;
1389pub const IPPROTO_NONE = 59;
1390pub const IPPROTO_DSTOPTS = 60;
1391pub const IPPROTO_MTP = 92;
1392pub const IPPROTO_BEETPH = 94;
1393pub const IPPROTO_ENCAP = 98;
1394pub const IPPROTO_PIM = 103;
1395pub const IPPROTO_COMP = 108;
1396pub const IPPROTO_SCTP = 132;
1397pub const IPPROTO_MH = 135;
1398pub const IPPROTO_UDPLITE = 136;
1399pub const IPPROTO_MPLS = 137;
1400pub const IPPROTO_RAW = 255;
1401pub const IPPROTO_MAX = 256;
1402
1403pub const RR_A = 1;
1404pub const RR_CNAME = 5;
1405pub const RR_AAAA = 28;
1406
1407pub const nfds_t = usize;
1408pub const pollfd = extern struct {
1409 fd: fd_t,
1410 events: i16,
1411 revents: i16,
1412};
1413
1414pub const POLLIN = 0x001;
1415pub const POLLPRI = 0x002;
1416pub const POLLOUT = 0x004;
1417pub const POLLERR = 0x008;
1418pub const POLLHUP = 0x010;
1419pub const POLLNVAL = 0x020;
1420pub const POLLRDNORM = 0x040;
1421pub 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 {
137pub const in_port_t = u16;137pub const in_port_t = u16;
138pub const sa_family_t = u8;138pub const sa_family_t = u8;
139139
140pub const sockaddr = extern union {140pub 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};
144150
145pub const sockaddr_in = extern struct {151pub 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};
152158
153pub const sockaddr_in6 = extern struct {159pub 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};
161167
168/// Definitions for UNIX IPC domain.
169pub 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
162pub const CTL_KERN = 1;180pub const CTL_KERN = 1;
163pub const CTL_DEBUG = 5;181pub const CTL_DEBUG = 5;
164182
...@@ -316,31 +334,6 @@ pub const SOCK_SEQPACKET = 5;...@@ -316,31 +334,6 @@ pub const SOCK_SEQPACKET = 5;
316pub const SOCK_CLOEXEC = 0x10000000;334pub const SOCK_CLOEXEC = 0x10000000;
317pub const SOCK_NONBLOCK = 0x20000000;335pub const SOCK_NONBLOCK = 0x20000000;
318336
319pub const PROTO_ip = 0;
320pub const PROTO_icmp = 1;
321pub const PROTO_igmp = 2;
322pub const PROTO_ggp = 3;
323pub const PROTO_ipencap = 4;
324pub const PROTO_tcp = 6;
325pub const PROTO_egp = 8;
326pub const PROTO_pup = 12;
327pub const PROTO_udp = 17;
328pub const PROTO_xns_idp = 22;
329pub const PROTO_iso_tp4 = 29;
330pub const PROTO_ipv6 = 41;
331pub const PROTO_ipv6_route = 43;
332pub const PROTO_ipv6_frag = 44;
333pub const PROTO_rsvp = 46;
334pub const PROTO_gre = 47;
335pub const PROTO_esp = 50;
336pub const PROTO_ah = 51;
337pub const PROTO_ipv6_icmp = 58;
338pub const PROTO_ipv6_nonxt = 59;
339pub const PROTO_ipv6_opts = 60;
340pub const PROTO_encap = 98;
341pub const PROTO_pim = 103;
342pub const PROTO_raw = 255;
343
344pub const PF_UNSPEC = 0;337pub const PF_UNSPEC = 0;
345pub const PF_LOCAL = 1;338pub const PF_LOCAL = 1;
346pub const PF_UNIX = PF_LOCAL;339pub 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}
828821
829pub const HOST_NAME_MAX = 255;822pub const HOST_NAME_MAX = 255;
823
824/// dummy for IP
825pub const IPPROTO_IP = 0;
826
827/// IP6 hop-by-hop options
828pub const IPPROTO_HOPOPTS = 0;
829
830/// control message protocol
831pub const IPPROTO_ICMP = 1;
832
833/// group mgmt protocol
834pub const IPPROTO_IGMP = 2;
835
836/// gateway^2 (deprecated)
837pub const IPPROTO_GGP = 3;
838
839/// IP header
840pub const IPPROTO_IPV4 = 4;
841
842/// IP inside IP
843pub const IPPROTO_IPIP = 4;
844
845/// tcp
846pub const IPPROTO_TCP = 6;
847
848/// exterior gateway protocol
849pub const IPPROTO_EGP = 8;
850
851/// pup
852pub const IPPROTO_PUP = 12;
853
854/// user datagram protocol
855pub const IPPROTO_UDP = 17;
856
857/// xns idp
858pub const IPPROTO_IDP = 22;
859
860/// tp-4 w/ class negotiation
861pub const IPPROTO_TP = 29;
862
863/// DCCP
864pub const IPPROTO_DCCP = 33;
865
866/// IP6 header
867pub const IPPROTO_IPV6 = 41;
868
869/// IP6 routing header
870pub const IPPROTO_ROUTING = 43;
871
872/// IP6 fragmentation header
873pub const IPPROTO_FRAGMENT = 44;
874
875/// resource reservation
876pub const IPPROTO_RSVP = 46;
877
878/// GRE encaps RFC 1701
879pub const IPPROTO_GRE = 47;
880
881/// encap. security payload
882pub const IPPROTO_ESP = 50;
883
884/// authentication header
885pub const IPPROTO_AH = 51;
886
887/// IP Mobility RFC 2004
888pub const IPPROTO_MOBILE = 55;
889
890/// IPv6 ICMP
891pub const IPPROTO_IPV6_ICMP = 58;
892
893/// ICMP6
894pub const IPPROTO_ICMPV6 = 58;
895
896/// IP6 no next header
897pub const IPPROTO_NONE = 59;
898
899/// IP6 destination option
900pub const IPPROTO_DSTOPTS = 60;
901
902/// ISO cnlp
903pub const IPPROTO_EON = 80;
904
905/// Ethernet-in-IP
906pub const IPPROTO_ETHERIP = 97;
907
908/// encapsulation header
909pub const IPPROTO_ENCAP = 98;
910
911/// Protocol indep. multicast
912pub const IPPROTO_PIM = 103;
913
914/// IP Payload Comp. Protocol
915pub const IPPROTO_IPCOMP = 108;
916
917/// VRRP RFC 2338
918pub const IPPROTO_VRRP = 112;
919
920/// Common Address Resolution Protocol
921pub const IPPROTO_CARP = 112;
922
923/// L2TPv3
924pub const IPPROTO_L2TP = 115;
925
926/// SCTP
927pub const IPPROTO_SCTP = 132;
928
929/// PFSYNC
930pub const IPPROTO_PFSYNC = 240;
931
932/// raw IP packet
933pub 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;
161161
162/// Remove directory instead of unlinking file162/// Remove directory instead of unlinking file
163pub const AT_REMOVEDIR = 0x200;163pub const AT_REMOVEDIR = 0x200;
164
165pub const in_port_t = u16;
166pub const sa_family_t = u16;
167pub const socklen_t = u32;
168
169pub const sockaddr = extern struct {
170 family: sa_family_t,
171 data: [14]u8,
172};
173pub 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};
179pub 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};
186pub const in6_addr = [16]u8;
187pub const in_addr = u32;
188
189pub const AF_UNSPEC = 0;
190pub const AF_UNIX = 1;
191pub const AF_INET = 2;
192pub const AF_IMPLINK = 3;
193pub const AF_PUP = 4;
194pub const AF_CHAOS = 5;
195pub const AF_NS = 6;
196pub const AF_IPX = AF_NS;
197pub const AF_ISO = 7;
198pub const AF_OSI = AF_ISO;
199pub const AF_ECMA = 8;
200pub const AF_DATAKIT = 9;
201pub const AF_CCITT = 10;
202pub const AF_SNA = 11;
203pub const AF_DECnet = 12;
204pub const AF_DLI = 13;
205pub const AF_LAT = 14;
206pub const AF_HYLINK = 15;
207pub const AF_APPLETALK = 16;
208pub const AF_NETBIOS = 17;
209pub const AF_VOICEVIEW = 18;
210pub const AF_FIREFOX = 19;
211pub const AF_UNKNOWN1 = 20;
212pub const AF_BAN = 21;
213pub const AF_ATM = 22;
214pub const AF_INET6 = 23;
215pub const AF_CLUSTER = 24;
216pub const AF_12844 = 25;
217pub const AF_IRDA = 26;
218pub const AF_NETDES = 28;
219pub const AF_TCNPROCESS = 29;
220pub const AF_TCNMESSAGE = 30;
221pub const AF_ICLFXBM = 31;
222pub const AF_BTH = 32;
223pub 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}
228228
229pub 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
229pub fn read(fd: i32, buf: [*]u8, count: usize) usize {251pub 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 };
207207
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
128export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {128export 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);
153153
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);
156156 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 later159 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 yet162 .arena_allocator = tree_arena,
163 .errors = undefined, // can't reference the allocator yet163 .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 allocator168 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);
168172
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 }
61316138
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) {
75387545
7539uint32_t get_host_int_bytes(CodeGen *g, ZigType *struct_type, TypeStructField *field) {7546uint32_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;
1787417878
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;
1792017927
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;
1792417931
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 );
169169
170 cases.add(170 cases.add(