authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-11 15:35:15-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-02 16:57:15-07:00
logcd0d514643404103a83881fc4d7c46674ed9f991
tree9fd84f8cb8cad2278680b72e4043ac10084d3d12
parentebcfc86bb9c8cec1a66511858a6443b1927191f2

remove the experimental std.x namespace

Playtime is over. I'm working on networking now.

22 files changed, 143 insertions(+), 3448 deletions(-)

lib/std/c.zig+2-2
......@@ -206,7 +206,7 @@ pub extern "c" fn sendto(
206206 dest_addr: ?*const c.sockaddr,
207207 addrlen: c.socklen_t,
208208) isize;
209pub extern "c" fn sendmsg(sockfd: c.fd_t, msg: *const std.x.os.Socket.Message, flags: c_int) isize;
209pub extern "c" fn sendmsg(sockfd: c.fd_t, msg: *const c.msghdr_const, flags: u32) isize;
210210
211211pub extern "c" fn recv(sockfd: c.fd_t, arg1: ?*anyopaque, arg2: usize, arg3: c_int) isize;
212212pub extern "c" fn recvfrom(
......@@ -217,7 +217,7 @@ pub extern "c" fn recvfrom(
217217 noalias src_addr: ?*c.sockaddr,
218218 noalias addrlen: ?*c.socklen_t,
219219) isize;
220pub extern "c" fn recvmsg(sockfd: c.fd_t, msg: *std.x.os.Socket.Message, flags: c_int) isize;
220pub extern "c" fn recvmsg(sockfd: c.fd_t, msg: *c.msghdr, flags: u32) isize;
221221
222222pub extern "c" fn kill(pid: c.pid_t, sig: c_int) c_int;
223223pub extern "c" fn getdirentries(fd: c.fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
lib/std/c/darwin.zig+10-1
......@@ -1007,7 +1007,16 @@ pub const sockaddr = extern struct {
10071007 data: [14]u8,
10081008
10091009 pub const SS_MAXSIZE = 128;
1010 pub const storage = std.x.os.Socket.Address.Native.Storage;
1010 pub const storage = extern struct {
1011 len: u8 align(8),
1012 family: sa_family_t,
1013 padding: [126]u8 = undefined,
1014
1015 comptime {
1016 assert(@sizeOf(storage) == SS_MAXSIZE);
1017 assert(@alignOf(storage) == 8);
1018 }
1019 };
10111020 pub const in = extern struct {
10121021 len: u8 = @sizeOf(in),
10131022 family: sa_family_t = AF.INET,
lib/std/c/dragonfly.zig+12-2
......@@ -1,5 +1,6 @@
11const builtin = @import("builtin");
22const std = @import("../std.zig");
3const assert = std.debug.assert;
34const maxInt = std.math.maxInt;
45const iovec = std.os.iovec;
56
......@@ -476,11 +477,20 @@ pub const CLOCK = struct {
476477
477478pub const sockaddr = extern struct {
478479 len: u8,
479 family: u8,
480 family: sa_family_t,
480481 data: [14]u8,
481482
482483 pub const SS_MAXSIZE = 128;
483 pub const storage = std.x.os.Socket.Address.Native.Storage;
484 pub const storage = extern struct {
485 len: u8 align(8),
486 family: sa_family_t,
487 padding: [126]u8 = undefined,
488
489 comptime {
490 assert(@sizeOf(storage) == SS_MAXSIZE);
491 assert(@alignOf(storage) == 8);
492 }
493 };
484494
485495 pub const in = extern struct {
486496 len: u8 = @sizeOf(in),
lib/std/c/freebsd.zig+11-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23const builtin = @import("builtin");
34const maxInt = std.math.maxInt;
45const iovec = std.os.iovec;
......@@ -401,7 +402,16 @@ pub const sockaddr = extern struct {
401402 data: [14]u8,
402403
403404 pub const SS_MAXSIZE = 128;
404 pub const storage = std.x.os.Socket.Address.Native.Storage;
405 pub const storage = extern struct {
406 len: u8 align(8),
407 family: sa_family_t,
408 padding: [126]u8 = undefined,
409
410 comptime {
411 assert(@sizeOf(storage) == SS_MAXSIZE);
412 assert(@alignOf(storage) == 8);
413 }
414 };
405415
406416 pub const in = extern struct {
407417 len: u8 = @sizeOf(in),
lib/std/c/haiku.zig+11-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23const builtin = @import("builtin");
34const maxInt = std.math.maxInt;
45const iovec = std.os.iovec;
......@@ -339,7 +340,16 @@ pub const sockaddr = extern struct {
339340 data: [14]u8,
340341
341342 pub const SS_MAXSIZE = 128;
342 pub const storage = std.x.os.Socket.Address.Native.Storage;
343 pub const storage = extern struct {
344 len: u8 align(8),
345 family: sa_family_t,
346 padding: [126]u8 = undefined,
347
348 comptime {
349 assert(@sizeOf(storage) == SS_MAXSIZE);
350 assert(@alignOf(storage) == 8);
351 }
352 };
343353
344354 pub const in = extern struct {
345355 len: u8 = @sizeOf(in),
lib/std/c/netbsd.zig+11-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23const builtin = @import("builtin");
34const maxInt = std.math.maxInt;
45const iovec = std.os.iovec;
......@@ -481,7 +482,16 @@ pub const sockaddr = extern struct {
481482 data: [14]u8,
482483
483484 pub const SS_MAXSIZE = 128;
484 pub const storage = std.x.os.Socket.Address.Native.Storage;
485 pub const storage = extern struct {
486 len: u8 align(8),
487 family: sa_family_t,
488 padding: [126]u8 = undefined,
489
490 comptime {
491 assert(@sizeOf(storage) == SS_MAXSIZE);
492 assert(@alignOf(storage) == 8);
493 }
494 };
485495
486496 pub const in = extern struct {
487497 len: u8 = @sizeOf(in),
lib/std/c/openbsd.zig+11-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23const maxInt = std.math.maxInt;
34const builtin = @import("builtin");
45const iovec = std.os.iovec;
......@@ -372,7 +373,16 @@ pub const sockaddr = extern struct {
372373 data: [14]u8,
373374
374375 pub const SS_MAXSIZE = 256;
375 pub const storage = std.x.os.Socket.Address.Native.Storage;
376 pub const storage = extern struct {
377 len: u8 align(8),
378 family: sa_family_t,
379 padding: [254]u8 = undefined,
380
381 comptime {
382 assert(@sizeOf(storage) == SS_MAXSIZE);
383 assert(@alignOf(storage) == 8);
384 }
385 };
376386
377387 pub const in = extern struct {
378388 len: u8 = @sizeOf(in),
lib/std/c/solaris.zig+10-1
......@@ -1,4 +1,5 @@
11const std = @import("../std.zig");
2const assert = std.debug.assert;
23const builtin = @import("builtin");
34const maxInt = std.math.maxInt;
45const iovec = std.os.iovec;
......@@ -435,7 +436,15 @@ pub const sockaddr = extern struct {
435436 data: [14]u8,
436437
437438 pub const SS_MAXSIZE = 256;
438 pub const storage = std.x.os.Socket.Address.Native.Storage;
439 pub const storage = extern struct {
440 family: sa_family_t align(8),
441 padding: [254]u8 = undefined,
442
443 comptime {
444 assert(@sizeOf(storage) == SS_MAXSIZE);
445 assert(@alignOf(storage) == 8);
446 }
447 };
439448
440449 pub const in = extern struct {
441450 family: sa_family_t = AF.INET,
lib/std/os.zig+2-2
......@@ -5616,11 +5616,11 @@ pub fn sendmsg(
56165616 /// The file descriptor of the sending socket.
56175617 sockfd: socket_t,
56185618 /// Message header and iovecs
5619 msg: msghdr_const,
5619 msg: *const msghdr_const,
56205620 flags: u32,
56215621) SendMsgError!usize {
56225622 while (true) {
5623 const rc = system.sendmsg(sockfd, @ptrCast(*const std.x.os.Socket.Message, &msg), @intCast(c_int, flags));
5623 const rc = system.sendmsg(sockfd, msg, flags);
56245624 if (builtin.os.tag == .windows) {
56255625 if (rc == windows.ws2_32.SOCKET_ERROR) {
56265626 switch (windows.ws2_32.WSAGetLastError()) {
lib/std/os/linux.zig+41-12
......@@ -1226,11 +1226,14 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal
12261226 return syscall5(.getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
12271227}
12281228
1229pub fn sendmsg(fd: i32, msg: *const std.x.os.Socket.Message, flags: c_int) usize {
1229pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize {
1230 const fd_usize = @bitCast(usize, @as(isize, fd));
1231 const msg_usize = @ptrToInt(msg);
12301232 if (native_arch == .x86) {
1231 return socketcall(SC.sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) });
1233 return socketcall(SC.sendmsg, &[3]usize{ fd_usize, msg_usize, flags });
1234 } else {
1235 return syscall3(.sendmsg, fd_usize, msg_usize, flags);
12321236 }
1233 return syscall3(.sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)));
12341237}
12351238
12361239pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
......@@ -1274,24 +1277,42 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
12741277}
12751278
12761279pub fn connect(fd: i32, addr: *const anyopaque, len: socklen_t) usize {
1280 const fd_usize = @bitCast(usize, @as(isize, fd));
1281 const addr_usize = @ptrToInt(addr);
12771282 if (native_arch == .x86) {
1278 return socketcall(SC.connect, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len });
1283 return socketcall(SC.connect, &[3]usize{ fd_usize, addr_usize, len });
1284 } else {
1285 return syscall3(.connect, fd_usize, addr_usize, len);
12791286 }
1280 return syscall3(.connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len);
12811287}
12821288
1283pub fn recvmsg(fd: i32, msg: *std.x.os.Socket.Message, flags: c_int) usize {
1289pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
1290 const fd_usize = @bitCast(usize, @as(isize, fd));
1291 const msg_usize = @ptrToInt(msg);
12841292 if (native_arch == .x86) {
1285 return socketcall(SC.recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)) });
1293 return socketcall(SC.recvmsg, &[3]usize{ fd_usize, msg_usize, flags });
1294 } else {
1295 return syscall3(.recvmsg, fd_usize, msg_usize, flags);
12861296 }
1287 return syscall3(.recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), @bitCast(usize, @as(isize, flags)));
12881297}
12891298
1290pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
1299pub fn recvfrom(
1300 fd: i32,
1301 noalias buf: [*]u8,
1302 len: usize,
1303 flags: u32,
1304 noalias addr: ?*sockaddr,
1305 noalias alen: ?*socklen_t,
1306) usize {
1307 const fd_usize = @bitCast(usize, @as(isize, fd));
1308 const buf_usize = @ptrToInt(buf);
1309 const addr_usize = @ptrToInt(addr);
1310 const alen_usize = @ptrToInt(alen);
12911311 if (native_arch == .x86) {
1292 return socketcall(SC.recvfrom, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen) });
1312 return socketcall(SC.recvfrom, &[6]usize{ fd_usize, buf_usize, len, flags, addr_usize, alen_usize });
1313 } else {
1314 return syscall6(.recvfrom, fd_usize, buf_usize, len, flags, addr_usize, alen_usize);
12931315 }
1294 return syscall6(.recvfrom, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
12951316}
12961317
12971318pub fn shutdown(fd: i32, how: i32) usize {
......@@ -3219,7 +3240,15 @@ pub const sockaddr = extern struct {
32193240 data: [14]u8,
32203241
32213242 pub const SS_MAXSIZE = 128;
3222 pub const storage = std.x.os.Socket.Address.Native.Storage;
3243 pub const storage = extern struct {
3244 family: sa_family_t align(8),
3245 padding: [SS_MAXSIZE - @sizeOf(sa_family_t)]u8 = undefined,
3246
3247 comptime {
3248 assert(@sizeOf(storage) == SS_MAXSIZE);
3249 assert(@alignOf(storage) == 8);
3250 }
3251 };
32233252
32243253 /// IPv4 socket address
32253254 pub const in = extern struct {
lib/std/os/linux/seccomp.zig+8-10
......@@ -6,16 +6,14 @@
66//! isn't that useful for general-purpose applications, and so a mode that
77//! utilizes user-supplied filters mode was added.
88//!
9//! Seccomp filters are classic BPF programs, which means that all the
10//! information under `std.x.net.bpf` applies here as well. Conceptually, a
11//! seccomp program is attached to the kernel and is executed on each syscall.
12//! The "packet" being validated is the `data` structure, and the verdict is an
13//! action that the kernel performs on the calling process. The actions are
14//! variations on a "pass" or "fail" result, where a pass allows the syscall to
15//! continue and a fail blocks the syscall and returns some sort of error value.
16//! See the full list of actions under ::RET for more information. Finally, only
17//! word-sized, absolute loads (`ld [k]`) are supported to read from the `data`
18//! structure.
9//! Seccomp filters are classic BPF programs. Conceptually, a seccomp program
10//! is attached to the kernel and is executed on each syscall. The "packet"
11//! being validated is the `data` structure, and the verdict is an action that
12//! the kernel performs on the calling process. The actions are variations on a
13//! "pass" or "fail" result, where a pass allows the syscall to continue and a
14//! fail blocks the syscall and returns some sort of error value. See the full
15//! list of actions under ::RET for more information. Finally, only word-sized,
16//! absolute loads (`ld [k]`) are supported to read from the `data` structure.
1917//!
2018//! There are some issues with the filter API that have traditionally made
2119//! writing them a pain:
lib/std/os/windows/ws2_32.zig+14-5
......@@ -1,4 +1,5 @@
11const std = @import("../../std.zig");
2const assert = std.debug.assert;
23const windows = std.os.windows;
34
45const WINAPI = windows.WINAPI;
......@@ -1106,7 +1107,15 @@ pub const sockaddr = extern struct {
11061107 data: [14]u8,
11071108
11081109 pub const SS_MAXSIZE = 128;
1109 pub const storage = std.x.os.Socket.Address.Native.Storage;
1110 pub const storage = extern struct {
1111 family: ADDRESS_FAMILY align(8),
1112 padding: [SS_MAXSIZE - @sizeOf(ADDRESS_FAMILY)]u8 = undefined,
1113
1114 comptime {
1115 assert(@sizeOf(storage) == SS_MAXSIZE);
1116 assert(@alignOf(storage) == 8);
1117 }
1118 };
11101119
11111120 /// IPv4 socket address
11121121 pub const in = extern struct {
......@@ -1207,7 +1216,7 @@ pub const LPFN_GETACCEPTEXSOCKADDRS = *const fn (
12071216
12081217pub const LPFN_WSASENDMSG = *const fn (
12091218 s: SOCKET,
1210 lpMsg: *const std.x.os.Socket.Message,
1219 lpMsg: *const WSAMSG_const,
12111220 dwFlags: u32,
12121221 lpNumberOfBytesSent: ?*u32,
12131222 lpOverlapped: ?*OVERLAPPED,
......@@ -1216,7 +1225,7 @@ pub const LPFN_WSASENDMSG = *const fn (
12161225
12171226pub const LPFN_WSARECVMSG = *const fn (
12181227 s: SOCKET,
1219 lpMsg: *std.x.os.Socket.Message,
1228 lpMsg: *WSAMSG,
12201229 lpdwNumberOfBytesRecv: ?*u32,
12211230 lpOverlapped: ?*OVERLAPPED,
12221231 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
......@@ -2090,7 +2099,7 @@ pub extern "ws2_32" fn WSASend(
20902099
20912100pub extern "ws2_32" fn WSASendMsg(
20922101 s: SOCKET,
2093 lpMsg: *const std.x.os.Socket.Message,
2102 lpMsg: *WSAMSG_const,
20942103 dwFlags: u32,
20952104 lpNumberOfBytesSent: ?*u32,
20962105 lpOverlapped: ?*OVERLAPPED,
......@@ -2099,7 +2108,7 @@ pub extern "ws2_32" fn WSASendMsg(
20992108
21002109pub extern "ws2_32" fn WSARecvMsg(
21012110 s: SOCKET,
2102 lpMsg: *std.x.os.Socket.Message,
2111 lpMsg: *WSAMSG,
21032112 lpdwNumberOfBytesRecv: ?*u32,
21042113 lpOverlapped: ?*OVERLAPPED,
21052114 lpCompletionRoutine: ?LPWSAOVERLAPPED_COMPLETION_ROUTINE,
lib/std/std.zig-1
......@@ -90,7 +90,6 @@ pub const tz = @import("tz.zig");
9090pub const unicode = @import("unicode.zig");
9191pub const valgrind = @import("valgrind.zig");
9292pub const wasm = @import("wasm.zig");
93pub const x = @import("x.zig");
9493pub const zig = @import("zig.zig");
9594pub const start = @import("start.zig");
9695
lib/std/x.zig deleted-19
......@@ -1,19 +0,0 @@
1const std = @import("std.zig");
2
3pub const os = struct {
4 pub const Socket = @import("x/os/socket.zig").Socket;
5 pub usingnamespace @import("x/os/io.zig");
6 pub usingnamespace @import("x/os/net.zig");
7};
8
9pub const net = struct {
10 pub const ip = @import("x/net/ip.zig");
11 pub const tcp = @import("x/net/tcp.zig");
12 pub const bpf = @import("x/net/bpf.zig");
13};
14
15test {
16 inline for (.{ os, net }) |module| {
17 std.testing.refAllDecls(module);
18 }
19}
lib/std/x/net/bpf.zig deleted-1003
......@@ -1,1003 +0,0 @@
1//! This package provides instrumentation for creating Berkeley Packet Filter[1]
2//! (BPF) programs, along with a simulator for running them.
3//!
4//! BPF is a mechanism for cheap, in-kernel packet filtering. Programs are
5//! attached to a network device and executed for every packet that flows
6//! through it. The program must then return a verdict: the amount of packet
7//! bytes that the kernel should copy into userspace. Execution speed is
8//! achieved by having programs run in a limited virtual machine, which has the
9//! added benefit of graceful failure in the face of buggy programs.
10//!
11//! The BPF virtual machine has a 32-bit word length and a small number of
12//! word-sized registers:
13//!
14//! - The accumulator, `a`: The source/destination of arithmetic and logic
15//! operations.
16//! - The index register, `x`: Used as an offset for indirect memory access and
17//! as a comparison value for conditional jumps.
18//! - The scratch memory store, `M[0]..M[15]`: Used for saving the value of a/x
19//! for later use.
20//!
21//! The packet being examined is an array of bytes, and is addressed using plain
22//! array subscript notation, e.g. [10] for the byte at offset 10. An implicit
23//! program counter, `pc`, is intialized to zero and incremented for each instruction.
24//!
25//! The machine has a fixed instruction set with the following form, where the
26//! numbers represent bit length:
27//!
28//! ```
29//! ┌───────────┬──────┬──────┐
30//! │ opcode:16 │ jt:8 │ jt:8 │
31//! ├───────────┴──────┴──────┤
32//! │ k:32 │
33//! └─────────────────────────┘
34//! ```
35//!
36//! The `opcode` indicates the instruction class and its addressing mode.
37//! Opcodes are generated by performing binary addition on the 8-bit class and
38//! mode constants. For example, the opcode for loading a byte from the packet
39//! at X + 2, (`ldb [x + 2]`), is:
40//!
41//! ```
42//! LD | IND | B = 0x00 | 0x40 | 0x20
43//! = 0x60
44//! ```
45//!
46//! `jt` is an offset used for conditional jumps, and increments the program
47//! counter by its amount if the comparison was true. Conversely, `jf`
48//! increments the counter if it was false. These fields are ignored in all
49//! other cases. `k` is a generic variable used for various purposes, most
50//! commonly as some sort of constant.
51//!
52//! This package contains opcode extensions used by different implementations,
53//! where "extension" is anything outside of the original that was imported into
54//! 4.4BSD[2]. These are marked with "EXTENSION", along with a list of
55//! implementations that use them.
56//!
57//! Most of the doc-comments use the BPF assembly syntax as described in the
58//! original paper[1]. For the sake of completeness, here is the complete
59//! instruction set, along with the extensions:
60//!
61//!```
62//! opcode addressing modes
63//! ld #k #len M[k] [k] [x + k]
64//! ldh [k] [x + k]
65//! ldb [k] [x + k]
66//! ldx #k #len M[k] 4 * ([k] & 0xf) arc4random()
67//! st M[k]
68//! stx M[k]
69//! jmp L
70//! jeq #k, Lt, Lf
71//! jgt #k, Lt, Lf
72//! jge #k, Lt, Lf
73//! jset #k, Lt, Lf
74//! add #k x
75//! sub #k x
76//! mul #k x
77//! div #k x
78//! or #k x
79//! and #k x
80//! lsh #k x
81//! rsh #k x
82//! neg #k x
83//! mod #k x
84//! xor #k x
85//! ret #k a
86//! tax
87//! txa
88//! ```
89//!
90//! Finally, a note on program design. The lack of backwards jumps leads to a
91//! "return early, return often" control flow. Take for example the program
92//! generated from the tcpdump filter `ip`:
93//!
94//! ```
95//! (000) ldh [12] ; Ethernet Packet Type
96//! (001) jeq #0x86dd, 2, 7 ; ETHERTYPE_IPV6
97//! (002) ldb [20] ; IPv6 Next Header
98//! (003) jeq #0x6, 10, 4 ; TCP
99//! (004) jeq #0x2c, 5, 11 ; IPv6 Fragment Header
100//! (005) ldb [54] ; TCP Source Port
101//! (006) jeq #0x6, 10, 11 ; IPPROTO_TCP
102//! (007) jeq #0x800, 8, 11 ; ETHERTYPE_IP
103//! (008) ldb [23] ; IPv4 Protocol
104//! (009) jeq #0x6, 10, 11 ; IPPROTO_TCP
105//! (010) ret #262144 ; copy 0x40000
106//! (011) ret #0 ; skip packet
107//! ```
108//!
109//! Here we can make a few observations:
110//!
111//! - The problem "filter only tcp packets" has essentially been transformed
112//! into a series of layer checks.
113//! - There are two distinct branches in the code, one for validating IPv4
114//! headers and one for IPv6 headers.
115//! - Most conditional jumps in these branches lead directly to the last two
116//! instructions, a pass or fail. Thus the goal of a program is to find the
117//! fastest route to a pass/fail comparison.
118//!
119//! [1]: S. McCanne and V. Jacobson, "The BSD Packet Filter: A New Architecture
120//! for User-level Packet Capture", Proceedings of the 1993 Winter USENIX.
121//! [2]: https://minnie.tuhs.org/cgi-bin/utree.pl?file=4.4BSD/usr/src/sys/net/bpf.h
122const std = @import("std");
123const builtin = @import("builtin");
124const native_endian = builtin.target.cpu.arch.endian();
125const mem = std.mem;
126const math = std.math;
127const random = std.crypto.random;
128const assert = std.debug.assert;
129const expectEqual = std.testing.expectEqual;
130const expectError = std.testing.expectError;
131const expect = std.testing.expect;
132
133// instruction classes
134/// ld, ldh, ldb: Load data into a.
135pub const LD = 0x00;
136/// ldx: Load data into x.
137pub const LDX = 0x01;
138/// st: Store into scratch memory the value of a.
139pub const ST = 0x02;
140/// st: Store into scratch memory the value of x.
141pub const STX = 0x03;
142/// alu: Wrapping arithmetic/bitwise operations on a using the value of k/x.
143pub const ALU = 0x04;
144/// jmp, jeq, jgt, je, jset: Increment the program counter based on a comparison
145/// between k/x and the accumulator.
146pub const JMP = 0x05;
147/// ret: Return a verdict using the value of k/the accumulator.
148pub const RET = 0x06;
149/// tax, txa: Register value copying between X and a.
150pub const MISC = 0x07;
151
152// Size of data to be loaded from the packet.
153/// ld: 32-bit full word.
154pub const W = 0x00;
155/// ldh: 16-bit half word.
156pub const H = 0x08;
157/// ldb: Single byte.
158pub const B = 0x10;
159
160// Addressing modes used for loads to a/x.
161/// #k: The immediate value stored in k.
162pub const IMM = 0x00;
163/// [k]: The value at offset k in the packet.
164pub const ABS = 0x20;
165/// [x + k]: The value at offset x + k in the packet.
166pub const IND = 0x40;
167/// M[k]: The value of the k'th scratch memory register.
168pub const MEM = 0x60;
169/// #len: The size of the packet.
170pub const LEN = 0x80;
171/// 4 * ([k] & 0xf): Four times the low four bits of the byte at offset k in the
172/// packet. This is used for efficiently loading the header length of an IP
173/// packet.
174pub const MSH = 0xa0;
175/// arc4random: 32-bit integer generated from a CPRNG (see arc4random(3)) loaded into a.
176/// EXTENSION. Defined for:
177/// - OpenBSD.
178pub const RND = 0xc0;
179
180// Modifiers for different instruction classes.
181/// Use the value of k for alu operations (add #k).
182/// Compare against the value of k for jumps (jeq #k, Lt, Lf).
183/// Return the value of k for returns (ret #k).
184pub const K = 0x00;
185/// Use the value of x for alu operations (add x).
186/// Compare against the value of X for jumps (jeq x, Lt, Lf).
187pub const X = 0x08;
188/// Return the value of a for returns (ret a).
189pub const A = 0x10;
190
191// ALU Operations on a using the value of k/x.
192// All arithmetic operations are defined to overflow the value of a.
193/// add: a = a + k
194/// a = a + x.
195pub const ADD = 0x00;
196/// sub: a = a - k
197/// a = a - x.
198pub const SUB = 0x10;
199/// mul: a = a * k
200/// a = a * x.
201pub const MUL = 0x20;
202/// div: a = a / k
203/// a = a / x.
204/// Truncated division.
205pub const DIV = 0x30;
206/// or: a = a | k
207/// a = a | x.
208pub const OR = 0x40;
209/// and: a = a & k
210/// a = a & x.
211pub const AND = 0x50;
212/// lsh: a = a << k
213/// a = a << x.
214/// a = a << k, a = a << x.
215pub const LSH = 0x60;
216/// rsh: a = a >> k
217/// a = a >> x.
218pub const RSH = 0x70;
219/// neg: a = -a.
220/// Note that this isn't a binary negation, rather the value of `~a + 1`.
221pub const NEG = 0x80;
222/// mod: a = a % k
223/// a = a % x.
224/// EXTENSION. Defined for:
225/// - Linux.
226/// - NetBSD + Minix 3.
227/// - FreeBSD and derivitives.
228pub const MOD = 0x90;
229/// xor: a = a ^ k
230/// a = a ^ x.
231/// EXTENSION. Defined for:
232/// - Linux.
233/// - NetBSD + Minix 3.
234/// - FreeBSD and derivitives.
235pub const XOR = 0xa0;
236
237// Jump operations using a comparison between a and x/k.
238/// jmp L: pc += k.
239/// No comparison done here.
240pub const JA = 0x00;
241/// jeq #k, Lt, Lf: pc += (a == k) ? jt : jf.
242/// jeq x, Lt, Lf: pc += (a == x) ? jt : jf.
243pub const JEQ = 0x10;
244/// jgt #k, Lt, Lf: pc += (a > k) ? jt : jf.
245/// jgt x, Lt, Lf: pc += (a > x) ? jt : jf.
246pub const JGT = 0x20;
247/// jge #k, Lt, Lf: pc += (a >= k) ? jt : jf.
248/// jge x, Lt, Lf: pc += (a >= x) ? jt : jf.
249pub const JGE = 0x30;
250/// jset #k, Lt, Lf: pc += (a & k > 0) ? jt : jf.
251/// jset x, Lt, Lf: pc += (a & x > 0) ? jt : jf.
252pub const JSET = 0x40;
253
254// Miscellaneous operations/register copy.
255/// tax: x = a.
256pub const TAX = 0x00;
257/// txa: a = x.
258pub const TXA = 0x80;
259
260/// The 16 registers in the scratch memory store as named enums.
261pub const Scratch = enum(u4) { m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15 };
262pub const MEMWORDS = 16;
263pub const MAXINSNS = switch (builtin.os.tag) {
264 .linux => 4096,
265 else => 512,
266};
267pub const MINBUFSIZE = 32;
268pub const MAXBUFSIZE = 1 << 21;
269
270pub const Insn = extern struct {
271 opcode: u16,
272 jt: u8,
273 jf: u8,
274 k: u32,
275
276 /// Implements the `std.fmt.format` API.
277 /// The formatting is similar to the output of tcpdump -dd.
278 pub fn format(
279 self: Insn,
280 comptime layout: []const u8,
281 opts: std.fmt.FormatOptions,
282 writer: anytype,
283 ) !void {
284 _ = opts;
285 if (layout.len != 0) std.fmt.invalidFmtError(layout, self);
286
287 try std.fmt.format(
288 writer,
289 "Insn{{ 0x{X:0<2}, {d}, {d}, 0x{X:0<8} }}",
290 .{ self.opcode, self.jt, self.jf, self.k },
291 );
292 }
293
294 const Size = enum(u8) {
295 word = W,
296 half_word = H,
297 byte = B,
298 };
299
300 fn stmt(opcode: u16, k: u32) Insn {
301 return .{
302 .opcode = opcode,
303 .jt = 0,
304 .jf = 0,
305 .k = k,
306 };
307 }
308
309 pub fn ld_imm(value: u32) Insn {
310 return stmt(LD | IMM, value);
311 }
312
313 pub fn ld_abs(size: Size, offset: u32) Insn {
314 return stmt(LD | ABS | @enumToInt(size), offset);
315 }
316
317 pub fn ld_ind(size: Size, offset: u32) Insn {
318 return stmt(LD | IND | @enumToInt(size), offset);
319 }
320
321 pub fn ld_mem(reg: Scratch) Insn {
322 return stmt(LD | MEM, @enumToInt(reg));
323 }
324
325 pub fn ld_len() Insn {
326 return stmt(LD | LEN | W, 0);
327 }
328
329 pub fn ld_rnd() Insn {
330 return stmt(LD | RND | W, 0);
331 }
332
333 pub fn ldx_imm(value: u32) Insn {
334 return stmt(LDX | IMM, value);
335 }
336
337 pub fn ldx_mem(reg: Scratch) Insn {
338 return stmt(LDX | MEM, @enumToInt(reg));
339 }
340
341 pub fn ldx_len() Insn {
342 return stmt(LDX | LEN | W, 0);
343 }
344
345 pub fn ldx_msh(offset: u32) Insn {
346 return stmt(LDX | MSH | B, offset);
347 }
348
349 pub fn st(reg: Scratch) Insn {
350 return stmt(ST, @enumToInt(reg));
351 }
352 pub fn stx(reg: Scratch) Insn {
353 return stmt(STX, @enumToInt(reg));
354 }
355
356 const AluOp = enum(u16) {
357 add = ADD,
358 sub = SUB,
359 mul = MUL,
360 div = DIV,
361 @"or" = OR,
362 @"and" = AND,
363 lsh = LSH,
364 rsh = RSH,
365 mod = MOD,
366 xor = XOR,
367 };
368
369 const Source = enum(u16) {
370 k = K,
371 x = X,
372 };
373 const KOrX = union(Source) {
374 k: u32,
375 x: void,
376 };
377
378 pub fn alu_neg() Insn {
379 return stmt(ALU | NEG, 0);
380 }
381
382 pub fn alu(op: AluOp, source: KOrX) Insn {
383 return stmt(
384 ALU | @enumToInt(op) | @enumToInt(source),
385 if (source == .k) source.k else 0,
386 );
387 }
388
389 const JmpOp = enum(u16) {
390 jeq = JEQ,
391 jgt = JGT,
392 jge = JGE,
393 jset = JSET,
394 };
395
396 pub fn jmp_ja(location: u32) Insn {
397 return stmt(JMP | JA, location);
398 }
399
400 pub fn jmp(op: JmpOp, source: KOrX, jt: u8, jf: u8) Insn {
401 return Insn{
402 .opcode = JMP | @enumToInt(op) | @enumToInt(source),
403 .jt = jt,
404 .jf = jf,
405 .k = if (source == .k) source.k else 0,
406 };
407 }
408
409 const Verdict = enum(u16) {
410 k = K,
411 a = A,
412 };
413 const KOrA = union(Verdict) {
414 k: u32,
415 a: void,
416 };
417
418 pub fn ret(verdict: KOrA) Insn {
419 return stmt(
420 RET | @enumToInt(verdict),
421 if (verdict == .k) verdict.k else 0,
422 );
423 }
424
425 pub fn tax() Insn {
426 return stmt(MISC | TAX, 0);
427 }
428
429 pub fn txa() Insn {
430 return stmt(MISC | TXA, 0);
431 }
432};
433
434fn opcodeEqual(opcode: u16, insn: Insn) !void {
435 try expectEqual(opcode, insn.opcode);
436}
437
438test "opcodes" {
439 try opcodeEqual(0x00, Insn.ld_imm(0));
440 try opcodeEqual(0x20, Insn.ld_abs(.word, 0));
441 try opcodeEqual(0x28, Insn.ld_abs(.half_word, 0));
442 try opcodeEqual(0x30, Insn.ld_abs(.byte, 0));
443 try opcodeEqual(0x40, Insn.ld_ind(.word, 0));
444 try opcodeEqual(0x48, Insn.ld_ind(.half_word, 0));
445 try opcodeEqual(0x50, Insn.ld_ind(.byte, 0));
446 try opcodeEqual(0x60, Insn.ld_mem(.m0));
447 try opcodeEqual(0x80, Insn.ld_len());
448 try opcodeEqual(0xc0, Insn.ld_rnd());
449
450 try opcodeEqual(0x01, Insn.ldx_imm(0));
451 try opcodeEqual(0x61, Insn.ldx_mem(.m0));
452 try opcodeEqual(0x81, Insn.ldx_len());
453 try opcodeEqual(0xb1, Insn.ldx_msh(0));
454
455 try opcodeEqual(0x02, Insn.st(.m0));
456 try opcodeEqual(0x03, Insn.stx(.m0));
457
458 try opcodeEqual(0x04, Insn.alu(.add, .{ .k = 0 }));
459 try opcodeEqual(0x14, Insn.alu(.sub, .{ .k = 0 }));
460 try opcodeEqual(0x24, Insn.alu(.mul, .{ .k = 0 }));
461 try opcodeEqual(0x34, Insn.alu(.div, .{ .k = 0 }));
462 try opcodeEqual(0x44, Insn.alu(.@"or", .{ .k = 0 }));
463 try opcodeEqual(0x54, Insn.alu(.@"and", .{ .k = 0 }));
464 try opcodeEqual(0x64, Insn.alu(.lsh, .{ .k = 0 }));
465 try opcodeEqual(0x74, Insn.alu(.rsh, .{ .k = 0 }));
466 try opcodeEqual(0x94, Insn.alu(.mod, .{ .k = 0 }));
467 try opcodeEqual(0xa4, Insn.alu(.xor, .{ .k = 0 }));
468 try opcodeEqual(0x84, Insn.alu_neg());
469 try opcodeEqual(0x0c, Insn.alu(.add, .x));
470 try opcodeEqual(0x1c, Insn.alu(.sub, .x));
471 try opcodeEqual(0x2c, Insn.alu(.mul, .x));
472 try opcodeEqual(0x3c, Insn.alu(.div, .x));
473 try opcodeEqual(0x4c, Insn.alu(.@"or", .x));
474 try opcodeEqual(0x5c, Insn.alu(.@"and", .x));
475 try opcodeEqual(0x6c, Insn.alu(.lsh, .x));
476 try opcodeEqual(0x7c, Insn.alu(.rsh, .x));
477 try opcodeEqual(0x9c, Insn.alu(.mod, .x));
478 try opcodeEqual(0xac, Insn.alu(.xor, .x));
479
480 try opcodeEqual(0x05, Insn.jmp_ja(0));
481 try opcodeEqual(0x15, Insn.jmp(.jeq, .{ .k = 0 }, 0, 0));
482 try opcodeEqual(0x25, Insn.jmp(.jgt, .{ .k = 0 }, 0, 0));
483 try opcodeEqual(0x35, Insn.jmp(.jge, .{ .k = 0 }, 0, 0));
484 try opcodeEqual(0x45, Insn.jmp(.jset, .{ .k = 0 }, 0, 0));
485 try opcodeEqual(0x1d, Insn.jmp(.jeq, .x, 0, 0));
486 try opcodeEqual(0x2d, Insn.jmp(.jgt, .x, 0, 0));
487 try opcodeEqual(0x3d, Insn.jmp(.jge, .x, 0, 0));
488 try opcodeEqual(0x4d, Insn.jmp(.jset, .x, 0, 0));
489
490 try opcodeEqual(0x06, Insn.ret(.{ .k = 0 }));
491 try opcodeEqual(0x16, Insn.ret(.a));
492
493 try opcodeEqual(0x07, Insn.tax());
494 try opcodeEqual(0x87, Insn.txa());
495}
496
497pub const Error = error{
498 InvalidOpcode,
499 InvalidOffset,
500 InvalidLocation,
501 DivisionByZero,
502 NoReturn,
503};
504
505/// A simple implementation of the BPF virtual-machine.
506/// Use this to run/debug programs.
507pub fn simulate(
508 packet: []const u8,
509 filter: []const Insn,
510 byte_order: std.builtin.Endian,
511) Error!u32 {
512 assert(filter.len > 0 and filter.len < MAXINSNS);
513 assert(packet.len < MAXBUFSIZE);
514 const len = @intCast(u32, packet.len);
515
516 var a: u32 = 0;
517 var x: u32 = 0;
518 var m = mem.zeroes([MEMWORDS]u32);
519 var pc: usize = 0;
520
521 while (pc < filter.len) : (pc += 1) {
522 const i = filter[pc];
523 // Cast to a wider type to protect against overflow.
524 const k = @as(u64, i.k);
525 const remaining = filter.len - (pc + 1);
526
527 // Do validation/error checking here to compress the second switch.
528 switch (i.opcode) {
529 LD | ABS | W => if (k + @sizeOf(u32) - 1 >= packet.len) return error.InvalidOffset,
530 LD | ABS | H => if (k + @sizeOf(u16) - 1 >= packet.len) return error.InvalidOffset,
531 LD | ABS | B => if (k >= packet.len) return error.InvalidOffset,
532 LD | IND | W => if (k + x + @sizeOf(u32) - 1 >= packet.len) return error.InvalidOffset,
533 LD | IND | H => if (k + x + @sizeOf(u16) - 1 >= packet.len) return error.InvalidOffset,
534 LD | IND | B => if (k + x >= packet.len) return error.InvalidOffset,
535
536 LDX | MSH | B => if (k >= packet.len) return error.InvalidOffset,
537 ST, STX, LD | MEM, LDX | MEM => if (i.k >= MEMWORDS) return error.InvalidOffset,
538
539 JMP | JA => if (remaining <= i.k) return error.InvalidOffset,
540 JMP | JEQ | K,
541 JMP | JGT | K,
542 JMP | JGE | K,
543 JMP | JSET | K,
544 JMP | JEQ | X,
545 JMP | JGT | X,
546 JMP | JGE | X,
547 JMP | JSET | X,
548 => if (remaining <= i.jt or remaining <= i.jf) return error.InvalidLocation,
549 else => {},
550 }
551 switch (i.opcode) {
552 LD | IMM => a = i.k,
553 LD | MEM => a = m[i.k],
554 LD | LEN | W => a = len,
555 LD | RND | W => a = random.int(u32),
556 LD | ABS | W => a = mem.readInt(u32, packet[i.k..][0..@sizeOf(u32)], byte_order),
557 LD | ABS | H => a = mem.readInt(u16, packet[i.k..][0..@sizeOf(u16)], byte_order),
558 LD | ABS | B => a = packet[i.k],
559 LD | IND | W => a = mem.readInt(u32, packet[i.k + x ..][0..@sizeOf(u32)], byte_order),
560 LD | IND | H => a = mem.readInt(u16, packet[i.k + x ..][0..@sizeOf(u16)], byte_order),
561 LD | IND | B => a = packet[i.k + x],
562
563 LDX | IMM => x = i.k,
564 LDX | MEM => x = m[i.k],
565 LDX | LEN | W => x = len,
566 LDX | MSH | B => x = @as(u32, @truncate(u4, packet[i.k])) << 2,
567
568 ST => m[i.k] = a,
569 STX => m[i.k] = x,
570
571 ALU | ADD | K => a +%= i.k,
572 ALU | SUB | K => a -%= i.k,
573 ALU | MUL | K => a *%= i.k,
574 ALU | DIV | K => a = try math.divTrunc(u32, a, i.k),
575 ALU | OR | K => a |= i.k,
576 ALU | AND | K => a &= i.k,
577 ALU | LSH | K => a = math.shl(u32, a, i.k),
578 ALU | RSH | K => a = math.shr(u32, a, i.k),
579 ALU | MOD | K => a = try math.mod(u32, a, i.k),
580 ALU | XOR | K => a ^= i.k,
581 ALU | ADD | X => a +%= x,
582 ALU | SUB | X => a -%= x,
583 ALU | MUL | X => a *%= x,
584 ALU | DIV | X => a = try math.divTrunc(u32, a, x),
585 ALU | OR | X => a |= x,
586 ALU | AND | X => a &= x,
587 ALU | LSH | X => a = math.shl(u32, a, x),
588 ALU | RSH | X => a = math.shr(u32, a, x),
589 ALU | MOD | X => a = try math.mod(u32, a, x),
590 ALU | XOR | X => a ^= x,
591 ALU | NEG => a = @bitCast(u32, -%@bitCast(i32, a)),
592
593 JMP | JA => pc += i.k,
594 JMP | JEQ | K => pc += if (a == i.k) i.jt else i.jf,
595 JMP | JGT | K => pc += if (a > i.k) i.jt else i.jf,
596 JMP | JGE | K => pc += if (a >= i.k) i.jt else i.jf,
597 JMP | JSET | K => pc += if (a & i.k > 0) i.jt else i.jf,
598 JMP | JEQ | X => pc += if (a == x) i.jt else i.jf,
599 JMP | JGT | X => pc += if (a > x) i.jt else i.jf,
600 JMP | JGE | X => pc += if (a >= x) i.jt else i.jf,
601 JMP | JSET | X => pc += if (a & x > 0) i.jt else i.jf,
602
603 RET | K => return i.k,
604 RET | A => return a,
605
606 MISC | TAX => x = a,
607 MISC | TXA => a = x,
608 else => return error.InvalidOpcode,
609 }
610 }
611
612 return error.NoReturn;
613}
614
615// This program is the BPF form of the tcpdump filter:
616//
617// tcpdump -dd 'ip host mirror.internode.on.net and tcp port ftp-data'
618//
619// As of January 2022, mirror.internode.on.net resolves to 150.101.135.3
620//
621// For reference, here's what it looks like in BPF assembler.
622// Note that the jumps are used for TCP/IP layer checks.
623//
624// ```
625// ldh [12] (#proto)
626// jeq #0x0800 (ETHERTYPE_IP), L1, fail
627// L1: ld [26]
628// jeq #150.101.135.3, L2, dest
629// dest: ld [30]
630// jeq #150.101.135.3, L2, fail
631// L2: ldb [23]
632// jeq #0x6 (IPPROTO_TCP), L3, fail
633// L3: ldh [20]
634// jset #0x1fff, fail, plen
635// plen: ldx 4 * ([14] & 0xf)
636// ldh [x + 14]
637// jeq #0x14 (FTP), pass, dstp
638// dstp: ldh [x + 16]
639// jeq #0x14 (FTP), pass, fail
640// pass: ret #0x40000
641// fail: ret #0
642// ```
643const tcpdump_filter = [_]Insn{
644 Insn.ld_abs(.half_word, 12),
645 Insn.jmp(.jeq, .{ .k = 0x800 }, 0, 14),
646 Insn.ld_abs(.word, 26),
647 Insn.jmp(.jeq, .{ .k = 0x96658703 }, 2, 0),
648 Insn.ld_abs(.word, 30),
649 Insn.jmp(.jeq, .{ .k = 0x96658703 }, 0, 10),
650 Insn.ld_abs(.byte, 23),
651 Insn.jmp(.jeq, .{ .k = 0x6 }, 0, 8),
652 Insn.ld_abs(.half_word, 20),
653 Insn.jmp(.jset, .{ .k = 0x1fff }, 6, 0),
654 Insn.ldx_msh(14),
655 Insn.ld_ind(.half_word, 14),
656 Insn.jmp(.jeq, .{ .k = 0x14 }, 2, 0),
657 Insn.ld_ind(.half_word, 16),
658 Insn.jmp(.jeq, .{ .k = 0x14 }, 0, 1),
659 Insn.ret(.{ .k = 0x40000 }),
660 Insn.ret(.{ .k = 0 }),
661};
662
663// This packet is the output of `ls` on mirror.internode.on.net:/, captured
664// using the filter above.
665//
666// zig fmt: off
667const ftp_data = [_]u8{
668 // ethernet - 14 bytes: IPv4(0x0800) from a4:71:74:ad:4b:f0 -> de:ad:be:ef:f0:0f
669 0xde, 0xad, 0xbe, 0xef, 0xf0, 0x0f, 0xa4, 0x71, 0x74, 0xad, 0x4b, 0xf0, 0x08, 0x00,
670 // IPv4 - 20 bytes: TCP data from 150.101.135.3 -> 192.168.1.3
671 0x45, 0x00, 0x01, 0xf2, 0x70, 0x3b, 0x40, 0x00, 0x37, 0x06, 0xf2, 0xb6,
672 0x96, 0x65, 0x87, 0x03, 0xc0, 0xa8, 0x01, 0x03,
673 // TCP - 32 bytes: Source port: 20 (FTP). Payload = 446 bytes
674 0x00, 0x14, 0x80, 0x6d, 0x35, 0x81, 0x2d, 0x40, 0x4f, 0x8a, 0x29, 0x9e, 0x80, 0x18, 0x00, 0x2e,
675 0x88, 0x8d, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x0b, 0x59, 0x5d, 0x09, 0x32, 0x8b, 0x51, 0xa0
676} ++
677 // Raw line-based FTP data - 446 bytes
678 "lrwxrwxrwx 1 root root 12 Feb 14 2012 debian -> .pub2/debian\r\n" ++
679 "lrwxrwxrwx 1 root root 15 Feb 14 2012 debian-cd -> .pub2/debian-cd\r\n" ++
680 "lrwxrwxrwx 1 root root 9 Mar 9 2018 linux -> pub/linux\r\n" ++
681 "drwxr-xr-X 3 mirror mirror 4096 Sep 20 08:10 pub\r\n" ++
682 "lrwxrwxrwx 1 root root 12 Feb 14 2012 ubuntu -> .pub2/ubuntu\r\n" ++
683 "-rw-r--r-- 1 root root 1044 Jan 20 2015 welcome.msg\r\n";
684// zig fmt: on
685
686test "tcpdump filter" {
687 try expectEqual(
688 @as(u32, 0x40000),
689 try simulate(ftp_data, &tcpdump_filter, .Big),
690 );
691}
692
693fn expectPass(data: anytype, filter: []const Insn) !void {
694 try expectEqual(
695 @as(u32, 0),
696 try simulate(mem.asBytes(data), filter, .Big),
697 );
698}
699
700fn expectFail(expected_error: anyerror, data: anytype, filter: []const Insn) !void {
701 try expectError(
702 expected_error,
703 simulate(mem.asBytes(data), filter, native_endian),
704 );
705}
706
707test "simulator coverage" {
708 const some_data = [_]u8{
709 0xaa, 0xbb, 0xcc, 0xdd, 0x7f,
710 };
711
712 try expectPass(&some_data, &.{
713 // ld #10
714 // ldx #1
715 // st M[0]
716 // stx M[1]
717 // fail if A != 10
718 Insn.ld_imm(10),
719 Insn.ldx_imm(1),
720 Insn.st(.m0),
721 Insn.stx(.m1),
722 Insn.jmp(.jeq, .{ .k = 10 }, 1, 0),
723 Insn.ret(.{ .k = 1 }),
724 // ld [0]
725 // fail if A != 0xaabbccdd
726 Insn.ld_abs(.word, 0),
727 Insn.jmp(.jeq, .{ .k = 0xaabbccdd }, 1, 0),
728 Insn.ret(.{ .k = 2 }),
729 // ldh [0]
730 // fail if A != 0xaabb
731 Insn.ld_abs(.half_word, 0),
732 Insn.jmp(.jeq, .{ .k = 0xaabb }, 1, 0),
733 Insn.ret(.{ .k = 3 }),
734 // ldb [0]
735 // fail if A != 0xaa
736 Insn.ld_abs(.byte, 0),
737 Insn.jmp(.jeq, .{ .k = 0xaa }, 1, 0),
738 Insn.ret(.{ .k = 4 }),
739 // ld [x + 0]
740 // fail if A != 0xbbccdd7f
741 Insn.ld_ind(.word, 0),
742 Insn.jmp(.jeq, .{ .k = 0xbbccdd7f }, 1, 0),
743 Insn.ret(.{ .k = 5 }),
744 // ldh [x + 0]
745 // fail if A != 0xbbcc
746 Insn.ld_ind(.half_word, 0),
747 Insn.jmp(.jeq, .{ .k = 0xbbcc }, 1, 0),
748 Insn.ret(.{ .k = 6 }),
749 // ldb [x + 0]
750 // fail if A != 0xbb
751 Insn.ld_ind(.byte, 0),
752 Insn.jmp(.jeq, .{ .k = 0xbb }, 1, 0),
753 Insn.ret(.{ .k = 7 }),
754 // ld M[0]
755 // fail if A != 10
756 Insn.ld_mem(.m0),
757 Insn.jmp(.jeq, .{ .k = 10 }, 1, 0),
758 Insn.ret(.{ .k = 8 }),
759 // ld #len
760 // fail if A != 5
761 Insn.ld_len(),
762 Insn.jmp(.jeq, .{ .k = some_data.len }, 1, 0),
763 Insn.ret(.{ .k = 9 }),
764 // ld #0
765 // ld arc4random()
766 // fail if A == 0
767 Insn.ld_imm(0),
768 Insn.ld_rnd(),
769 Insn.jmp(.jgt, .{ .k = 0 }, 1, 0),
770 Insn.ret(.{ .k = 10 }),
771 // ld #3
772 // ldx #10
773 // st M[2]
774 // txa
775 // fail if a != x
776 Insn.ld_imm(3),
777 Insn.ldx_imm(10),
778 Insn.st(.m2),
779 Insn.txa(),
780 Insn.jmp(.jeq, .x, 1, 0),
781 Insn.ret(.{ .k = 11 }),
782 // ldx M[2]
783 // fail if A <= X
784 Insn.ldx_mem(.m2),
785 Insn.jmp(.jgt, .x, 1, 0),
786 Insn.ret(.{ .k = 12 }),
787 // ldx #len
788 // fail if a <= x
789 Insn.ldx_len(),
790 Insn.jmp(.jgt, .x, 1, 0),
791 Insn.ret(.{ .k = 13 }),
792 // a = 4 * (0x7f & 0xf)
793 // x = 4 * ([4] & 0xf)
794 // fail if a != x
795 Insn.ld_imm(4 * (0x7f & 0xf)),
796 Insn.ldx_msh(4),
797 Insn.jmp(.jeq, .x, 1, 0),
798 Insn.ret(.{ .k = 14 }),
799 // ld #(u32)-1
800 // ldx #2
801 // add #1
802 // fail if a != 0
803 Insn.ld_imm(0xffffffff),
804 Insn.ldx_imm(2),
805 Insn.alu(.add, .{ .k = 1 }),
806 Insn.jmp(.jeq, .{ .k = 0 }, 1, 0),
807 Insn.ret(.{ .k = 15 }),
808 // sub #1
809 // fail if a != (u32)-1
810 Insn.alu(.sub, .{ .k = 1 }),
811 Insn.jmp(.jeq, .{ .k = 0xffffffff }, 1, 0),
812 Insn.ret(.{ .k = 16 }),
813 // add x
814 // fail if a != 1
815 Insn.alu(.add, .x),
816 Insn.jmp(.jeq, .{ .k = 1 }, 1, 0),
817 Insn.ret(.{ .k = 17 }),
818 // sub x
819 // fail if a != (u32)-1
820 Insn.alu(.sub, .x),
821 Insn.jmp(.jeq, .{ .k = 0xffffffff }, 1, 0),
822 Insn.ret(.{ .k = 18 }),
823 // ld #16
824 // mul #2
825 // fail if a != 32
826 Insn.ld_imm(16),
827 Insn.alu(.mul, .{ .k = 2 }),
828 Insn.jmp(.jeq, .{ .k = 32 }, 1, 0),
829 Insn.ret(.{ .k = 19 }),
830 // mul x
831 // fail if a != 64
832 Insn.alu(.mul, .x),
833 Insn.jmp(.jeq, .{ .k = 64 }, 1, 0),
834 Insn.ret(.{ .k = 20 }),
835 // div #2
836 // fail if a != 32
837 Insn.alu(.div, .{ .k = 2 }),
838 Insn.jmp(.jeq, .{ .k = 32 }, 1, 0),
839 Insn.ret(.{ .k = 21 }),
840 // div x
841 // fail if a != 16
842 Insn.alu(.div, .x),
843 Insn.jmp(.jeq, .{ .k = 16 }, 1, 0),
844 Insn.ret(.{ .k = 22 }),
845 // or #4
846 // fail if a != 20
847 Insn.alu(.@"or", .{ .k = 4 }),
848 Insn.jmp(.jeq, .{ .k = 20 }, 1, 0),
849 Insn.ret(.{ .k = 23 }),
850 // or x
851 // fail if a != 22
852 Insn.alu(.@"or", .x),
853 Insn.jmp(.jeq, .{ .k = 22 }, 1, 0),
854 Insn.ret(.{ .k = 24 }),
855 // and #6
856 // fail if a != 6
857 Insn.alu(.@"and", .{ .k = 0b110 }),
858 Insn.jmp(.jeq, .{ .k = 6 }, 1, 0),
859 Insn.ret(.{ .k = 25 }),
860 // and x
861 // fail if a != 2
862 Insn.alu(.@"and", .x),
863 Insn.jmp(.jeq, .x, 1, 0),
864 Insn.ret(.{ .k = 26 }),
865 // xor #15
866 // fail if a != 13
867 Insn.alu(.xor, .{ .k = 0b1111 }),
868 Insn.jmp(.jeq, .{ .k = 0b1101 }, 1, 0),
869 Insn.ret(.{ .k = 27 }),
870 // xor x
871 // fail if a != 15
872 Insn.alu(.xor, .x),
873 Insn.jmp(.jeq, .{ .k = 0b1111 }, 1, 0),
874 Insn.ret(.{ .k = 28 }),
875 // rsh #1
876 // fail if a != 7
877 Insn.alu(.rsh, .{ .k = 1 }),
878 Insn.jmp(.jeq, .{ .k = 0b0111 }, 1, 0),
879 Insn.ret(.{ .k = 29 }),
880 // rsh x
881 // fail if a != 1
882 Insn.alu(.rsh, .x),
883 Insn.jmp(.jeq, .{ .k = 0b0001 }, 1, 0),
884 Insn.ret(.{ .k = 30 }),
885 // lsh #1
886 // fail if a != 2
887 Insn.alu(.lsh, .{ .k = 1 }),
888 Insn.jmp(.jeq, .{ .k = 0b0010 }, 1, 0),
889 Insn.ret(.{ .k = 31 }),
890 // lsh x
891 // fail if a != 8
892 Insn.alu(.lsh, .x),
893 Insn.jmp(.jeq, .{ .k = 0b1000 }, 1, 0),
894 Insn.ret(.{ .k = 32 }),
895 // mod 6
896 // fail if a != 2
897 Insn.alu(.mod, .{ .k = 6 }),
898 Insn.jmp(.jeq, .{ .k = 2 }, 1, 0),
899 Insn.ret(.{ .k = 33 }),
900 // mod x
901 // fail if a != 0
902 Insn.alu(.mod, .x),
903 Insn.jmp(.jeq, .{ .k = 0 }, 1, 0),
904 Insn.ret(.{ .k = 34 }),
905 // tax
906 // neg
907 // fail if a != (u32)-2
908 Insn.txa(),
909 Insn.alu_neg(),
910 Insn.jmp(.jeq, .{ .k = ~@as(u32, 2) + 1 }, 1, 0),
911 Insn.ret(.{ .k = 35 }),
912 // ja #1 (skip the next instruction)
913 Insn.jmp_ja(1),
914 Insn.ret(.{ .k = 36 }),
915 // ld #20
916 // tax
917 // fail if a != 20
918 // fail if a != x
919 Insn.ld_imm(20),
920 Insn.tax(),
921 Insn.jmp(.jeq, .{ .k = 20 }, 1, 0),
922 Insn.ret(.{ .k = 37 }),
923 Insn.jmp(.jeq, .x, 1, 0),
924 Insn.ret(.{ .k = 38 }),
925 // ld #19
926 // fail if a == 20
927 // fail if a == x
928 // fail if a >= 20
929 // fail if a >= X
930 Insn.ld_imm(19),
931 Insn.jmp(.jeq, .{ .k = 20 }, 0, 1),
932 Insn.ret(.{ .k = 39 }),
933 Insn.jmp(.jeq, .x, 0, 1),
934 Insn.ret(.{ .k = 40 }),
935 Insn.jmp(.jgt, .{ .k = 20 }, 0, 1),
936 Insn.ret(.{ .k = 41 }),
937 Insn.jmp(.jgt, .x, 0, 1),
938 Insn.ret(.{ .k = 42 }),
939 // ld #21
940 // fail if a < 20
941 // fail if a < x
942 Insn.ld_imm(21),
943 Insn.jmp(.jgt, .{ .k = 20 }, 1, 0),
944 Insn.ret(.{ .k = 43 }),
945 Insn.jmp(.jgt, .x, 1, 0),
946 Insn.ret(.{ .k = 44 }),
947 // ldx #22
948 // fail if a < 22
949 // fail if a < x
950 Insn.ldx_imm(22),
951 Insn.jmp(.jge, .{ .k = 22 }, 0, 1),
952 Insn.ret(.{ .k = 45 }),
953 Insn.jmp(.jge, .x, 0, 1),
954 Insn.ret(.{ .k = 46 }),
955 // ld #23
956 // fail if a >= 22
957 // fail if a >= x
958 Insn.ld_imm(23),
959 Insn.jmp(.jge, .{ .k = 22 }, 1, 0),
960 Insn.ret(.{ .k = 47 }),
961 Insn.jmp(.jge, .x, 1, 0),
962 Insn.ret(.{ .k = 48 }),
963 // ldx #0b10100
964 // fail if a & 0b10100 == 0
965 // fail if a & x == 0
966 Insn.ldx_imm(0b10100),
967 Insn.jmp(.jset, .{ .k = 0b10100 }, 1, 0),
968 Insn.ret(.{ .k = 47 }),
969 Insn.jmp(.jset, .x, 1, 0),
970 Insn.ret(.{ .k = 48 }),
971 // ldx #0
972 // fail if a & 0 > 0
973 // fail if a & x > 0
974 Insn.ldx_imm(0),
975 Insn.jmp(.jset, .{ .k = 0 }, 0, 1),
976 Insn.ret(.{ .k = 49 }),
977 Insn.jmp(.jset, .x, 0, 1),
978 Insn.ret(.{ .k = 50 }),
979 Insn.ret(.{ .k = 0 }),
980 });
981 try expectPass(&some_data, &.{
982 Insn.ld_imm(35),
983 Insn.ld_imm(0),
984 Insn.ret(.a),
985 });
986
987 // Errors
988 try expectFail(error.NoReturn, &some_data, &.{
989 Insn.ld_imm(10),
990 });
991 try expectFail(error.InvalidOpcode, &some_data, &.{
992 Insn.stmt(0x7f, 0xdeadbeef),
993 });
994 try expectFail(error.InvalidOffset, &some_data, &.{
995 Insn.stmt(LD | ABS | W, 10),
996 });
997 try expectFail(error.InvalidLocation, &some_data, &.{
998 Insn.jmp(.jeq, .{ .k = 0 }, 10, 0),
999 });
1000 try expectFail(error.InvalidLocation, &some_data, &.{
1001 Insn.jmp(.jeq, .{ .k = 0 }, 0, 10),
1002 });
1003}
lib/std/x/net/ip.zig deleted-57
......@@ -1,57 +0,0 @@
1const std = @import("../../std.zig");
2
3const fmt = std.fmt;
4
5const IPv4 = std.x.os.IPv4;
6const IPv6 = std.x.os.IPv6;
7const Socket = std.x.os.Socket;
8
9/// A generic IP abstraction.
10const ip = @This();
11
12/// A union of all eligible types of IP addresses.
13pub const Address = union(enum) {
14 ipv4: IPv4.Address,
15 ipv6: IPv6.Address,
16
17 /// Instantiate a new address with a IPv4 host and port.
18 pub fn initIPv4(host: IPv4, port: u16) Address {
19 return .{ .ipv4 = .{ .host = host, .port = port } };
20 }
21
22 /// Instantiate a new address with a IPv6 host and port.
23 pub fn initIPv6(host: IPv6, port: u16) Address {
24 return .{ .ipv6 = .{ .host = host, .port = port } };
25 }
26
27 /// Re-interpret a generic socket address into an IP address.
28 pub fn from(address: Socket.Address) ip.Address {
29 return switch (address) {
30 .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
31 .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
32 };
33 }
34
35 /// Re-interpret an IP address into a generic socket address.
36 pub fn into(self: ip.Address) Socket.Address {
37 return switch (self) {
38 .ipv4 => |ipv4_address| .{ .ipv4 = ipv4_address },
39 .ipv6 => |ipv6_address| .{ .ipv6 = ipv6_address },
40 };
41 }
42
43 /// Implements the `std.fmt.format` API.
44 pub fn format(
45 self: ip.Address,
46 comptime layout: []const u8,
47 opts: fmt.FormatOptions,
48 writer: anytype,
49 ) !void {
50 if (layout.len != 0) std.fmt.invalidFmtError(layout, self);
51 _ = opts;
52 switch (self) {
53 .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
54 .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
55 }
56 }
57};
lib/std/x/net/tcp.zig deleted-447
......@@ -1,447 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3
4const io = std.io;
5const os = std.os;
6const ip = std.x.net.ip;
7
8const fmt = std.fmt;
9const mem = std.mem;
10const testing = std.testing;
11const native_os = builtin.os;
12
13const IPv4 = std.x.os.IPv4;
14const IPv6 = std.x.os.IPv6;
15const Socket = std.x.os.Socket;
16const Buffer = std.x.os.Buffer;
17
18/// A generic TCP socket abstraction.
19const tcp = @This();
20
21/// A TCP client-address pair.
22pub const Connection = struct {
23 client: tcp.Client,
24 address: ip.Address,
25
26 /// Enclose a TCP client and address into a client-address pair.
27 pub fn from(conn: Socket.Connection) tcp.Connection {
28 return .{
29 .client = tcp.Client.from(conn.socket),
30 .address = ip.Address.from(conn.address),
31 };
32 }
33
34 /// Unravel a TCP client-address pair into a socket-address pair.
35 pub fn into(self: tcp.Connection) Socket.Connection {
36 return .{
37 .socket = self.client.socket,
38 .address = self.address.into(),
39 };
40 }
41
42 /// Closes the underlying client of the connection.
43 pub fn deinit(self: tcp.Connection) void {
44 self.client.deinit();
45 }
46};
47
48/// Possible domains that a TCP client/listener may operate over.
49pub const Domain = enum(u16) {
50 ip = os.AF.INET,
51 ipv6 = os.AF.INET6,
52};
53
54/// A TCP client.
55pub const Client = struct {
56 socket: Socket,
57
58 /// Implements `std.io.Reader`.
59 pub const Reader = struct {
60 client: Client,
61 flags: u32,
62
63 /// Implements `readFn` for `std.io.Reader`.
64 pub fn read(self: Client.Reader, buffer: []u8) !usize {
65 return self.client.read(buffer, self.flags);
66 }
67 };
68
69 /// Implements `std.io.Writer`.
70 pub const Writer = struct {
71 client: Client,
72 flags: u32,
73
74 /// Implements `writeFn` for `std.io.Writer`.
75 pub fn write(self: Client.Writer, buffer: []const u8) !usize {
76 return self.client.write(buffer, self.flags);
77 }
78 };
79
80 /// Opens a new client.
81 pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Client {
82 return Client{
83 .socket = try Socket.init(
84 @enumToInt(domain),
85 os.SOCK.STREAM,
86 os.IPPROTO.TCP,
87 flags,
88 ),
89 };
90 }
91
92 /// Enclose a TCP client over an existing socket.
93 pub fn from(socket: Socket) Client {
94 return Client{ .socket = socket };
95 }
96
97 /// Closes the client.
98 pub fn deinit(self: Client) void {
99 self.socket.deinit();
100 }
101
102 /// Shutdown either the read side, write side, or all sides of the client's underlying socket.
103 pub fn shutdown(self: Client, how: os.ShutdownHow) !void {
104 return self.socket.shutdown(how);
105 }
106
107 /// Have the client attempt to the connect to an address.
108 pub fn connect(self: Client, address: ip.Address) !void {
109 return self.socket.connect(address.into());
110 }
111
112 /// Extracts the error set of a function.
113 /// TODO: remove after Socket.{read, write} error unions are well-defined across different platforms
114 fn ErrorSetOf(comptime Function: anytype) type {
115 return @typeInfo(@typeInfo(@TypeOf(Function)).Fn.return_type.?).ErrorUnion.error_set;
116 }
117
118 /// Wrap `tcp.Client` into `std.io.Reader`.
119 pub fn reader(self: Client, flags: u32) io.Reader(Client.Reader, ErrorSetOf(Client.Reader.read), Client.Reader.read) {
120 return .{ .context = .{ .client = self, .flags = flags } };
121 }
122
123 /// Wrap `tcp.Client` into `std.io.Writer`.
124 pub fn writer(self: Client, flags: u32) io.Writer(Client.Writer, ErrorSetOf(Client.Writer.write), Client.Writer.write) {
125 return .{ .context = .{ .client = self, .flags = flags } };
126 }
127
128 /// Read data from the socket into the buffer provided with a set of flags
129 /// specified. It returns the number of bytes read into the buffer provided.
130 pub fn read(self: Client, buf: []u8, flags: u32) !usize {
131 return self.socket.read(buf, flags);
132 }
133
134 /// Write a buffer of data provided to the socket with a set of flags specified.
135 /// It returns the number of bytes that are written to the socket.
136 pub fn write(self: Client, buf: []const u8, flags: u32) !usize {
137 return self.socket.write(buf, flags);
138 }
139
140 /// Writes multiple I/O vectors with a prepended message header to the socket
141 /// with a set of flags specified. It returns the number of bytes that are
142 /// written to the socket.
143 pub fn writeMessage(self: Client, msg: Socket.Message, flags: u32) !usize {
144 return self.socket.writeMessage(msg, flags);
145 }
146
147 /// Read multiple I/O vectors with a prepended message header from the socket
148 /// with a set of flags specified. It returns the number of bytes that were
149 /// read into the buffer provided.
150 pub fn readMessage(self: Client, msg: *Socket.Message, flags: u32) !usize {
151 return self.socket.readMessage(msg, flags);
152 }
153
154 /// Query and return the latest cached error on the client's underlying socket.
155 pub fn getError(self: Client) !void {
156 return self.socket.getError();
157 }
158
159 /// Query the read buffer size of the client's underlying socket.
160 pub fn getReadBufferSize(self: Client) !u32 {
161 return self.socket.getReadBufferSize();
162 }
163
164 /// Query the write buffer size of the client's underlying socket.
165 pub fn getWriteBufferSize(self: Client) !u32 {
166 return self.socket.getWriteBufferSize();
167 }
168
169 /// Query the address that the client's socket is locally bounded to.
170 pub fn getLocalAddress(self: Client) !ip.Address {
171 return ip.Address.from(try self.socket.getLocalAddress());
172 }
173
174 /// Query the address that the socket is connected to.
175 pub fn getRemoteAddress(self: Client) !ip.Address {
176 return ip.Address.from(try self.socket.getRemoteAddress());
177 }
178
179 /// Have close() or shutdown() syscalls block until all queued messages in the client have been successfully
180 /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
181 /// if the host does not support the option for a socket to linger around up until a timeout specified in
182 /// seconds.
183 pub fn setLinger(self: Client, timeout_seconds: ?u16) !void {
184 return self.socket.setLinger(timeout_seconds);
185 }
186
187 /// Have keep-alive messages be sent periodically. The timing in which keep-alive messages are sent are
188 /// dependant on operating system settings. It returns `error.UnsupportedSocketOption` if the host does
189 /// not support periodically sending keep-alive messages on connection-oriented sockets.
190 pub fn setKeepAlive(self: Client, enabled: bool) !void {
191 return self.socket.setKeepAlive(enabled);
192 }
193
194 /// Disable Nagle's algorithm on a TCP socket. It returns `error.UnsupportedSocketOption` if
195 /// the host does not support sockets disabling Nagle's algorithm.
196 pub fn setNoDelay(self: Client, enabled: bool) !void {
197 if (@hasDecl(os.TCP, "NODELAY")) {
198 const bytes = mem.asBytes(&@as(usize, @boolToInt(enabled)));
199 return self.socket.setOption(os.IPPROTO.TCP, os.TCP.NODELAY, bytes);
200 }
201 return error.UnsupportedSocketOption;
202 }
203
204 /// Enables TCP Quick ACK on a TCP socket to immediately send rather than delay ACKs when necessary. It returns
205 /// `error.UnsupportedSocketOption` if the host does not support TCP Quick ACK.
206 pub fn setQuickACK(self: Client, enabled: bool) !void {
207 if (@hasDecl(os.TCP, "QUICKACK")) {
208 return self.socket.setOption(os.IPPROTO.TCP, os.TCP.QUICKACK, mem.asBytes(&@as(u32, @boolToInt(enabled))));
209 }
210 return error.UnsupportedSocketOption;
211 }
212
213 /// Set the write buffer size of the socket.
214 pub fn setWriteBufferSize(self: Client, size: u32) !void {
215 return self.socket.setWriteBufferSize(size);
216 }
217
218 /// Set the read buffer size of the socket.
219 pub fn setReadBufferSize(self: Client, size: u32) !void {
220 return self.socket.setReadBufferSize(size);
221 }
222
223 /// Set a timeout on the socket that is to occur if no messages are successfully written
224 /// to its bound destination after a specified number of milliseconds. A subsequent write
225 /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
226 pub fn setWriteTimeout(self: Client, milliseconds: u32) !void {
227 return self.socket.setWriteTimeout(milliseconds);
228 }
229
230 /// Set a timeout on the socket that is to occur if no messages are successfully read
231 /// from its bound destination after a specified number of milliseconds. A subsequent
232 /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
233 /// exceeded.
234 pub fn setReadTimeout(self: Client, milliseconds: u32) !void {
235 return self.socket.setReadTimeout(milliseconds);
236 }
237};
238
239/// A TCP listener.
240pub const Listener = struct {
241 socket: Socket,
242
243 /// Opens a new listener.
244 pub fn init(domain: tcp.Domain, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Listener {
245 return Listener{
246 .socket = try Socket.init(
247 @enumToInt(domain),
248 os.SOCK.STREAM,
249 os.IPPROTO.TCP,
250 flags,
251 ),
252 };
253 }
254
255 /// Closes the listener.
256 pub fn deinit(self: Listener) void {
257 self.socket.deinit();
258 }
259
260 /// Shuts down the underlying listener's socket. The next subsequent call, or
261 /// a current pending call to accept() after shutdown is called will return
262 /// an error.
263 pub fn shutdown(self: Listener) !void {
264 return self.socket.shutdown(.recv);
265 }
266
267 /// Binds the listener's socket to an address.
268 pub fn bind(self: Listener, address: ip.Address) !void {
269 return self.socket.bind(address.into());
270 }
271
272 /// Start listening for incoming connections.
273 pub fn listen(self: Listener, max_backlog_size: u31) !void {
274 return self.socket.listen(max_backlog_size);
275 }
276
277 /// Accept a pending incoming connection queued to the kernel backlog
278 /// of the listener's socket.
279 pub fn accept(self: Listener, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !tcp.Connection {
280 return tcp.Connection.from(try self.socket.accept(flags));
281 }
282
283 /// Query and return the latest cached error on the listener's underlying socket.
284 pub fn getError(self: Client) !void {
285 return self.socket.getError();
286 }
287
288 /// Query the address that the listener's socket is locally bounded to.
289 pub fn getLocalAddress(self: Listener) !ip.Address {
290 return ip.Address.from(try self.socket.getLocalAddress());
291 }
292
293 /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
294 /// the host does not support sockets listening the same address.
295 pub fn setReuseAddress(self: Listener, enabled: bool) !void {
296 return self.socket.setReuseAddress(enabled);
297 }
298
299 /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
300 /// the host does not supports sockets listening on the same port.
301 pub fn setReusePort(self: Listener, enabled: bool) !void {
302 return self.socket.setReusePort(enabled);
303 }
304
305 /// Enables TCP Fast Open (RFC 7413) on a TCP socket. It returns `error.UnsupportedSocketOption` if the host does not
306 /// support TCP Fast Open.
307 pub fn setFastOpen(self: Listener, enabled: bool) !void {
308 if (@hasDecl(os.TCP, "FASTOPEN")) {
309 return self.socket.setOption(os.IPPROTO.TCP, os.TCP.FASTOPEN, mem.asBytes(&@as(u32, @boolToInt(enabled))));
310 }
311 return error.UnsupportedSocketOption;
312 }
313
314 /// Set a timeout on the listener that is to occur if no new incoming connections come in
315 /// after a specified number of milliseconds. A subsequent accept call to the listener
316 /// will thereafter return `error.WouldBlock` should the timeout be exceeded.
317 pub fn setAcceptTimeout(self: Listener, milliseconds: usize) !void {
318 return self.socket.setReadTimeout(milliseconds);
319 }
320};
321
322test "tcp: create client/listener pair" {
323 if (native_os.tag == .wasi) return error.SkipZigTest;
324
325 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
326 defer listener.deinit();
327
328 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
329 try listener.listen(128);
330
331 var binded_address = try listener.getLocalAddress();
332 switch (binded_address) {
333 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
334 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
335 }
336
337 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
338 defer client.deinit();
339
340 try client.connect(binded_address);
341
342 const conn = try listener.accept(.{ .close_on_exec = true });
343 defer conn.deinit();
344}
345
346test "tcp/client: 1ms read timeout" {
347 if (native_os.tag == .wasi) return error.SkipZigTest;
348
349 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
350 defer listener.deinit();
351
352 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
353 try listener.listen(128);
354
355 var binded_address = try listener.getLocalAddress();
356 switch (binded_address) {
357 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
358 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
359 }
360
361 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
362 defer client.deinit();
363
364 try client.connect(binded_address);
365 try client.setReadTimeout(1);
366
367 const conn = try listener.accept(.{ .close_on_exec = true });
368 defer conn.deinit();
369
370 var buf: [1]u8 = undefined;
371 try testing.expectError(error.WouldBlock, client.reader(0).read(&buf));
372}
373
374test "tcp/client: read and write multiple vectors" {
375 if (native_os.tag == .wasi) return error.SkipZigTest;
376
377 if (builtin.os.tag == .windows) {
378 // https://github.com/ziglang/zig/issues/13893
379 return error.SkipZigTest;
380 }
381
382 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
383 defer listener.deinit();
384
385 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
386 try listener.listen(128);
387
388 var binded_address = try listener.getLocalAddress();
389 switch (binded_address) {
390 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
391 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
392 }
393
394 const client = try tcp.Client.init(.ip, .{ .close_on_exec = true });
395 defer client.deinit();
396
397 try client.connect(binded_address);
398
399 const conn = try listener.accept(.{ .close_on_exec = true });
400 defer conn.deinit();
401
402 const message = "hello world";
403 _ = try conn.client.writeMessage(Socket.Message.fromBuffers(&[_]Buffer{
404 Buffer.from(message[0 .. message.len / 2]),
405 Buffer.from(message[message.len / 2 ..]),
406 }), 0);
407
408 var buf: [message.len + 1]u8 = undefined;
409 var msg = Socket.Message.fromBuffers(&[_]Buffer{
410 Buffer.from(buf[0 .. message.len / 2]),
411 Buffer.from(buf[message.len / 2 ..]),
412 });
413 _ = try client.readMessage(&msg, 0);
414
415 try testing.expectEqualStrings(message, buf[0..message.len]);
416}
417
418test "tcp/listener: bind to unspecified ipv4 address" {
419 if (native_os.tag == .wasi) return error.SkipZigTest;
420
421 const listener = try tcp.Listener.init(.ip, .{ .close_on_exec = true });
422 defer listener.deinit();
423
424 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
425 try listener.listen(128);
426
427 const address = try listener.getLocalAddress();
428 try testing.expect(address == .ipv4);
429}
430
431test "tcp/listener: bind to unspecified ipv6 address" {
432 if (native_os.tag == .wasi) return error.SkipZigTest;
433
434 if (builtin.os.tag == .windows) {
435 // https://github.com/ziglang/zig/issues/13893
436 return error.SkipZigTest;
437 }
438
439 const listener = try tcp.Listener.init(.ipv6, .{ .close_on_exec = true });
440 defer listener.deinit();
441
442 try listener.bind(ip.Address.initIPv6(IPv6.unspecified, 0));
443 try listener.listen(128);
444
445 const address = try listener.getLocalAddress();
446 try testing.expect(address == .ipv6);
447}
lib/std/x/os/io.zig deleted-224
......@@ -1,224 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3
4const os = std.os;
5const mem = std.mem;
6const testing = std.testing;
7const native_os = builtin.os;
8const linux = std.os.linux;
9
10/// POSIX `iovec`, or Windows `WSABUF`. The difference between the two are the ordering
11/// of fields, alongside the length being represented as either a ULONG or a size_t.
12pub const Buffer = if (native_os.tag == .windows)
13 extern struct {
14 len: c_ulong,
15 ptr: usize,
16
17 pub fn from(slice: []const u8) Buffer {
18 return .{ .len = @intCast(c_ulong, slice.len), .ptr = @ptrToInt(slice.ptr) };
19 }
20
21 pub fn into(self: Buffer) []const u8 {
22 return @intToPtr([*]const u8, self.ptr)[0..self.len];
23 }
24
25 pub fn intoMutable(self: Buffer) []u8 {
26 return @intToPtr([*]u8, self.ptr)[0..self.len];
27 }
28 }
29else
30 extern struct {
31 ptr: usize,
32 len: usize,
33
34 pub fn from(slice: []const u8) Buffer {
35 return .{ .ptr = @ptrToInt(slice.ptr), .len = slice.len };
36 }
37
38 pub fn into(self: Buffer) []const u8 {
39 return @intToPtr([*]const u8, self.ptr)[0..self.len];
40 }
41
42 pub fn intoMutable(self: Buffer) []u8 {
43 return @intToPtr([*]u8, self.ptr)[0..self.len];
44 }
45 };
46
47pub const Reactor = struct {
48 pub const InitFlags = enum {
49 close_on_exec,
50 };
51
52 pub const Event = struct {
53 data: usize,
54 is_error: bool,
55 is_hup: bool,
56 is_readable: bool,
57 is_writable: bool,
58 };
59
60 pub const Interest = struct {
61 hup: bool = false,
62 oneshot: bool = false,
63 readable: bool = false,
64 writable: bool = false,
65 };
66
67 fd: os.fd_t,
68
69 pub fn init(flags: std.enums.EnumFieldStruct(Reactor.InitFlags, bool, false)) !Reactor {
70 var raw_flags: u32 = 0;
71 const set = std.EnumSet(Reactor.InitFlags).init(flags);
72 if (set.contains(.close_on_exec)) raw_flags |= linux.EPOLL.CLOEXEC;
73 return Reactor{ .fd = try os.epoll_create1(raw_flags) };
74 }
75
76 pub fn deinit(self: Reactor) void {
77 os.close(self.fd);
78 }
79
80 pub fn update(self: Reactor, fd: os.fd_t, identifier: usize, interest: Reactor.Interest) !void {
81 var flags: u32 = 0;
82 flags |= if (interest.oneshot) linux.EPOLL.ONESHOT else linux.EPOLL.ET;
83 if (interest.hup) flags |= linux.EPOLL.RDHUP;
84 if (interest.readable) flags |= linux.EPOLL.IN;
85 if (interest.writable) flags |= linux.EPOLL.OUT;
86
87 const event = &linux.epoll_event{
88 .events = flags,
89 .data = .{ .ptr = identifier },
90 };
91
92 os.epoll_ctl(self.fd, linux.EPOLL.CTL_MOD, fd, event) catch |err| switch (err) {
93 error.FileDescriptorNotRegistered => try os.epoll_ctl(self.fd, linux.EPOLL.CTL_ADD, fd, event),
94 else => return err,
95 };
96 }
97
98 pub fn remove(self: Reactor, fd: os.fd_t) !void {
99 // directly from man epoll_ctl BUGS section
100 // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation re‐
101 // quired a non-null pointer in event, even though this argument is
102 // ignored. Since Linux 2.6.9, event can be specified as NULL when
103 // using EPOLL_CTL_DEL. Applications that need to be portable to
104 // kernels before 2.6.9 should specify a non-null pointer in event.
105 var event = linux.epoll_event{
106 .events = 0,
107 .data = .{ .ptr = 0 },
108 };
109
110 return os.epoll_ctl(self.fd, linux.EPOLL.CTL_DEL, fd, &event);
111 }
112
113 pub fn poll(self: Reactor, comptime max_num_events: comptime_int, closure: anytype, timeout_milliseconds: ?u64) !void {
114 var events: [max_num_events]linux.epoll_event = undefined;
115
116 const num_events = os.epoll_wait(self.fd, &events, if (timeout_milliseconds) |ms| @intCast(i32, ms) else -1);
117 for (events[0..num_events]) |ev| {
118 const is_error = ev.events & linux.EPOLL.ERR != 0;
119 const is_hup = ev.events & (linux.EPOLL.HUP | linux.EPOLL.RDHUP) != 0;
120 const is_readable = ev.events & linux.EPOLL.IN != 0;
121 const is_writable = ev.events & linux.EPOLL.OUT != 0;
122
123 try closure.call(Reactor.Event{
124 .data = ev.data.ptr,
125 .is_error = is_error,
126 .is_hup = is_hup,
127 .is_readable = is_readable,
128 .is_writable = is_writable,
129 });
130 }
131 }
132};
133
134test "reactor/linux: drive async tcp client/listener pair" {
135 if (native_os.tag != .linux) return error.SkipZigTest;
136
137 const ip = std.x.net.ip;
138 const tcp = std.x.net.tcp;
139
140 const IPv4 = std.x.os.IPv4;
141 const IPv6 = std.x.os.IPv6;
142
143 const reactor = try Reactor.init(.{ .close_on_exec = true });
144 defer reactor.deinit();
145
146 const listener = try tcp.Listener.init(.ip, .{
147 .close_on_exec = true,
148 .nonblocking = true,
149 });
150 defer listener.deinit();
151
152 try reactor.update(listener.socket.fd, 0, .{ .readable = true });
153 try reactor.poll(1, struct {
154 fn call(event: Reactor.Event) !void {
155 try testing.expectEqual(Reactor.Event{
156 .data = 0,
157 .is_error = false,
158 .is_hup = true,
159 .is_readable = false,
160 .is_writable = false,
161 }, event);
162 }
163 }, null);
164
165 try listener.bind(ip.Address.initIPv4(IPv4.unspecified, 0));
166 try listener.listen(128);
167
168 var binded_address = try listener.getLocalAddress();
169 switch (binded_address) {
170 .ipv4 => |*ipv4| ipv4.host = IPv4.localhost,
171 .ipv6 => |*ipv6| ipv6.host = IPv6.localhost,
172 }
173
174 const client = try tcp.Client.init(.ip, .{
175 .close_on_exec = true,
176 .nonblocking = true,
177 });
178 defer client.deinit();
179
180 try reactor.update(client.socket.fd, 1, .{ .readable = true, .writable = true });
181 try reactor.poll(1, struct {
182 fn call(event: Reactor.Event) !void {
183 try testing.expectEqual(Reactor.Event{
184 .data = 1,
185 .is_error = false,
186 .is_hup = true,
187 .is_readable = false,
188 .is_writable = true,
189 }, event);
190 }
191 }, null);
192
193 client.connect(binded_address) catch |err| switch (err) {
194 error.WouldBlock => {},
195 else => return err,
196 };
197
198 try reactor.poll(1, struct {
199 fn call(event: Reactor.Event) !void {
200 try testing.expectEqual(Reactor.Event{
201 .data = 1,
202 .is_error = false,
203 .is_hup = false,
204 .is_readable = false,
205 .is_writable = true,
206 }, event);
207 }
208 }, null);
209
210 try reactor.poll(1, struct {
211 fn call(event: Reactor.Event) !void {
212 try testing.expectEqual(Reactor.Event{
213 .data = 0,
214 .is_error = false,
215 .is_hup = false,
216 .is_readable = true,
217 .is_writable = false,
218 }, event);
219 }
220 }, null);
221
222 try reactor.remove(client.socket.fd);
223 try reactor.remove(listener.socket.fd);
224}
lib/std/x/os/net.zig deleted-605
......@@ -1,605 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3
4const os = std.os;
5const fmt = std.fmt;
6const mem = std.mem;
7const math = std.math;
8const testing = std.testing;
9const native_os = builtin.os;
10const have_ifnamesize = @hasDecl(os.system, "IFNAMESIZE");
11
12pub const ResolveScopeIdError = error{
13 NameTooLong,
14 PermissionDenied,
15 AddressFamilyNotSupported,
16 ProtocolFamilyNotAvailable,
17 ProcessFdQuotaExceeded,
18 SystemFdQuotaExceeded,
19 SystemResources,
20 ProtocolNotSupported,
21 SocketTypeNotSupported,
22 InterfaceNotFound,
23 FileSystem,
24 Unexpected,
25};
26
27/// Resolves a network interface name into a scope/zone ID. It returns
28/// an error if either resolution fails, or if the interface name is
29/// too long.
30pub fn resolveScopeId(name: []const u8) ResolveScopeIdError!u32 {
31 if (have_ifnamesize) {
32 if (name.len >= os.IFNAMESIZE) return error.NameTooLong;
33
34 if (native_os.tag == .windows or comptime native_os.tag.isDarwin()) {
35 var interface_name: [os.IFNAMESIZE:0]u8 = undefined;
36 mem.copy(u8, &interface_name, name);
37 interface_name[name.len] = 0;
38
39 const rc = blk: {
40 if (native_os.tag == .windows) {
41 break :blk os.windows.ws2_32.if_nametoindex(@ptrCast([*:0]const u8, &interface_name));
42 } else {
43 const index = os.system.if_nametoindex(@ptrCast([*:0]const u8, &interface_name));
44 break :blk @bitCast(u32, index);
45 }
46 };
47 if (rc == 0) {
48 return error.InterfaceNotFound;
49 }
50 return rc;
51 }
52
53 if (native_os.tag == .linux) {
54 const fd = try os.socket(os.AF.INET, os.SOCK.DGRAM, 0);
55 defer os.closeSocket(fd);
56
57 var f: os.ifreq = undefined;
58 mem.copy(u8, &f.ifrn.name, name);
59 f.ifrn.name[name.len] = 0;
60
61 try os.ioctl_SIOCGIFINDEX(fd, &f);
62
63 return @bitCast(u32, f.ifru.ivalue);
64 }
65 }
66
67 return error.InterfaceNotFound;
68}
69
70/// An IPv4 address comprised of 4 bytes.
71pub const IPv4 = extern struct {
72 /// A IPv4 host-port pair.
73 pub const Address = extern struct {
74 host: IPv4,
75 port: u16,
76 };
77
78 /// Octets of a IPv4 address designating the local host.
79 pub const localhost_octets = [_]u8{ 127, 0, 0, 1 };
80
81 /// The IPv4 address of the local host.
82 pub const localhost: IPv4 = .{ .octets = localhost_octets };
83
84 /// Octets of an unspecified IPv4 address.
85 pub const unspecified_octets = [_]u8{0} ** 4;
86
87 /// An unspecified IPv4 address.
88 pub const unspecified: IPv4 = .{ .octets = unspecified_octets };
89
90 /// Octets of a broadcast IPv4 address.
91 pub const broadcast_octets = [_]u8{255} ** 4;
92
93 /// An IPv4 broadcast address.
94 pub const broadcast: IPv4 = .{ .octets = broadcast_octets };
95
96 /// The prefix octet pattern of a link-local IPv4 address.
97 pub const link_local_prefix = [_]u8{ 169, 254 };
98
99 /// The prefix octet patterns of IPv4 addresses intended for
100 /// documentation.
101 pub const documentation_prefixes = [_][]const u8{
102 &[_]u8{ 192, 0, 2 },
103 &[_]u8{ 198, 51, 100 },
104 &[_]u8{ 203, 0, 113 },
105 };
106
107 octets: [4]u8,
108
109 /// Returns whether or not the two addresses are equal to, less than, or
110 /// greater than each other.
111 pub fn cmp(self: IPv4, other: IPv4) math.Order {
112 return mem.order(u8, &self.octets, &other.octets);
113 }
114
115 /// Returns true if both addresses are semantically equivalent.
116 pub fn eql(self: IPv4, other: IPv4) bool {
117 return mem.eql(u8, &self.octets, &other.octets);
118 }
119
120 /// Returns true if the address is a loopback address.
121 pub fn isLoopback(self: IPv4) bool {
122 return self.octets[0] == 127;
123 }
124
125 /// Returns true if the address is an unspecified IPv4 address.
126 pub fn isUnspecified(self: IPv4) bool {
127 return mem.eql(u8, &self.octets, &unspecified_octets);
128 }
129
130 /// Returns true if the address is a private IPv4 address.
131 pub fn isPrivate(self: IPv4) bool {
132 return self.octets[0] == 10 or
133 (self.octets[0] == 172 and self.octets[1] >= 16 and self.octets[1] <= 31) or
134 (self.octets[0] == 192 and self.octets[1] == 168);
135 }
136
137 /// Returns true if the address is a link-local IPv4 address.
138 pub fn isLinkLocal(self: IPv4) bool {
139 return mem.startsWith(u8, &self.octets, &link_local_prefix);
140 }
141
142 /// Returns true if the address is a multicast IPv4 address.
143 pub fn isMulticast(self: IPv4) bool {
144 return self.octets[0] >= 224 and self.octets[0] <= 239;
145 }
146
147 /// Returns true if the address is a IPv4 broadcast address.
148 pub fn isBroadcast(self: IPv4) bool {
149 return mem.eql(u8, &self.octets, &broadcast_octets);
150 }
151
152 /// Returns true if the address is in a range designated for documentation. Refer
153 /// to IETF RFC 5737 for more details.
154 pub fn isDocumentation(self: IPv4) bool {
155 inline for (documentation_prefixes) |prefix| {
156 if (mem.startsWith(u8, &self.octets, prefix)) {
157 return true;
158 }
159 }
160 return false;
161 }
162
163 /// Implements the `std.fmt.format` API.
164 pub fn format(
165 self: IPv4,
166 comptime layout: []const u8,
167 opts: fmt.FormatOptions,
168 writer: anytype,
169 ) !void {
170 _ = opts;
171 if (layout.len != 0) std.fmt.invalidFmtError(layout, self);
172
173 try fmt.format(writer, "{}.{}.{}.{}", .{
174 self.octets[0],
175 self.octets[1],
176 self.octets[2],
177 self.octets[3],
178 });
179 }
180
181 /// Set of possible errors that may encountered when parsing an IPv4
182 /// address.
183 pub const ParseError = error{
184 UnexpectedEndOfOctet,
185 TooManyOctets,
186 OctetOverflow,
187 UnexpectedToken,
188 IncompleteAddress,
189 };
190
191 /// Parses an arbitrary IPv4 address.
192 pub fn parse(buf: []const u8) ParseError!IPv4 {
193 var octets: [4]u8 = undefined;
194 var octet: u8 = 0;
195
196 var index: u8 = 0;
197 var saw_any_digits: bool = false;
198
199 for (buf) |c| {
200 switch (c) {
201 '.' => {
202 if (!saw_any_digits) return error.UnexpectedEndOfOctet;
203 if (index == 3) return error.TooManyOctets;
204 octets[index] = octet;
205 index += 1;
206 octet = 0;
207 saw_any_digits = false;
208 },
209 '0'...'9' => {
210 saw_any_digits = true;
211 octet = math.mul(u8, octet, 10) catch return error.OctetOverflow;
212 octet = math.add(u8, octet, c - '0') catch return error.OctetOverflow;
213 },
214 else => return error.UnexpectedToken,
215 }
216 }
217
218 if (index == 3 and saw_any_digits) {
219 octets[index] = octet;
220 return IPv4{ .octets = octets };
221 }
222
223 return error.IncompleteAddress;
224 }
225
226 /// Maps the address to its IPv6 equivalent. In most cases, you would
227 /// want to map the address to its IPv6 equivalent rather than directly
228 /// re-interpreting the address.
229 pub fn mapToIPv6(self: IPv4) IPv6 {
230 var octets: [16]u8 = undefined;
231 mem.copy(u8, octets[0..12], &IPv6.v4_mapped_prefix);
232 mem.copy(u8, octets[12..], &self.octets);
233 return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id };
234 }
235
236 /// Directly re-interprets the address to its IPv6 equivalent. In most
237 /// cases, you would want to map the address to its IPv6 equivalent rather
238 /// than directly re-interpreting the address.
239 pub fn toIPv6(self: IPv4) IPv6 {
240 var octets: [16]u8 = undefined;
241 mem.set(u8, octets[0..12], 0);
242 mem.copy(u8, octets[12..], &self.octets);
243 return IPv6{ .octets = octets, .scope_id = IPv6.no_scope_id };
244 }
245};
246
247/// An IPv6 address comprised of 16 bytes for an address, and 4 bytes
248/// for a scope ID; cumulatively summing to 20 bytes in total.
249pub const IPv6 = extern struct {
250 /// A IPv6 host-port pair.
251 pub const Address = extern struct {
252 host: IPv6,
253 port: u16,
254 };
255
256 /// Octets of a IPv6 address designating the local host.
257 pub const localhost_octets = [_]u8{0} ** 15 ++ [_]u8{0x01};
258
259 /// The IPv6 address of the local host.
260 pub const localhost: IPv6 = .{
261 .octets = localhost_octets,
262 .scope_id = no_scope_id,
263 };
264
265 /// Octets of an unspecified IPv6 address.
266 pub const unspecified_octets = [_]u8{0} ** 16;
267
268 /// An unspecified IPv6 address.
269 pub const unspecified: IPv6 = .{
270 .octets = unspecified_octets,
271 .scope_id = no_scope_id,
272 };
273
274 /// The prefix of a IPv6 address that is mapped to a IPv4 address.
275 pub const v4_mapped_prefix = [_]u8{0} ** 10 ++ [_]u8{0xFF} ** 2;
276
277 /// A marker value used to designate an IPv6 address with no
278 /// associated scope ID.
279 pub const no_scope_id = math.maxInt(u32);
280
281 octets: [16]u8,
282 scope_id: u32,
283
284 /// Returns whether or not the two addresses are equal to, less than, or
285 /// greater than each other.
286 pub fn cmp(self: IPv6, other: IPv6) math.Order {
287 return switch (mem.order(u8, self.octets, other.octets)) {
288 .eq => math.order(self.scope_id, other.scope_id),
289 else => |order| order,
290 };
291 }
292
293 /// Returns true if both addresses are semantically equivalent.
294 pub fn eql(self: IPv6, other: IPv6) bool {
295 return self.scope_id == other.scope_id and mem.eql(u8, &self.octets, &other.octets);
296 }
297
298 /// Returns true if the address is an unspecified IPv6 address.
299 pub fn isUnspecified(self: IPv6) bool {
300 return mem.eql(u8, &self.octets, &unspecified_octets);
301 }
302
303 /// Returns true if the address is a loopback address.
304 pub fn isLoopback(self: IPv6) bool {
305 return mem.eql(u8, self.octets[0..3], &[_]u8{ 0, 0, 0 }) and
306 mem.eql(u8, self.octets[12..], &[_]u8{ 0, 0, 0, 1 });
307 }
308
309 /// Returns true if the address maps to an IPv4 address.
310 pub fn mapsToIPv4(self: IPv6) bool {
311 return mem.startsWith(u8, &self.octets, &v4_mapped_prefix);
312 }
313
314 /// Returns an IPv4 address representative of the address should
315 /// it the address be mapped to an IPv4 address. It returns null
316 /// otherwise.
317 pub fn toIPv4(self: IPv6) ?IPv4 {
318 if (!self.mapsToIPv4()) return null;
319 return IPv4{ .octets = self.octets[12..][0..4].* };
320 }
321
322 /// Returns true if the address is a multicast IPv6 address.
323 pub fn isMulticast(self: IPv6) bool {
324 return self.octets[0] == 0xFF;
325 }
326
327 /// Returns true if the address is a unicast link local IPv6 address.
328 pub fn isLinkLocal(self: IPv6) bool {
329 return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0x80;
330 }
331
332 /// Returns true if the address is a deprecated unicast site local
333 /// IPv6 address. Refer to IETF RFC 3879 for more details as to
334 /// why they are deprecated.
335 pub fn isSiteLocal(self: IPv6) bool {
336 return self.octets[0] == 0xFE and self.octets[1] & 0xC0 == 0xC0;
337 }
338
339 /// IPv6 multicast address scopes.
340 pub const Scope = enum(u8) {
341 interface = 1,
342 link = 2,
343 realm = 3,
344 admin = 4,
345 site = 5,
346 organization = 8,
347 global = 14,
348 unknown = 0xFF,
349 };
350
351 /// Returns the multicast scope of the address.
352 pub fn scope(self: IPv6) Scope {
353 if (!self.isMulticast()) return .unknown;
354
355 return switch (self.octets[0] & 0x0F) {
356 1 => .interface,
357 2 => .link,
358 3 => .realm,
359 4 => .admin,
360 5 => .site,
361 8 => .organization,
362 14 => .global,
363 else => .unknown,
364 };
365 }
366
367 /// Implements the `std.fmt.format` API. Specifying 'x' or 's' formats the
368 /// address lower-cased octets, while specifying 'X' or 'S' formats the
369 /// address using upper-cased ASCII octets.
370 ///
371 /// The default specifier is 'x'.
372 pub fn format(
373 self: IPv6,
374 comptime layout: []const u8,
375 opts: fmt.FormatOptions,
376 writer: anytype,
377 ) !void {
378 _ = opts;
379 const specifier = comptime &[_]u8{if (layout.len == 0) 'x' else switch (layout[0]) {
380 'x', 'X' => |specifier| specifier,
381 's' => 'x',
382 'S' => 'X',
383 else => std.fmt.invalidFmtError(layout, self),
384 }};
385
386 if (mem.startsWith(u8, &self.octets, &v4_mapped_prefix)) {
387 return fmt.format(writer, "::{" ++ specifier ++ "}{" ++ specifier ++ "}:{}.{}.{}.{}", .{
388 0xFF,
389 0xFF,
390 self.octets[12],
391 self.octets[13],
392 self.octets[14],
393 self.octets[15],
394 });
395 }
396
397 const zero_span: struct { from: usize, to: usize } = span: {
398 var i: usize = 0;
399 while (i < self.octets.len) : (i += 2) {
400 if (self.octets[i] == 0 and self.octets[i + 1] == 0) break;
401 } else break :span .{ .from = 0, .to = 0 };
402
403 const from = i;
404
405 while (i < self.octets.len) : (i += 2) {
406 if (self.octets[i] != 0 or self.octets[i + 1] != 0) break;
407 }
408
409 break :span .{ .from = from, .to = i };
410 };
411
412 var i: usize = 0;
413 while (i != 16) : (i += 2) {
414 if (zero_span.from != zero_span.to and i == zero_span.from) {
415 try writer.writeAll("::");
416 } else if (i >= zero_span.from and i < zero_span.to) {} else {
417 if (i != 0 and i != zero_span.to) try writer.writeAll(":");
418
419 const val = @as(u16, self.octets[i]) << 8 | self.octets[i + 1];
420 try fmt.formatIntValue(val, specifier, .{}, writer);
421 }
422 }
423
424 if (self.scope_id != no_scope_id and self.scope_id != 0) {
425 try fmt.format(writer, "%{d}", .{self.scope_id});
426 }
427 }
428
429 /// Set of possible errors that may encountered when parsing an IPv6
430 /// address.
431 pub const ParseError = error{
432 MalformedV4Mapping,
433 InterfaceNotFound,
434 UnknownScopeId,
435 } || IPv4.ParseError;
436
437 /// Parses an arbitrary IPv6 address, including link-local addresses.
438 pub fn parse(buf: []const u8) ParseError!IPv6 {
439 if (mem.lastIndexOfScalar(u8, buf, '%')) |index| {
440 const ip_slice = buf[0..index];
441 const scope_id_slice = buf[index + 1 ..];
442
443 if (scope_id_slice.len == 0) return error.UnknownScopeId;
444
445 const scope_id: u32 = switch (scope_id_slice[0]) {
446 '0'...'9' => fmt.parseInt(u32, scope_id_slice, 10),
447 else => resolveScopeId(scope_id_slice) catch |err| switch (err) {
448 error.InterfaceNotFound => return error.InterfaceNotFound,
449 else => err,
450 },
451 } catch return error.UnknownScopeId;
452
453 return parseWithScopeID(ip_slice, scope_id);
454 }
455
456 return parseWithScopeID(buf, no_scope_id);
457 }
458
459 /// Parses an IPv6 address with a pre-specified scope ID. Presumes
460 /// that the address is not a link-local address.
461 pub fn parseWithScopeID(buf: []const u8, scope_id: u32) ParseError!IPv6 {
462 var octets: [16]u8 = undefined;
463 var octet: u16 = 0;
464 var tail: [16]u8 = undefined;
465
466 var out: []u8 = &octets;
467 var index: u8 = 0;
468
469 var saw_any_digits: bool = false;
470 var abbrv: bool = false;
471
472 for (buf) |c, i| {
473 switch (c) {
474 ':' => {
475 if (!saw_any_digits) {
476 if (abbrv) return error.UnexpectedToken;
477 if (i != 0) abbrv = true;
478 mem.set(u8, out[index..], 0);
479 out = &tail;
480 index = 0;
481 continue;
482 }
483 if (index == 14) return error.TooManyOctets;
484
485 out[index] = @truncate(u8, octet >> 8);
486 index += 1;
487 out[index] = @truncate(u8, octet);
488 index += 1;
489
490 octet = 0;
491 saw_any_digits = false;
492 },
493 '.' => {
494 if (!abbrv or out[0] != 0xFF and out[1] != 0xFF) {
495 return error.MalformedV4Mapping;
496 }
497 const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
498 const v4 = try IPv4.parse(buf[start_index..]);
499 octets[10] = 0xFF;
500 octets[11] = 0xFF;
501 mem.copy(u8, octets[12..], &v4.octets);
502
503 return IPv6{ .octets = octets, .scope_id = scope_id };
504 },
505 else => {
506 saw_any_digits = true;
507 const digit = fmt.charToDigit(c, 16) catch return error.UnexpectedToken;
508 octet = math.mul(u16, octet, 16) catch return error.OctetOverflow;
509 octet = math.add(u16, octet, digit) catch return error.OctetOverflow;
510 },
511 }
512 }
513
514 if (!saw_any_digits and !abbrv) {
515 return error.IncompleteAddress;
516 }
517
518 if (index == 14) {
519 out[14] = @truncate(u8, octet >> 8);
520 out[15] = @truncate(u8, octet);
521 } else {
522 out[index] = @truncate(u8, octet >> 8);
523 index += 1;
524 out[index] = @truncate(u8, octet);
525 index += 1;
526 mem.copy(u8, octets[16 - index ..], out[0..index]);
527 }
528
529 return IPv6{ .octets = octets, .scope_id = scope_id };
530 }
531};
532
533test {
534 testing.refAllDecls(@This());
535}
536
537test "ip: convert to and from ipv6" {
538 try testing.expectFmt("::7f00:1", "{}", .{IPv4.localhost.toIPv6()});
539 try testing.expect(!IPv4.localhost.toIPv6().mapsToIPv4());
540
541 try testing.expectFmt("::ffff:127.0.0.1", "{}", .{IPv4.localhost.mapToIPv6()});
542 try testing.expect(IPv4.localhost.mapToIPv6().mapsToIPv4());
543
544 try testing.expect(IPv4.localhost.toIPv6().toIPv4() == null);
545 try testing.expectFmt("127.0.0.1", "{?}", .{IPv4.localhost.mapToIPv6().toIPv4()});
546}
547
548test "ipv4: parse & format" {
549 const cases = [_][]const u8{
550 "0.0.0.0",
551 "255.255.255.255",
552 "1.2.3.4",
553 "123.255.0.91",
554 "127.0.0.1",
555 };
556
557 for (cases) |case| {
558 try testing.expectFmt(case, "{}", .{try IPv4.parse(case)});
559 }
560}
561
562test "ipv6: parse & format" {
563 const inputs = [_][]const u8{
564 "FF01:0:0:0:0:0:0:FB",
565 "FF01::Fb",
566 "::1",
567 "::",
568 "2001:db8::",
569 "::1234:5678",
570 "2001:db8::1234:5678",
571 "::ffff:123.5.123.5",
572 };
573
574 const outputs = [_][]const u8{
575 "ff01::fb",
576 "ff01::fb",
577 "::1",
578 "::",
579 "2001:db8::",
580 "::1234:5678",
581 "2001:db8::1234:5678",
582 "::ffff:123.5.123.5",
583 };
584
585 for (inputs) |input, i| {
586 try testing.expectFmt(outputs[i], "{}", .{try IPv6.parse(input)});
587 }
588}
589
590test "ipv6: parse & format addresses with scope ids" {
591 if (!have_ifnamesize) return error.SkipZigTest;
592 const iface = if (native_os.tag == .linux)
593 "lo"
594 else
595 "lo0";
596 const input = "FF01::FB%" ++ iface;
597 const output = "ff01::fb%1";
598
599 const parsed = IPv6.parse(input) catch |err| switch (err) {
600 error.InterfaceNotFound => return,
601 else => return err,
602 };
603
604 try testing.expectFmt(output, "{}", .{parsed});
605}
lib/std/x/os/socket.zig deleted-320
......@@ -1,320 +0,0 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3const net = @import("net.zig");
4
5const os = std.os;
6const fmt = std.fmt;
7const mem = std.mem;
8const time = std.time;
9const meta = std.meta;
10const native_os = builtin.os;
11const native_endian = builtin.cpu.arch.endian();
12
13const Buffer = std.x.os.Buffer;
14
15const assert = std.debug.assert;
16
17/// A generic, cross-platform socket abstraction.
18pub const Socket = struct {
19 /// A socket-address pair.
20 pub const Connection = struct {
21 socket: Socket,
22 address: Socket.Address,
23
24 /// Enclose a socket and address into a socket-address pair.
25 pub fn from(socket: Socket, address: Socket.Address) Socket.Connection {
26 return .{ .socket = socket, .address = address };
27 }
28 };
29
30 /// A generic socket address abstraction. It is safe to directly access and modify
31 /// the fields of a `Socket.Address`.
32 pub const Address = union(enum) {
33 pub const Native = struct {
34 pub const requires_prepended_length = native_os.getVersionRange() == .semver;
35 pub const Length = if (requires_prepended_length) u8 else [0]u8;
36
37 pub const Family = if (requires_prepended_length) u8 else c_ushort;
38
39 /// POSIX `sockaddr.storage`. The expected size and alignment is specified in IETF RFC 2553.
40 pub const Storage = extern struct {
41 pub const expected_size = os.sockaddr.SS_MAXSIZE;
42 pub const expected_alignment = 8;
43
44 pub const padding_size = expected_size -
45 mem.alignForward(@sizeOf(Address.Native.Length), expected_alignment) -
46 mem.alignForward(@sizeOf(Address.Native.Family), expected_alignment);
47
48 len: Address.Native.Length align(expected_alignment) = undefined,
49 family: Address.Native.Family align(expected_alignment) = undefined,
50 padding: [padding_size]u8 align(expected_alignment) = undefined,
51
52 comptime {
53 assert(@sizeOf(Storage) == Storage.expected_size);
54 assert(@alignOf(Storage) == Storage.expected_alignment);
55 }
56 };
57 };
58
59 ipv4: net.IPv4.Address,
60 ipv6: net.IPv6.Address,
61
62 /// Instantiate a new address with a IPv4 host and port.
63 pub fn initIPv4(host: net.IPv4, port: u16) Socket.Address {
64 return .{ .ipv4 = .{ .host = host, .port = port } };
65 }
66
67 /// Instantiate a new address with a IPv6 host and port.
68 pub fn initIPv6(host: net.IPv6, port: u16) Socket.Address {
69 return .{ .ipv6 = .{ .host = host, .port = port } };
70 }
71
72 /// Parses a `sockaddr` into a generic socket address.
73 pub fn fromNative(address: *align(4) const os.sockaddr) Socket.Address {
74 switch (address.family) {
75 os.AF.INET => {
76 const info = @ptrCast(*const os.sockaddr.in, address);
77 const host = net.IPv4{ .octets = @bitCast([4]u8, info.addr) };
78 const port = mem.bigToNative(u16, info.port);
79 return Socket.Address.initIPv4(host, port);
80 },
81 os.AF.INET6 => {
82 const info = @ptrCast(*const os.sockaddr.in6, address);
83 const host = net.IPv6{ .octets = info.addr, .scope_id = info.scope_id };
84 const port = mem.bigToNative(u16, info.port);
85 return Socket.Address.initIPv6(host, port);
86 },
87 else => unreachable,
88 }
89 }
90
91 /// Encodes a generic socket address into an extern union that may be reliably
92 /// casted into a `sockaddr` which may be passed into socket syscalls.
93 pub fn toNative(self: Socket.Address) extern union {
94 ipv4: os.sockaddr.in,
95 ipv6: os.sockaddr.in6,
96 } {
97 return switch (self) {
98 .ipv4 => |address| .{
99 .ipv4 = .{
100 .addr = @bitCast(u32, address.host.octets),
101 .port = mem.nativeToBig(u16, address.port),
102 },
103 },
104 .ipv6 => |address| .{
105 .ipv6 = .{
106 .addr = address.host.octets,
107 .port = mem.nativeToBig(u16, address.port),
108 .scope_id = address.host.scope_id,
109 .flowinfo = 0,
110 },
111 },
112 };
113 }
114
115 /// Returns the number of bytes that make up the `sockaddr` equivalent to the address.
116 pub fn getNativeSize(self: Socket.Address) u32 {
117 return switch (self) {
118 .ipv4 => @sizeOf(os.sockaddr.in),
119 .ipv6 => @sizeOf(os.sockaddr.in6),
120 };
121 }
122
123 /// Implements the `std.fmt.format` API.
124 pub fn format(
125 self: Socket.Address,
126 comptime layout: []const u8,
127 opts: fmt.FormatOptions,
128 writer: anytype,
129 ) !void {
130 if (layout.len != 0) std.fmt.invalidFmtError(layout, self);
131 _ = opts;
132 switch (self) {
133 .ipv4 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
134 .ipv6 => |address| try fmt.format(writer, "{}:{}", .{ address.host, address.port }),
135 }
136 }
137 };
138
139 /// POSIX `msghdr`. Denotes a destination address, set of buffers, control data, and flags. Ported
140 /// directly from musl.
141 pub const Message = if (native_os.isAtLeast(.windows, .vista) != null and native_os.isAtLeast(.windows, .vista).?)
142 extern struct {
143 name: usize = @ptrToInt(@as(?[*]u8, null)),
144 name_len: c_int = 0,
145
146 buffers: usize = undefined,
147 buffers_len: c_ulong = undefined,
148
149 control: Buffer = .{
150 .ptr = @ptrToInt(@as(?[*]u8, null)),
151 .len = 0,
152 },
153 flags: c_ulong = 0,
154
155 pub usingnamespace MessageMixin(Message);
156 }
157 else if (native_os.tag == .windows)
158 extern struct {
159 name: usize = @ptrToInt(@as(?[*]u8, null)),
160 name_len: c_int = 0,
161
162 buffers: usize = undefined,
163 buffers_len: u32 = undefined,
164
165 control: Buffer = .{
166 .ptr = @ptrToInt(@as(?[*]u8, null)),
167 .len = 0,
168 },
169 flags: u32 = 0,
170
171 pub usingnamespace MessageMixin(Message);
172 }
173 else if (@sizeOf(usize) > 4 and native_endian == .Big)
174 extern struct {
175 name: usize = @ptrToInt(@as(?[*]u8, null)),
176 name_len: c_uint = 0,
177
178 buffers: usize = undefined,
179 _pad_1: c_int = 0,
180 buffers_len: c_int = undefined,
181
182 control: usize = @ptrToInt(@as(?[*]u8, null)),
183 _pad_2: c_int = 0,
184 control_len: c_uint = 0,
185
186 flags: c_int = 0,
187
188 pub usingnamespace MessageMixin(Message);
189 }
190 else if (@sizeOf(usize) > 4 and native_endian == .Little)
191 extern struct {
192 name: usize = @ptrToInt(@as(?[*]u8, null)),
193 name_len: c_uint = 0,
194
195 buffers: usize = undefined,
196 buffers_len: c_int = undefined,
197 _pad_1: c_int = 0,
198
199 control: usize = @ptrToInt(@as(?[*]u8, null)),
200 control_len: c_uint = 0,
201 _pad_2: c_int = 0,
202
203 flags: c_int = 0,
204
205 pub usingnamespace MessageMixin(Message);
206 }
207 else
208 extern struct {
209 name: usize = @ptrToInt(@as(?[*]u8, null)),
210 name_len: c_uint = 0,
211
212 buffers: usize = undefined,
213 buffers_len: c_int = undefined,
214
215 control: usize = @ptrToInt(@as(?[*]u8, null)),
216 control_len: c_uint = 0,
217
218 flags: c_int = 0,
219
220 pub usingnamespace MessageMixin(Message);
221 };
222
223 fn MessageMixin(comptime Self: type) type {
224 return struct {
225 pub fn fromBuffers(buffers: []const Buffer) Self {
226 var self: Self = .{};
227 self.setBuffers(buffers);
228 return self;
229 }
230
231 pub fn setName(self: *Self, name: []const u8) void {
232 self.name = @ptrToInt(name.ptr);
233 self.name_len = @intCast(meta.fieldInfo(Self, .name_len).type, name.len);
234 }
235
236 pub fn setBuffers(self: *Self, buffers: []const Buffer) void {
237 self.buffers = @ptrToInt(buffers.ptr);
238 self.buffers_len = @intCast(meta.fieldInfo(Self, .buffers_len).type, buffers.len);
239 }
240
241 pub fn setControl(self: *Self, control: []const u8) void {
242 if (native_os.tag == .windows) {
243 self.control = Buffer.from(control);
244 } else {
245 self.control = @ptrToInt(control.ptr);
246 self.control_len = @intCast(meta.fieldInfo(Self, .control_len).type, control.len);
247 }
248 }
249
250 pub fn setFlags(self: *Self, flags: u32) void {
251 self.flags = @intCast(meta.fieldInfo(Self, .flags).type, flags);
252 }
253
254 pub fn getName(self: Self) []const u8 {
255 return @intToPtr([*]const u8, self.name)[0..@intCast(usize, self.name_len)];
256 }
257
258 pub fn getBuffers(self: Self) []const Buffer {
259 return @intToPtr([*]const Buffer, self.buffers)[0..@intCast(usize, self.buffers_len)];
260 }
261
262 pub fn getControl(self: Self) []const u8 {
263 if (native_os.tag == .windows) {
264 return self.control.into();
265 } else {
266 return @intToPtr([*]const u8, self.control)[0..@intCast(usize, self.control_len)];
267 }
268 }
269
270 pub fn getFlags(self: Self) u32 {
271 return @intCast(u32, self.flags);
272 }
273 };
274 }
275
276 /// POSIX `linger`, denoting the linger settings of a socket.
277 ///
278 /// Microsoft's documentation and glibc denote the fields to be unsigned
279 /// short's on Windows, whereas glibc and musl denote the fields to be
280 /// int's on every other platform.
281 pub const Linger = extern struct {
282 pub const Field = switch (native_os.tag) {
283 .windows => c_ushort,
284 else => c_int,
285 };
286
287 enabled: Field,
288 timeout_seconds: Field,
289
290 pub fn init(timeout_seconds: ?u16) Socket.Linger {
291 return .{
292 .enabled = @intCast(Socket.Linger.Field, @boolToInt(timeout_seconds != null)),
293 .timeout_seconds = if (timeout_seconds) |seconds| @intCast(Socket.Linger.Field, seconds) else 0,
294 };
295 }
296 };
297
298 /// Possible set of flags to initialize a socket with.
299 pub const InitFlags = enum {
300 // Initialize a socket to be non-blocking.
301 nonblocking,
302
303 // Have a socket close itself on exec syscalls.
304 close_on_exec,
305 };
306
307 /// The underlying handle of a socket.
308 fd: os.socket_t,
309
310 /// Enclose a socket abstraction over an existing socket file descriptor.
311 pub fn from(fd: os.socket_t) Socket {
312 return Socket{ .fd = fd };
313 }
314
315 /// Mix in socket syscalls depending on the platform we are compiling against.
316 pub usingnamespace switch (native_os.tag) {
317 .windows => @import("socket_windows.zig"),
318 else => @import("socket_posix.zig"),
319 }.Mixin(Socket);
320};
lib/std/x/os/socket_posix.zig deleted-275
......@@ -1,275 +0,0 @@
1const std = @import("../../std.zig");
2
3const os = std.os;
4const mem = std.mem;
5const time = std.time;
6
7pub fn Mixin(comptime Socket: type) type {
8 return struct {
9 /// Open a new socket.
10 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
11 var raw_flags: u32 = socket_type;
12 const set = std.EnumSet(Socket.InitFlags).init(flags);
13 if (set.contains(.close_on_exec)) raw_flags |= os.SOCK.CLOEXEC;
14 if (set.contains(.nonblocking)) raw_flags |= os.SOCK.NONBLOCK;
15 return Socket{ .fd = try os.socket(domain, raw_flags, protocol) };
16 }
17
18 /// Closes the socket.
19 pub fn deinit(self: Socket) void {
20 os.closeSocket(self.fd);
21 }
22
23 /// Shutdown either the read side, write side, or all side of the socket.
24 pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
25 return os.shutdown(self.fd, how);
26 }
27
28 /// Binds the socket to an address.
29 pub fn bind(self: Socket, address: Socket.Address) !void {
30 return os.bind(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
31 }
32
33 /// Start listening for incoming connections on the socket.
34 pub fn listen(self: Socket, max_backlog_size: u31) !void {
35 return os.listen(self.fd, max_backlog_size);
36 }
37
38 /// Have the socket attempt to the connect to an address.
39 pub fn connect(self: Socket, address: Socket.Address) !void {
40 return os.connect(self.fd, @ptrCast(*const os.sockaddr, &address.toNative()), address.getNativeSize());
41 }
42
43 /// Accept a pending incoming connection queued to the kernel backlog
44 /// of the socket.
45 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
46 var address: Socket.Address.Native.Storage = undefined;
47 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
48
49 var raw_flags: u32 = 0;
50 const set = std.EnumSet(Socket.InitFlags).init(flags);
51 if (set.contains(.close_on_exec)) raw_flags |= os.SOCK.CLOEXEC;
52 if (set.contains(.nonblocking)) raw_flags |= os.SOCK.NONBLOCK;
53
54 const socket = Socket{ .fd = try os.accept(self.fd, @ptrCast(*os.sockaddr, &address), &address_len, raw_flags) };
55 const socket_address = Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
56
57 return Socket.Connection.from(socket, socket_address);
58 }
59
60 /// Read data from the socket into the buffer provided with a set of flags
61 /// specified. It returns the number of bytes read into the buffer provided.
62 pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
63 return os.recv(self.fd, buf, flags);
64 }
65
66 /// Write a buffer of data provided to the socket with a set of flags specified.
67 /// It returns the number of bytes that are written to the socket.
68 pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
69 return os.send(self.fd, buf, flags);
70 }
71
72 /// Writes multiple I/O vectors with a prepended message header to the socket
73 /// with a set of flags specified. It returns the number of bytes that are
74 /// written to the socket.
75 pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize {
76 while (true) {
77 const rc = os.system.sendmsg(self.fd, &msg, @intCast(c_int, flags));
78 return switch (os.errno(rc)) {
79 .SUCCESS => return @intCast(usize, rc),
80 .ACCES => error.AccessDenied,
81 .AGAIN => error.WouldBlock,
82 .ALREADY => error.FastOpenAlreadyInProgress,
83 .BADF => unreachable, // always a race condition
84 .CONNRESET => error.ConnectionResetByPeer,
85 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
86 .FAULT => unreachable, // An invalid user space address was specified for an argument.
87 .INTR => continue,
88 .INVAL => unreachable, // Invalid argument passed.
89 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
90 .MSGSIZE => error.MessageTooBig,
91 .NOBUFS => error.SystemResources,
92 .NOMEM => error.SystemResources,
93 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
94 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
95 .PIPE => error.BrokenPipe,
96 .AFNOSUPPORT => error.AddressFamilyNotSupported,
97 .LOOP => error.SymLinkLoop,
98 .NAMETOOLONG => error.NameTooLong,
99 .NOENT => error.FileNotFound,
100 .NOTDIR => error.NotDir,
101 .HOSTUNREACH => error.NetworkUnreachable,
102 .NETUNREACH => error.NetworkUnreachable,
103 .NOTCONN => error.SocketNotConnected,
104 .NETDOWN => error.NetworkSubsystemFailed,
105 else => |err| os.unexpectedErrno(err),
106 };
107 }
108 }
109
110 /// Read multiple I/O vectors with a prepended message header from the socket
111 /// with a set of flags specified. It returns the number of bytes that were
112 /// read into the buffer provided.
113 pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize {
114 while (true) {
115 const rc = os.system.recvmsg(self.fd, msg, @intCast(c_int, flags));
116 return switch (os.errno(rc)) {
117 .SUCCESS => @intCast(usize, rc),
118 .BADF => unreachable, // always a race condition
119 .FAULT => unreachable,
120 .INVAL => unreachable,
121 .NOTCONN => unreachable,
122 .NOTSOCK => unreachable,
123 .INTR => continue,
124 .AGAIN => error.WouldBlock,
125 .NOMEM => error.SystemResources,
126 .CONNREFUSED => error.ConnectionRefused,
127 .CONNRESET => error.ConnectionResetByPeer,
128 else => |err| os.unexpectedErrno(err),
129 };
130 }
131 }
132
133 /// Query the address that the socket is locally bounded to.
134 pub fn getLocalAddress(self: Socket) !Socket.Address {
135 var address: Socket.Address.Native.Storage = undefined;
136 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
137 try os.getsockname(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
138 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
139 }
140
141 /// Query the address that the socket is connected to.
142 pub fn getRemoteAddress(self: Socket) !Socket.Address {
143 var address: Socket.Address.Native.Storage = undefined;
144 var address_len: u32 = @sizeOf(Socket.Address.Native.Storage);
145 try os.getpeername(self.fd, @ptrCast(*os.sockaddr, &address), &address_len);
146 return Socket.Address.fromNative(@ptrCast(*os.sockaddr, &address));
147 }
148
149 /// Query and return the latest cached error on the socket.
150 pub fn getError(self: Socket) !void {
151 return os.getsockoptError(self.fd);
152 }
153
154 /// Query the read buffer size of the socket.
155 pub fn getReadBufferSize(self: Socket) !u32 {
156 var value: u32 = undefined;
157 var value_len: u32 = @sizeOf(u32);
158
159 const rc = os.system.getsockopt(self.fd, os.SOL.SOCKET, os.SO.RCVBUF, mem.asBytes(&value), &value_len);
160 return switch (os.errno(rc)) {
161 .SUCCESS => value,
162 .BADF => error.BadFileDescriptor,
163 .FAULT => error.InvalidAddressSpace,
164 .INVAL => error.InvalidSocketOption,
165 .NOPROTOOPT => error.UnknownSocketOption,
166 .NOTSOCK => error.NotASocket,
167 else => |err| os.unexpectedErrno(err),
168 };
169 }
170
171 /// Query the write buffer size of the socket.
172 pub fn getWriteBufferSize(self: Socket) !u32 {
173 var value: u32 = undefined;
174 var value_len: u32 = @sizeOf(u32);
175
176 const rc = os.system.getsockopt(self.fd, os.SOL.SOCKET, os.SO.SNDBUF, mem.asBytes(&value), &value_len);
177 return switch (os.errno(rc)) {
178 .SUCCESS => value,
179 .BADF => error.BadFileDescriptor,
180 .FAULT => error.InvalidAddressSpace,
181 .INVAL => error.InvalidSocketOption,
182 .NOPROTOOPT => error.UnknownSocketOption,
183 .NOTSOCK => error.NotASocket,
184 else => |err| os.unexpectedErrno(err),
185 };
186 }
187
188 /// Set a socket option.
189 pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
190 return os.setsockopt(self.fd, level, code, value);
191 }
192
193 /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
194 /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
195 /// if the host does not support the option for a socket to linger around up until a timeout specified in
196 /// seconds.
197 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
198 if (@hasDecl(os.SO, "LINGER")) {
199 const settings = Socket.Linger.init(timeout_seconds);
200 return self.setOption(os.SOL.SOCKET, os.SO.LINGER, mem.asBytes(&settings));
201 }
202
203 return error.UnsupportedSocketOption;
204 }
205
206 /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
207 /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
208 /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
209 pub fn setKeepAlive(self: Socket, enabled: bool) !void {
210 if (@hasDecl(os.SO, "KEEPALIVE")) {
211 return self.setOption(os.SOL.SOCKET, os.SO.KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
212 }
213 return error.UnsupportedSocketOption;
214 }
215
216 /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
217 /// the host does not support sockets listening the same address.
218 pub fn setReuseAddress(self: Socket, enabled: bool) !void {
219 if (@hasDecl(os.SO, "REUSEADDR")) {
220 return self.setOption(os.SOL.SOCKET, os.SO.REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
221 }
222 return error.UnsupportedSocketOption;
223 }
224
225 /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
226 /// the host does not supports sockets listening on the same port.
227 pub fn setReusePort(self: Socket, enabled: bool) !void {
228 if (@hasDecl(os.SO, "REUSEPORT")) {
229 return self.setOption(os.SOL.SOCKET, os.SO.REUSEPORT, mem.asBytes(&@as(u32, @boolToInt(enabled))));
230 }
231 return error.UnsupportedSocketOption;
232 }
233
234 /// Set the write buffer size of the socket.
235 pub fn setWriteBufferSize(self: Socket, size: u32) !void {
236 return self.setOption(os.SOL.SOCKET, os.SO.SNDBUF, mem.asBytes(&size));
237 }
238
239 /// Set the read buffer size of the socket.
240 pub fn setReadBufferSize(self: Socket, size: u32) !void {
241 return self.setOption(os.SOL.SOCKET, os.SO.RCVBUF, mem.asBytes(&size));
242 }
243
244 /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
245 /// set on a non-blocking socket.
246 ///
247 /// Set a timeout on the socket that is to occur if no messages are successfully written
248 /// to its bound destination after a specified number of milliseconds. A subsequent write
249 /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
250 pub fn setWriteTimeout(self: Socket, milliseconds: usize) !void {
251 const timeout = os.timeval{
252 .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
253 .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
254 };
255
256 return self.setOption(os.SOL.SOCKET, os.SO.SNDTIMEO, mem.asBytes(&timeout));
257 }
258
259 /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
260 /// set on a non-blocking socket.
261 ///
262 /// Set a timeout on the socket that is to occur if no messages are successfully read
263 /// from its bound destination after a specified number of milliseconds. A subsequent
264 /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
265 /// exceeded.
266 pub fn setReadTimeout(self: Socket, milliseconds: usize) !void {
267 const timeout = os.timeval{
268 .tv_sec = @intCast(i32, milliseconds / time.ms_per_s),
269 .tv_usec = @intCast(i32, (milliseconds % time.ms_per_s) * time.us_per_ms),
270 };
271
272 return self.setOption(os.SOL.SOCKET, os.SO.RCVTIMEO, mem.asBytes(&timeout));
273 }
274 };
275}
lib/std/x/os/socket_windows.zig deleted-458
......@@ -1,458 +0,0 @@
1const std = @import("../../std.zig");
2const net = @import("net.zig");
3
4const os = std.os;
5const mem = std.mem;
6
7const windows = std.os.windows;
8const ws2_32 = windows.ws2_32;
9
10pub fn Mixin(comptime Socket: type) type {
11 return struct {
12 /// Open a new socket.
13 pub fn init(domain: u32, socket_type: u32, protocol: u32, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket {
14 var raw_flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED;
15 const set = std.EnumSet(Socket.InitFlags).init(flags);
16 if (set.contains(.close_on_exec)) raw_flags |= ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
17
18 const fd = ws2_32.WSASocketW(
19 @intCast(i32, domain),
20 @intCast(i32, socket_type),
21 @intCast(i32, protocol),
22 null,
23 0,
24 raw_flags,
25 );
26 if (fd == ws2_32.INVALID_SOCKET) {
27 return switch (ws2_32.WSAGetLastError()) {
28 .WSANOTINITIALISED => {
29 _ = try windows.WSAStartup(2, 2);
30 return init(domain, socket_type, protocol, flags);
31 },
32 .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
33 .WSAEMFILE => error.ProcessFdQuotaExceeded,
34 .WSAENOBUFS => error.SystemResources,
35 .WSAEPROTONOSUPPORT => error.ProtocolNotSupported,
36 else => |err| windows.unexpectedWSAError(err),
37 };
38 }
39
40 if (set.contains(.nonblocking)) {
41 var enabled: c_ulong = 1;
42 const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled);
43 if (rc == ws2_32.SOCKET_ERROR) {
44 return windows.unexpectedWSAError(ws2_32.WSAGetLastError());
45 }
46 }
47
48 return Socket{ .fd = fd };
49 }
50
51 /// Closes the socket.
52 pub fn deinit(self: Socket) void {
53 _ = ws2_32.closesocket(self.fd);
54 }
55
56 /// Shutdown either the read side, write side, or all side of the socket.
57 pub fn shutdown(self: Socket, how: os.ShutdownHow) !void {
58 const rc = ws2_32.shutdown(self.fd, switch (how) {
59 .recv => ws2_32.SD_RECEIVE,
60 .send => ws2_32.SD_SEND,
61 .both => ws2_32.SD_BOTH,
62 });
63 if (rc == ws2_32.SOCKET_ERROR) {
64 return switch (ws2_32.WSAGetLastError()) {
65 .WSAECONNABORTED => return error.ConnectionAborted,
66 .WSAECONNRESET => return error.ConnectionResetByPeer,
67 .WSAEINPROGRESS => return error.BlockingOperationInProgress,
68 .WSAEINVAL => unreachable,
69 .WSAENETDOWN => return error.NetworkSubsystemFailed,
70 .WSAENOTCONN => return error.SocketNotConnected,
71 .WSAENOTSOCK => unreachable,
72 .WSANOTINITIALISED => unreachable,
73 else => |err| return windows.unexpectedWSAError(err),
74 };
75 }
76 }
77
78 /// Binds the socket to an address.
79 pub fn bind(self: Socket, address: Socket.Address) !void {
80 const rc = ws2_32.bind(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
81 if (rc == ws2_32.SOCKET_ERROR) {
82 return switch (ws2_32.WSAGetLastError()) {
83 .WSAENETDOWN => error.NetworkSubsystemFailed,
84 .WSAEACCES => error.AccessDenied,
85 .WSAEADDRINUSE => error.AddressInUse,
86 .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
87 .WSAEFAULT => error.BadAddress,
88 .WSAEINPROGRESS => error.WouldBlock,
89 .WSAEINVAL => error.AlreadyBound,
90 .WSAENOBUFS => error.NoEphemeralPortsAvailable,
91 .WSAENOTSOCK => error.NotASocket,
92 else => |err| windows.unexpectedWSAError(err),
93 };
94 }
95 }
96
97 /// Start listening for incoming connections on the socket.
98 pub fn listen(self: Socket, max_backlog_size: u31) !void {
99 const rc = ws2_32.listen(self.fd, max_backlog_size);
100 if (rc == ws2_32.SOCKET_ERROR) {
101 return switch (ws2_32.WSAGetLastError()) {
102 .WSAENETDOWN => error.NetworkSubsystemFailed,
103 .WSAEADDRINUSE => error.AddressInUse,
104 .WSAEISCONN => error.AlreadyConnected,
105 .WSAEINVAL => error.SocketNotBound,
106 .WSAEMFILE, .WSAENOBUFS => error.SystemResources,
107 .WSAENOTSOCK => error.FileDescriptorNotASocket,
108 .WSAEOPNOTSUPP => error.OperationNotSupported,
109 .WSAEINPROGRESS => error.WouldBlock,
110 else => |err| windows.unexpectedWSAError(err),
111 };
112 }
113 }
114
115 /// Have the socket attempt to the connect to an address.
116 pub fn connect(self: Socket, address: Socket.Address) !void {
117 const rc = ws2_32.connect(self.fd, @ptrCast(*const ws2_32.sockaddr, &address.toNative()), @intCast(c_int, address.getNativeSize()));
118 if (rc == ws2_32.SOCKET_ERROR) {
119 return switch (ws2_32.WSAGetLastError()) {
120 .WSAEADDRINUSE => error.AddressInUse,
121 .WSAEADDRNOTAVAIL => error.AddressNotAvailable,
122 .WSAECONNREFUSED => error.ConnectionRefused,
123 .WSAETIMEDOUT => error.ConnectionTimedOut,
124 .WSAEFAULT => error.BadAddress,
125 .WSAEINVAL => error.ListeningSocket,
126 .WSAEISCONN => error.AlreadyConnected,
127 .WSAENOTSOCK => error.NotASocket,
128 .WSAEACCES => error.BroadcastNotEnabled,
129 .WSAENOBUFS => error.SystemResources,
130 .WSAEAFNOSUPPORT => error.AddressFamilyNotSupported,
131 .WSAEINPROGRESS, .WSAEWOULDBLOCK => error.WouldBlock,
132 .WSAEHOSTUNREACH, .WSAENETUNREACH => error.NetworkUnreachable,
133 else => |err| windows.unexpectedWSAError(err),
134 };
135 }
136 }
137
138 /// Accept a pending incoming connection queued to the kernel backlog
139 /// of the socket.
140 pub fn accept(self: Socket, flags: std.enums.EnumFieldStruct(Socket.InitFlags, bool, false)) !Socket.Connection {
141 var address: Socket.Address.Native.Storage = undefined;
142 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
143
144 const fd = ws2_32.accept(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
145 if (fd == ws2_32.INVALID_SOCKET) {
146 return switch (ws2_32.WSAGetLastError()) {
147 .WSANOTINITIALISED => unreachable,
148 .WSAECONNRESET => error.ConnectionResetByPeer,
149 .WSAEFAULT => unreachable,
150 .WSAEINVAL => error.SocketNotListening,
151 .WSAEMFILE => error.ProcessFdQuotaExceeded,
152 .WSAENETDOWN => error.NetworkSubsystemFailed,
153 .WSAENOBUFS => error.FileDescriptorNotASocket,
154 .WSAEOPNOTSUPP => error.OperationNotSupported,
155 .WSAEWOULDBLOCK => error.WouldBlock,
156 else => |err| windows.unexpectedWSAError(err),
157 };
158 }
159
160 const socket = Socket.from(fd);
161 errdefer socket.deinit();
162
163 const socket_address = Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
164
165 const set = std.EnumSet(Socket.InitFlags).init(flags);
166 if (set.contains(.nonblocking)) {
167 var enabled: c_ulong = 1;
168 const rc = ws2_32.ioctlsocket(fd, ws2_32.FIONBIO, &enabled);
169 if (rc == ws2_32.SOCKET_ERROR) {
170 return windows.unexpectedWSAError(ws2_32.WSAGetLastError());
171 }
172 }
173
174 return Socket.Connection.from(socket, socket_address);
175 }
176
177 /// Read data from the socket into the buffer provided with a set of flags
178 /// specified. It returns the number of bytes read into the buffer provided.
179 pub fn read(self: Socket, buf: []u8, flags: u32) !usize {
180 var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = buf.ptr }};
181 var num_bytes: u32 = undefined;
182 var flags_ = flags;
183
184 const rc = ws2_32.WSARecv(self.fd, bufs, 1, &num_bytes, &flags_, null, null);
185 if (rc == ws2_32.SOCKET_ERROR) {
186 return switch (ws2_32.WSAGetLastError()) {
187 .WSAECONNABORTED => error.ConnectionAborted,
188 .WSAECONNRESET => error.ConnectionResetByPeer,
189 .WSAEDISCON => error.ConnectionClosedByPeer,
190 .WSAEFAULT => error.BadBuffer,
191 .WSAEINPROGRESS,
192 .WSAEWOULDBLOCK,
193 .WSA_IO_PENDING,
194 .WSAETIMEDOUT,
195 => error.WouldBlock,
196 .WSAEINTR => error.Cancelled,
197 .WSAEINVAL => error.SocketNotBound,
198 .WSAEMSGSIZE => error.MessageTooLarge,
199 .WSAENETDOWN => error.NetworkSubsystemFailed,
200 .WSAENETRESET => error.NetworkReset,
201 .WSAENOTCONN => error.SocketNotConnected,
202 .WSAENOTSOCK => error.FileDescriptorNotASocket,
203 .WSAEOPNOTSUPP => error.OperationNotSupported,
204 .WSAESHUTDOWN => error.AlreadyShutdown,
205 .WSA_OPERATION_ABORTED => error.OperationAborted,
206 else => |err| windows.unexpectedWSAError(err),
207 };
208 }
209
210 return @intCast(usize, num_bytes);
211 }
212
213 /// Write a buffer of data provided to the socket with a set of flags specified.
214 /// It returns the number of bytes that are written to the socket.
215 pub fn write(self: Socket, buf: []const u8, flags: u32) !usize {
216 var bufs = &[_]ws2_32.WSABUF{.{ .len = @intCast(u32, buf.len), .buf = @intToPtr([*]u8, @ptrToInt(buf.ptr)) }};
217 var num_bytes: u32 = undefined;
218
219 const rc = ws2_32.WSASend(self.fd, bufs, 1, &num_bytes, flags, null, null);
220 if (rc == ws2_32.SOCKET_ERROR) {
221 return switch (ws2_32.WSAGetLastError()) {
222 .WSAECONNABORTED => error.ConnectionAborted,
223 .WSAECONNRESET => error.ConnectionResetByPeer,
224 .WSAEFAULT => error.BadBuffer,
225 .WSAEINPROGRESS,
226 .WSAEWOULDBLOCK,
227 .WSA_IO_PENDING,
228 .WSAETIMEDOUT,
229 => error.WouldBlock,
230 .WSAEINTR => error.Cancelled,
231 .WSAEINVAL => error.SocketNotBound,
232 .WSAEMSGSIZE => error.MessageTooLarge,
233 .WSAENETDOWN => error.NetworkSubsystemFailed,
234 .WSAENETRESET => error.NetworkReset,
235 .WSAENOBUFS => error.BufferDeadlock,
236 .WSAENOTCONN => error.SocketNotConnected,
237 .WSAENOTSOCK => error.FileDescriptorNotASocket,
238 .WSAEOPNOTSUPP => error.OperationNotSupported,
239 .WSAESHUTDOWN => error.AlreadyShutdown,
240 .WSA_OPERATION_ABORTED => error.OperationAborted,
241 else => |err| windows.unexpectedWSAError(err),
242 };
243 }
244
245 return @intCast(usize, num_bytes);
246 }
247
248 /// Writes multiple I/O vectors with a prepended message header to the socket
249 /// with a set of flags specified. It returns the number of bytes that are
250 /// written to the socket.
251 pub fn writeMessage(self: Socket, msg: Socket.Message, flags: u32) !usize {
252 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSASENDMSG, self.fd, ws2_32.WSAID_WSASENDMSG);
253
254 var num_bytes: u32 = undefined;
255
256 const rc = call(self.fd, &msg, flags, &num_bytes, null, null);
257 if (rc == ws2_32.SOCKET_ERROR) {
258 return switch (ws2_32.WSAGetLastError()) {
259 .WSAECONNABORTED => error.ConnectionAborted,
260 .WSAECONNRESET => error.ConnectionResetByPeer,
261 .WSAEFAULT => error.BadBuffer,
262 .WSAEINPROGRESS,
263 .WSAEWOULDBLOCK,
264 .WSA_IO_PENDING,
265 .WSAETIMEDOUT,
266 => error.WouldBlock,
267 .WSAEINTR => error.Cancelled,
268 .WSAEINVAL => error.SocketNotBound,
269 .WSAEMSGSIZE => error.MessageTooLarge,
270 .WSAENETDOWN => error.NetworkSubsystemFailed,
271 .WSAENETRESET => error.NetworkReset,
272 .WSAENOBUFS => error.BufferDeadlock,
273 .WSAENOTCONN => error.SocketNotConnected,
274 .WSAENOTSOCK => error.FileDescriptorNotASocket,
275 .WSAEOPNOTSUPP => error.OperationNotSupported,
276 .WSAESHUTDOWN => error.AlreadyShutdown,
277 .WSA_OPERATION_ABORTED => error.OperationAborted,
278 else => |err| windows.unexpectedWSAError(err),
279 };
280 }
281
282 return @intCast(usize, num_bytes);
283 }
284
285 /// Read multiple I/O vectors with a prepended message header from the socket
286 /// with a set of flags specified. It returns the number of bytes that were
287 /// read into the buffer provided.
288 pub fn readMessage(self: Socket, msg: *Socket.Message, flags: u32) !usize {
289 _ = flags;
290 const call = try windows.loadWinsockExtensionFunction(ws2_32.LPFN_WSARECVMSG, self.fd, ws2_32.WSAID_WSARECVMSG);
291
292 var num_bytes: u32 = undefined;
293
294 const rc = call(self.fd, msg, &num_bytes, null, null);
295 if (rc == ws2_32.SOCKET_ERROR) {
296 return switch (ws2_32.WSAGetLastError()) {
297 .WSAECONNABORTED => error.ConnectionAborted,
298 .WSAECONNRESET => error.ConnectionResetByPeer,
299 .WSAEDISCON => error.ConnectionClosedByPeer,
300 .WSAEFAULT => error.BadBuffer,
301 .WSAEINPROGRESS,
302 .WSAEWOULDBLOCK,
303 .WSA_IO_PENDING,
304 .WSAETIMEDOUT,
305 => error.WouldBlock,
306 .WSAEINTR => error.Cancelled,
307 .WSAEINVAL => error.SocketNotBound,
308 .WSAEMSGSIZE => error.MessageTooLarge,
309 .WSAENETDOWN => error.NetworkSubsystemFailed,
310 .WSAENETRESET => error.NetworkReset,
311 .WSAENOTCONN => error.SocketNotConnected,
312 .WSAENOTSOCK => error.FileDescriptorNotASocket,
313 .WSAEOPNOTSUPP => error.OperationNotSupported,
314 .WSAESHUTDOWN => error.AlreadyShutdown,
315 .WSA_OPERATION_ABORTED => error.OperationAborted,
316 else => |err| windows.unexpectedWSAError(err),
317 };
318 }
319
320 return @intCast(usize, num_bytes);
321 }
322
323 /// Query the address that the socket is locally bounded to.
324 pub fn getLocalAddress(self: Socket) !Socket.Address {
325 var address: Socket.Address.Native.Storage = undefined;
326 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
327
328 const rc = ws2_32.getsockname(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
329 if (rc == ws2_32.SOCKET_ERROR) {
330 return switch (ws2_32.WSAGetLastError()) {
331 .WSANOTINITIALISED => unreachable,
332 .WSAEFAULT => unreachable,
333 .WSAENETDOWN => error.NetworkSubsystemFailed,
334 .WSAENOTSOCK => error.FileDescriptorNotASocket,
335 .WSAEINVAL => error.SocketNotBound,
336 else => |err| windows.unexpectedWSAError(err),
337 };
338 }
339
340 return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
341 }
342
343 /// Query the address that the socket is connected to.
344 pub fn getRemoteAddress(self: Socket) !Socket.Address {
345 var address: Socket.Address.Native.Storage = undefined;
346 var address_len: c_int = @sizeOf(Socket.Address.Native.Storage);
347
348 const rc = ws2_32.getpeername(self.fd, @ptrCast(*ws2_32.sockaddr, &address), &address_len);
349 if (rc == ws2_32.SOCKET_ERROR) {
350 return switch (ws2_32.WSAGetLastError()) {
351 .WSANOTINITIALISED => unreachable,
352 .WSAEFAULT => unreachable,
353 .WSAENETDOWN => error.NetworkSubsystemFailed,
354 .WSAENOTSOCK => error.FileDescriptorNotASocket,
355 .WSAEINVAL => error.SocketNotBound,
356 else => |err| windows.unexpectedWSAError(err),
357 };
358 }
359
360 return Socket.Address.fromNative(@ptrCast(*ws2_32.sockaddr, &address));
361 }
362
363 /// Query and return the latest cached error on the socket.
364 pub fn getError(self: Socket) !void {
365 _ = self;
366 return {};
367 }
368
369 /// Query the read buffer size of the socket.
370 pub fn getReadBufferSize(self: Socket) !u32 {
371 _ = self;
372 return 0;
373 }
374
375 /// Query the write buffer size of the socket.
376 pub fn getWriteBufferSize(self: Socket) !u32 {
377 _ = self;
378 return 0;
379 }
380
381 /// Set a socket option.
382 pub fn setOption(self: Socket, level: u32, code: u32, value: []const u8) !void {
383 const rc = ws2_32.setsockopt(self.fd, @intCast(i32, level), @intCast(i32, code), value.ptr, @intCast(i32, value.len));
384 if (rc == ws2_32.SOCKET_ERROR) {
385 return switch (ws2_32.WSAGetLastError()) {
386 .WSANOTINITIALISED => unreachable,
387 .WSAENETDOWN => return error.NetworkSubsystemFailed,
388 .WSAEFAULT => unreachable,
389 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
390 .WSAEINVAL => return error.SocketNotBound,
391 else => |err| windows.unexpectedWSAError(err),
392 };
393 }
394 }
395
396 /// Have close() or shutdown() syscalls block until all queued messages in the socket have been successfully
397 /// sent, or if the timeout specified in seconds has been reached. It returns `error.UnsupportedSocketOption`
398 /// if the host does not support the option for a socket to linger around up until a timeout specified in
399 /// seconds.
400 pub fn setLinger(self: Socket, timeout_seconds: ?u16) !void {
401 const settings = Socket.Linger.init(timeout_seconds);
402 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.LINGER, mem.asBytes(&settings));
403 }
404
405 /// On connection-oriented sockets, have keep-alive messages be sent periodically. The timing in which keep-alive
406 /// messages are sent are dependant on operating system settings. It returns `error.UnsupportedSocketOption` if
407 /// the host does not support periodically sending keep-alive messages on connection-oriented sockets.
408 pub fn setKeepAlive(self: Socket, enabled: bool) !void {
409 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.KEEPALIVE, mem.asBytes(&@as(u32, @boolToInt(enabled))));
410 }
411
412 /// Allow multiple sockets on the same host to listen on the same address. It returns `error.UnsupportedSocketOption` if
413 /// the host does not support sockets listening the same address.
414 pub fn setReuseAddress(self: Socket, enabled: bool) !void {
415 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.REUSEADDR, mem.asBytes(&@as(u32, @boolToInt(enabled))));
416 }
417
418 /// Allow multiple sockets on the same host to listen on the same port. It returns `error.UnsupportedSocketOption` if
419 /// the host does not supports sockets listening on the same port.
420 ///
421 /// TODO: verify if this truly mimicks SO.REUSEPORT behavior, or if SO.REUSE_UNICASTPORT provides the correct behavior
422 pub fn setReusePort(self: Socket, enabled: bool) !void {
423 try self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.BROADCAST, mem.asBytes(&@as(u32, @boolToInt(enabled))));
424 try self.setReuseAddress(enabled);
425 }
426
427 /// Set the write buffer size of the socket.
428 pub fn setWriteBufferSize(self: Socket, size: u32) !void {
429 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.SNDBUF, mem.asBytes(&size));
430 }
431
432 /// Set the read buffer size of the socket.
433 pub fn setReadBufferSize(self: Socket, size: u32) !void {
434 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.RCVBUF, mem.asBytes(&size));
435 }
436
437 /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
438 /// set on a non-blocking socket.
439 ///
440 /// Set a timeout on the socket that is to occur if no messages are successfully written
441 /// to its bound destination after a specified number of milliseconds. A subsequent write
442 /// to the socket will thereafter return `error.WouldBlock` should the timeout be exceeded.
443 pub fn setWriteTimeout(self: Socket, milliseconds: u32) !void {
444 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.SNDTIMEO, mem.asBytes(&milliseconds));
445 }
446
447 /// WARNING: Timeouts only affect blocking sockets. It is undefined behavior if a timeout is
448 /// set on a non-blocking socket.
449 ///
450 /// Set a timeout on the socket that is to occur if no messages are successfully read
451 /// from its bound destination after a specified number of milliseconds. A subsequent
452 /// read from the socket will thereafter return `error.WouldBlock` should the timeout be
453 /// exceeded.
454 pub fn setReadTimeout(self: Socket, milliseconds: u32) !void {
455 return self.setOption(ws2_32.SOL.SOCKET, ws2_32.SO.RCVTIMEO, mem.asBytes(&milliseconds));
456 }
457 };
458}